@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
@@ -1,2 +1,2 @@
1
- "use strict";(self.webpackChunk_internetarchive_bookreader=self.webpackChunk_internetarchive_bookreader||[]).push([[336],{8837:function(t,o,e){var a;e(5827),e(4916),e(5306),e(2222),e(6992),e(1539),e(8783),e(3948),e(285),e(3609).extend(BookReader.defaultOptions,{enableUrlPlugin:!0,bookId:"",defaults:null,updateWindowTitle:!1,urlMode:"hash",urlHistoryBasePath:"/",urlTrackedParams:["page","search","mode","region","highlight","view"],urlTrackIndex0:!1}),BookReader.prototype.setup=(a=BookReader.prototype.setup,function(t){a.call(this,t),this.bookId=t.bookId,this.defaults=t.defaults,this.locationPollId=null,this.oldLocationHash=null,this.oldUserHash=null}),BookReader.prototype.init=function(t){return function(){var o=this;this.options.enableUrlPlugin&&(this.bind(BookReader.eventNames.PostInit,(function(){var t=o.options,e=t.updateWindowTitle,a=t.urlMode;e&&(document.title=o.shortTitle(50)),"hash"===a&&o.urlStartLocationPolling()})),this.bind(BookReader.eventNames.fragmentChange,this.urlUpdateFragment.bind(this))),t.call(this)}}(BookReader.prototype.init),BookReader.prototype.shortTitle=function(t){return this.bookTitle.length<t?this.bookTitle:"".concat(this.bookTitle.substr(0,t-3),"...")},BookReader.prototype.urlStartLocationPolling=function(){var t=this;this.oldLocationHash=this.urlReadFragment(),this.locationPollId&&(clearInterval(this.locationPollID),this.locationPollId=null),this.locationPollId=setInterval((function(){var o=t.urlReadFragment();if(o!=t.oldLocationHash&&o!=t.oldUserHash){var e=t.paramsFromFragment(o),a=function(){return t.updateFromParams(e)};t.trigger(BookReader.eventNames.stop),t.animating?(t.autoStop&&t.autoStop(),t.animationFinishedCallback=a):a(),t.oldUserHash=o}}),500)},BookReader.prototype.urlUpdateFragment=function(){var t=this.paramsFromCurrent(),o=this.options,e=o.urlMode,a=o.urlTrackIndex0,r=o.urlTrackedParams;a||void 0===t.index||0!==t.index||(delete t.index,delete t.page);var i=r.reduce((function(o,e){return e in t&&(o[e]=t[e]),o}),{}),n=this.fragmentFromParams(i,e),s=this.urlReadFragment(),l=this.getLocationSearch(),h=this.queryStringFromParams(i,l,e);if(s!==n||l!==h)if("history"===e){if(window.history&&window.history.replaceState){var d=this.options.urlHistoryBasePath.replace(/\/+$/,""),u=""===n?"":"/".concat(n),c="".concat(d).concat(u).concat(h);window.history.replaceState({},null,c),this.oldLocationHash=n+h}}else{var p=this.urlParamsFiltersOnlySearch(this.readQueryString());window.location.replace("#"+n+p),this.oldLocationHash=n+p}},BookReader.prototype.urlParamsFiltersOnlySearch=function(t){var o=new URLSearchParams(t);return o.has("q")?"?".concat(new URLSearchParams({q:o.get("q")})):""},BookReader.prototype.urlReadFragment=function(){var t=this.options,o=t.urlMode,e=t.urlHistoryBasePath;return"history"===o?window.location.pathname.substr(e.length):window.location.hash.substr(1)},BookReader.prototype.urlReadHashFragment=function(){return window.location.hash.substr(1)}}},function(t){t(t.s=8837)}]);
1
+ (self.webpackChunk_internetarchive_bookreader=self.webpackChunk_internetarchive_bookreader||[]).push([[336],{1226:function(t,e,r){"use strict";function n(t,e){(null==e||e>t.length)&&(e=t.length);for(var r=0,n=new Array(e);r<e;r++)n[r]=t[r];return n}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function a(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}r(5827),r(4916),r(5306),r(2222),r(6992),r(1539),r(8783),r(3948),r(285),r(489),r(2419),r(4819),r(5003),r(2526),r(1817),r(2165),r(4747),r(7941),r(9826),r(8309),r(9600),r(1249),r(7327),r(9714),r(8559),r(4723),r(3123),r(9720),r(4765),r(7042),r(1038);var i,l=function(){function t(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};o(this,t),this.bookReaderOptions=e,this.urlSchema=[{name:"page",position:"path",default:"n0"},{name:"mode",position:"path",default:"2up"},{name:"search",position:"path",deprecated_for:"q"},{name:"q",position:"query_param"},{name:"sort",position:"query_param"},{name:"view",position:"query_param"},{name:"admin",position:"query_param"}],this.urlState={},this.urlMode=this.bookReaderOptions.urlMode||"hash",this.urlHistoryBasePath=this.bookReaderOptions.urlHistoryBasePath||"/",this.urlLocationPollId=null,this.oldLocationHash=null,this.oldUserHash=null}var e,r;return e=t,(r=[{key:"urlStateToUrlString",value:function(t){var e=this,r=new URLSearchParams,n={};Object.keys(t).forEach((function(o){var a,i,l,u,s=e.urlSchema.find((function(t){return t.name===o}));null!==(a=s)&&void 0!==a&&a.deprecated_for&&(s=e.urlSchema.find((function(t){return t.name===s.deprecated_for}))),"path"==(null===(i=s)||void 0===i?void 0:i.position)?n[null===(l=s)||void 0===l?void 0:l.name]=t[o]:r.append((null===(u=s)||void 0===u?void 0:u.name)||o,t[o])}));var o=this.urlSchema.filter((function(t){return"path"==t.position})).map((function(t){return n[t.name]?"".concat(t.name,"/").concat(n[t.name]):""})).join("/"),a="".concat(o.replace(/\/+/g,"/").replace(/\/+$/,"")),i="".concat(a,"?").concat(r.toString());return r.toString()?i:"".concat(a)}},{key:"urlStringToUrlState",value:function(t){var e={},r=new URL(t,"http://example.com"),o=Object.fromEntries(r.searchParams.entries()),a=r.pathname.match(/[^\\/]+\/[^\\/]+/g),i=a?Object.fromEntries(a.map((function(t){return t.split("/")}))):{},l=function(t,e){return Object.keys(t).some((function(t){return t==e}))};return this.urlSchema.filter((function(t){return"path"==t.position})).forEach((function(t){var r=l(i,t.name);l(t,"deprecated_for")&&r?e[t.deprecated_for]=i[t.name]:r&&(e[t.name]=i[t.name])})),Object.entries(o).forEach((function(t){var r,o,a=(o=2,function(t){if(Array.isArray(t))return t}(r=t)||function(t,e){var r=null==t?null:"undefined"!=typeof Symbol&&t[Symbol.iterator]||t["@@iterator"];if(null!=r){var n,o,a=[],i=!0,l=!1;try{for(r=r.call(t);!(i=(n=r.next()).done)&&(a.push(n.value),!e||a.length!==e);i=!0);}catch(t){l=!0,o=t}finally{try{i||null==r.return||r.return()}finally{if(l)throw o}}return a}}(r,o)||function(t,e){if(t){if("string"==typeof t)return n(t,e);var r=Object.prototype.toString.call(t).slice(8,-1);return"Object"===r&&t.constructor&&(r=t.constructor.name),"Map"===r||"Set"===r?Array.from(t):"Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r)?n(t,e):void 0}}(r,o)||function(){throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}()),i=a[0],l=a[1];e[i]=l})),e}},{key:"setUrlParam",value:function(t,e){this.urlState[t]=e,this.pushToAddressBar()}},{key:"removeUrlParam",value:function(t){delete this.urlState[t],this.pushToAddressBar()}},{key:"getUrlParam",value:function(t){return this.urlState[t]}},{key:"pushToAddressBar",value:function(){var t=this.urlStateToUrlString(this.urlState),e="/"!==t?t:"";if("history"==this.urlMode){if(window.history&&window.history.replaceState){var r="".concat(this.urlHistoryBasePath).concat(e);window.history.replaceState({},null,r)}}else window.location.replace("#"+e);this.oldLocationHash=t}},{key:"listenForHashChanges",value:function(){var t=this;this.oldLocationHash=window.location.hash.substr(1),this.urlLocationPollId&&(clearInterval(this.urlLocationPollId),this.urlLocationPollId=null),this.urlLocationPollId=setInterval((function(){var e=window.location.hash.substr(1);e!=t.oldLocationHash&&(t.urlState=t.urlStringToUrlState(e))}),500)}},{key:"pullFromAddressBar",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:window.location,e="history"===this.urlMode?t.pathname.substr(this.urlHistoryBasePath.length)+t.search:t.hash.substr(1);this.urlState=this.urlStringToUrlState(e)}}])&&a(e.prototype,r),t}();function u(t){return(u="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function s(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function c(t,e){for(var r=0;r<e.length;r++){var n=e[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}function h(t,e,r){return(h="undefined"!=typeof Reflect&&Reflect.get?Reflect.get:function(t,e,r){var n=function(t,e){for(;!Object.prototype.hasOwnProperty.call(t,e)&&null!==(t=p(t)););return t}(t,e);if(n){var o=Object.getOwnPropertyDescriptor(n,e);return o.get?o.get.call(r):o.value}})(t,e,r||t)}function f(t,e){return(f=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function d(t,e){if(e&&("object"===u(e)||"function"==typeof e))return e;if(void 0!==e)throw new TypeError("Derived constructors may only return object or undefined");return function(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}(t)}function p(t){return(p=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}r(3609).extend(BookReader.defaultOptions,{enableUrlPlugin:!0,bookId:"",defaults:null,updateWindowTitle:!1,urlMode:"hash",urlHistoryBasePath:"/",urlTrackedParams:["page","search","mode","region","highlight","view"],urlTrackIndex0:!1}),BookReader.prototype.setup=(i=BookReader.prototype.setup,function(t){i.call(this,t),this.bookId=t.bookId,this.defaults=t.defaults,this.locationPollId=null,this.oldLocationHash=null,this.oldUserHash=null}),BookReader.prototype.init=function(t){return function(){var e=this;this.options.enableUrlPlugin&&(this.bind(BookReader.eventNames.PostInit,(function(){var t=e.options,r=t.updateWindowTitle,n=t.urlMode;r&&(document.title=e.shortTitle(e.bookTitle,50)),"hash"===n&&e.urlStartLocationPolling()})),this.bind(BookReader.eventNames.fragmentChange,this.urlUpdateFragment.bind(this))),t.call(this)}}(BookReader.prototype.init),BookReader.prototype.shortTitle=function(t){return this.bookTitle.length<t?this.bookTitle:"".concat(this.bookTitle.substr(0,t-3),"...")},BookReader.prototype.urlStartLocationPolling=function(){var t=this;this.oldLocationHash=this.urlReadFragment(),this.locationPollId&&(clearInterval(this.locationPollId),this.locationPollId=null),this.locationPollId=setInterval((function(){var e=t.urlReadFragment();if(e!=t.oldLocationHash&&e!=t.oldUserHash){var r=t.paramsFromFragment(e),n=function(){return t.updateFromParams(r)};t.trigger(BookReader.eventNames.stop),t.animating?(t.autoStop&&t.autoStop(),t.animationFinishedCallback=n):n(),t.oldUserHash=e}}),500)},BookReader.prototype.urlUpdateFragment=function(){var t=this.paramsFromCurrent(),e=this.options,r=e.urlMode,n=e.urlTrackIndex0,o=e.urlTrackedParams;n||void 0===t.index||0!==t.index||(delete t.index,delete t.page);var a=o.reduce((function(e,r){return r in t&&(e[r]=t[r]),e}),{}),i=this.fragmentFromParams(a,r),l=this.urlReadFragment(),u=this.getLocationSearch(),s=this.queryStringFromParams(a,u,r);if(l!==i||u!==s)if("history"===r){if(window.history&&window.history.replaceState){var c=this.options.urlHistoryBasePath.replace(/\/+$/,""),h=""===i?"":"/".concat(i),f="".concat(c).concat(h).concat(s);window.history.replaceState({},null,f),this.oldLocationHash=i+s}}else{var d=this.urlParamsFiltersOnlySearch(this.readQueryString());window.location.replace("#"+i+d),this.oldLocationHash=i+d}},BookReader.prototype.urlParamsFiltersOnlySearch=function(t){var e=new URLSearchParams(t);return e.has("q")?"?".concat(new URLSearchParams({q:e.get("q")})):""},BookReader.prototype.urlReadFragment=function(){var t=this.options,e=t.urlMode,r=t.urlHistoryBasePath;return"history"===e?window.location.pathname.substr(r.length):window.location.hash.substr(1)},BookReader.prototype.urlReadHashFragment=function(){return window.location.hash.substr(1)};var y=function(t){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&f(t,e)}(i,BookReader);var e,r,n,o,a=(n=i,o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(t){return!1}}(),function(){var t,e=p(n);if(o){var r=p(this).constructor;t=Reflect.construct(e,arguments,r)}else t=e.apply(this,arguments);return d(this,t)});function i(){return s(this,i),a.apply(this,arguments)}return e=i,(r=[{key:"init",value:function(){var t=this;this.options.enableUrlPlugin&&(this.urlPlugin=new l(this.options),this.bind(BookReader.eventNames.PostInit,(function(){"hash"===t.options.urlMode&&t.urlPlugin.listenForHashChanges()}))),h(p(i.prototype),"init",this).call(this)}}])&&c(e.prototype,r),i}();window.BookReader=y},9720:function(t,e,r){var n=r(2109),o=r(4699).entries;n({target:"Object",stat:!0},{entries:function(t){return o(t)}})},8559:function(t,e,r){var n=r(2109),o=r(408),a=r(6135);n({target:"Object",stat:!0},{fromEntries:function(t){var e={};return o(t,(function(t,r){a(e,t,r)}),{AS_ENTRIES:!0}),e}})},4723:function(t,e,r){"use strict";var n=r(7007),o=r(9670),a=r(7466),i=r(1340),l=r(4488),u=r(1530),s=r(7651);n("match",(function(t,e,r){return[function(e){var r=l(this),n=null==e?void 0:e[t];return void 0!==n?n.call(e,r):new RegExp(e)[t](i(r))},function(t){var n=o(this),l=i(t),c=r(e,n,l);if(c.done)return c.value;if(!n.global)return s(n,l);var h=n.unicode;n.lastIndex=0;for(var f,d=[],p=0;null!==(f=s(n,l));){var y=i(f[0]);d[p]=y,""===y&&(n.lastIndex=u(l,a(n.lastIndex),h)),p++}return 0===p?null:d}]}))}},function(t){t(t.s=1226)}]);
2
2
  //# sourceMappingURL=plugin.url.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugins/plugin.url.js","mappings":"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","sources":["webpack://@internetarchive/bookreader/./src/plugins/plugin.url.js"],"sourcesContent":["/* global BookReader */\n/**\n * Plugin for URL management in BookReader\n * Note read more about the url \"fragment\" here:\n * https://openlibrary.org/dev/docs/bookurls\n */\n\njQuery.extend(BookReader.defaultOptions, {\n enableUrlPlugin: true,\n bookId: '',\n /** @type {string} Defaults can be a urlFragment string */\n defaults: null,\n updateWindowTitle: false,\n\n /** @type {'history' | 'hash'} */\n urlMode: 'hash',\n\n /**\n * When using 'history' mode, this part of the URL is kept constant\n * @example /details/plato/\n */\n urlHistoryBasePath: '/',\n\n /** Only these params will be reflected onto the URL */\n urlTrackedParams: ['page', 'search', 'mode', 'region', 'highlight', 'view'],\n\n /** If true, don't update the URL when `page == n0 (eg \"/page/n0\")` */\n urlTrackIndex0: false,\n});\n\n/** @override */\nBookReader.prototype.setup = (function(super_) {\n return function(options) {\n super_.call(this, options);\n\n this.bookId = options.bookId;\n this.defaults = options.defaults;\n\n this.locationPollId = null;\n this.oldLocationHash = null;\n this.oldUserHash = null;\n };\n})(BookReader.prototype.setup);\n\n/** @override */\nBookReader.prototype.init = (function(super_) {\n return function() {\n\n if (this.options.enableUrlPlugin) {\n this.bind(BookReader.eventNames.PostInit, () => {\n const { updateWindowTitle, urlMode } = this.options;\n if (updateWindowTitle) {\n document.title = this.shortTitle(50);\n }\n if (urlMode === 'hash') {\n this.urlStartLocationPolling();\n }\n });\n\n this.bind(BookReader.eventNames.fragmentChange,\n this.urlUpdateFragment.bind(this)\n );\n }\n super_.call(this);\n };\n})(BookReader.prototype.init);\n\n/**\n * Returns a shortened version of the title with the maximum number of characters\n * @param {number} maximumCharacters\n * @return {string}\n */\nBookReader.prototype.shortTitle = function(maximumCharacters) {\n if (this.bookTitle.length < maximumCharacters) {\n return this.bookTitle;\n }\n\n const title = `${this.bookTitle.substr(0, maximumCharacters - 3)}...`;\n return title;\n};\n\n/**\n * Starts polling of window.location to see hash fragment changes\n */\nBookReader.prototype.urlStartLocationPolling = function() {\n this.oldLocationHash = this.urlReadFragment();\n\n if (this.locationPollId) {\n clearInterval(this.locationPollID);\n this.locationPollId = null;\n }\n\n const updateHash = () => {\n const newFragment = this.urlReadFragment();\n const hasFragmentChange = (newFragment != this.oldLocationHash) && (newFragment != this.oldUserHash);\n\n if (!hasFragmentChange) { return; }\n\n const params = this.paramsFromFragment(newFragment);\n\n const updateParams = () => this.updateFromParams(params);\n\n this.trigger(BookReader.eventNames.stop);\n if (this.animating) {\n // Queue change if animating\n if (this.autoStop) this.autoStop();\n this.animationFinishedCallback = updateParams;\n } else {\n // update immediately\n updateParams();\n }\n this.oldUserHash = newFragment;\n };\n\n this.locationPollId = setInterval(updateHash, 500);\n};\n\n/**\n * Update URL from the current parameters.\n * Call this instead of manually using window.location.replace\n */\nBookReader.prototype.urlUpdateFragment = function() {\n const allParams = this.paramsFromCurrent();\n const { urlMode, urlTrackIndex0, urlTrackedParams } = this.options;\n\n if (!urlTrackIndex0\n && (typeof(allParams.index) !== 'undefined')\n && allParams.index === 0) {\n delete allParams.index;\n delete allParams.page;\n }\n\n const params = urlTrackedParams.reduce((validParams, paramName) => {\n if (paramName in allParams) {\n validParams[paramName] = allParams[paramName];\n }\n return validParams;\n }, {});\n\n const newFragment = this.fragmentFromParams(params, urlMode);\n const currFragment = this.urlReadFragment();\n const currQueryString = this.getLocationSearch();\n const newQueryString = this.queryStringFromParams(params, currQueryString, urlMode);\n if (currFragment === newFragment && currQueryString === newQueryString) {\n return;\n }\n\n if (urlMode === 'history') {\n if (window.history && window.history.replaceState) {\n const baseWithoutSlash = this.options.urlHistoryBasePath.replace(/\\/+$/, '');\n const newFragmentWithSlash = newFragment === '' ? '' : `/${newFragment}`;\n\n const newUrlPath = `${baseWithoutSlash}${newFragmentWithSlash}${newQueryString}`;\n window.history.replaceState({}, null, newUrlPath);\n this.oldLocationHash = newFragment + newQueryString;\n\n }\n } else {\n const newQueryStringSearch = this.urlParamsFiltersOnlySearch(this.readQueryString());\n window.location.replace('#' + newFragment + newQueryStringSearch);\n this.oldLocationHash = newFragment + newQueryStringSearch;\n\n }\n};\n\n/**\n * @private\n * Filtering query parameters to select only book search param (?q=foo)\n This needs to be updated/URL system modified if future query params are to be added\n * @param {string} url\n * @return {string}\n * */\nBookReader.prototype.urlParamsFiltersOnlySearch = function(url) {\n const params = new URLSearchParams(url);\n return params.has('q') ? `?${new URLSearchParams({ q: params.get('q') })}` : '';\n};\n\n\n/**\n * Will read either the hash or URL and return the bookreader fragment\n * @return {string}\n */\nBookReader.prototype.urlReadFragment = function() {\n const { urlMode, urlHistoryBasePath } = this.options;\n if (urlMode === 'history') {\n return window.location.pathname.substr(urlHistoryBasePath.length);\n } else {\n return window.location.hash.substr(1);\n }\n};\n\n/**\n * Will read the hash return the bookreader fragment\n * @return {string}\n */\nBookReader.prototype.urlReadHashFragment = function() {\n return window.location.hash.substr(1);\n};\n"],"names":["super_","extend","BookReader","defaultOptions","enableUrlPlugin","bookId","defaults","updateWindowTitle","urlMode","urlHistoryBasePath","urlTrackedParams","urlTrackIndex0","prototype","setup","options","call","this","locationPollId","oldLocationHash","oldUserHash","init","bind","eventNames","PostInit","document","title","shortTitle","urlStartLocationPolling","fragmentChange","urlUpdateFragment","maximumCharacters","bookTitle","length","substr","urlReadFragment","clearInterval","locationPollID","setInterval","newFragment","params","paramsFromFragment","updateParams","updateFromParams","trigger","stop","animating","autoStop","animationFinishedCallback","allParams","paramsFromCurrent","index","page","reduce","validParams","paramName","fragmentFromParams","currFragment","currQueryString","getLocationSearch","newQueryString","queryStringFromParams","window","history","replaceState","baseWithoutSlash","replace","newFragmentWithSlash","newUrlPath","newQueryStringSearch","urlParamsFiltersOnlySearch","readQueryString","location","url","URLSearchParams","has","q","get","pathname","hash","urlReadHashFragment"],"sourceRoot":""}
1
+ {"version":3,"file":"plugins/plugin.url.js","mappings":"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","sources":["webpack://@internetarchive/bookreader/./src/plugins/url/UrlPlugin.js","webpack://@internetarchive/bookreader/./src/plugins/url/plugin.url.js","webpack://@internetarchive/bookreader/./node_modules/core-js/modules/es.object.entries.js","webpack://@internetarchive/bookreader/./node_modules/core-js/modules/es.object.from-entries.js","webpack://@internetarchive/bookreader/./node_modules/core-js/modules/es.string.match.js"],"sourcesContent":["export class UrlPlugin {\n constructor(options = {}) {\n this.bookReaderOptions = options;\n\n // the canonical order of elements is important in the path and query string\n this.urlSchema = [\n { name: 'page', position: 'path', default: 'n0' },\n { name: 'mode', position: 'path', default: '2up' },\n { name: 'search', position: 'path', deprecated_for: 'q' },\n { name: 'q', position: 'query_param' },\n { name: 'sort', position: 'query_param' },\n { name: 'view', position: 'query_param' },\n { name: 'admin', position: 'query_param' },\n ];\n\n this.urlState = {};\n this.urlMode = this.bookReaderOptions.urlMode || 'hash';\n this.urlHistoryBasePath = this.bookReaderOptions.urlHistoryBasePath || '/';\n this.urlLocationPollId = null;\n this.oldLocationHash = null;\n this.oldUserHash = null;\n }\n\n /**\n * Parse JSON object URL state to string format\n * Arrange path names in an order that it is positioned on the urlSchema\n * @param {Object} urlState\n * @returns {string}\n */\n urlStateToUrlString(urlState) {\n const searchParams = new URLSearchParams();\n const pathParams = {};\n\n Object.keys(urlState).forEach(key => {\n let schema = this.urlSchema.find(schema => schema.name === key);\n if (schema?.deprecated_for) {\n schema = this.urlSchema.find(schemaKey => schemaKey.name === schema.deprecated_for);\n }\n if (schema?.position == 'path') {\n pathParams[schema?.name] = urlState[key];\n } else {\n searchParams.append(schema?.name || key, urlState[key]);\n }\n });\n\n const strPathParams = this.urlSchema\n .filter(s => s.position == 'path')\n .map(schema => pathParams[schema.name] ? `${schema.name}/${pathParams[schema.name]}` : '')\n .join('/');\n\n // replace consecutive slashes with a single slash + remove trailing slashes\n const strStrippedTrailingSlash = `${strPathParams.replace(/\\/+/g, '/').replace(/\\/+$/, '')}`;\n const concatenatedPath = `${strStrippedTrailingSlash}?${searchParams.toString()}`;\n return searchParams.toString() ? concatenatedPath : `${strStrippedTrailingSlash}`;\n }\n\n /**\n * Parse string URL and add it in the current urlState\n * Example:\n * /page/n7/mode/2up => {page: 'n7', mode: '2up'}\n * /page/n7/mode/2up/search/hello => {page: 'n7', mode: '2up', q: 'hello'}\n * @param {string} urlString\n * @returns {object}\n */\n urlStringToUrlState(urlString) {\n const urlState = {};\n\n // Fetch searchParams from given {str}\n // Note: whole URL path is needed for URL parsing\n const urlPath = new URL(urlString, 'http://example.com');\n const urlSearchParamsObj = Object.fromEntries(urlPath.searchParams.entries());\n const splitUrlMatches = urlPath.pathname.match(/[^\\\\/]+\\/[^\\\\/]+/g);\n const urlStrSplitSlashObj = splitUrlMatches ? Object.fromEntries(splitUrlMatches.map(x => x.split('/'))) : {};\n\n const doesKeyExists = (_object, _key) => {\n return Object.keys(_object).some(value => value == _key);\n };\n\n // Add path objects to urlState\n this.urlSchema\n .filter(schema => schema.position == 'path')\n .forEach(schema => {\n const hasPropertyKey = doesKeyExists(urlStrSplitSlashObj, schema.name);\n const hasDeprecatedKey = doesKeyExists(schema, 'deprecated_for') && hasPropertyKey;\n\n if (hasDeprecatedKey) {\n urlState[schema.deprecated_for] = urlStrSplitSlashObj[schema.name];\n return;\n }\n\n if (hasPropertyKey) {\n urlState[schema.name] = urlStrSplitSlashObj[schema.name];\n return;\n }\n });\n\n // Add searchParams to urlState\n Object.entries(urlSearchParamsObj).forEach(([key, value]) => {\n urlState[key] = value;\n });\n\n return urlState;\n }\n\n /**\n * Add or update key-value to the urlState\n * @param {string} key\n * @param {string} val\n */\n setUrlParam(key, value) {\n this.urlState[key] = value;\n\n this.pushToAddressBar();\n }\n\n /**\n * Delete key-value to the urlState\n * @param {string} key\n */\n removeUrlParam(key) {\n delete this.urlState[key];\n\n this.pushToAddressBar();\n }\n\n /**\n * Get key-value from the urlState\n * @param {string} key\n * @return {string}\n */\n getUrlParam(key) {\n return this.urlState[key];\n }\n\n /**\n * Push URL params to addressbar\n */\n pushToAddressBar() {\n const urlStrPath = this.urlStateToUrlString(this.urlState);\n const concatenatedPath = urlStrPath !== '/' ? urlStrPath : '';\n if (this.urlMode == 'history') {\n if (window.history && window.history.replaceState) {\n const newUrlPath = `${this.urlHistoryBasePath}${concatenatedPath}`;\n window.history.replaceState({}, null, newUrlPath);\n }\n } else {\n window.location.replace('#' + concatenatedPath);\n }\n this.oldLocationHash = urlStrPath;\n }\n\n /**\n * Get the url and check if it has changed\n * If it was changeed, update the urlState\n */\n listenForHashChanges() {\n this.oldLocationHash = window.location.hash.substr(1);\n if (this.urlLocationPollId) {\n clearInterval(this.urlLocationPollId);\n this.urlLocationPollId = null;\n }\n\n // check if the URL changes\n const updateHash = () => {\n const newFragment = window.location.hash.substr(1);\n const hasFragmentChange = newFragment != this.oldLocationHash;\n\n if (!hasFragmentChange) { return; }\n\n this.urlState = this.urlStringToUrlState(newFragment);\n };\n this.urlLocationPollId = setInterval(updateHash, 500);\n }\n\n /**\n * Will read either the hash or URL and return the bookreader fragment\n */\n pullFromAddressBar (location = window.location) {\n const path = this.urlMode === 'history'\n ? (location.pathname.substr(this.urlHistoryBasePath.length) + location.search)\n : location.hash.substr(1);\n this.urlState = this.urlStringToUrlState(path);\n }\n}\n","/* global BookReader */\n\nimport { UrlPlugin } from \"./UrlPlugin\";\n\n/**\n * Plugin for URL management in BookReader\n * Note read more about the url \"fragment\" here:\n * https://openlibrary.org/dev/docs/bookurls\n */\n\njQuery.extend(BookReader.defaultOptions, {\n enableUrlPlugin: true,\n bookId: '',\n /** @type {string} Defaults can be a urlFragment string */\n defaults: null,\n updateWindowTitle: false,\n\n /** @type {'history' | 'hash'} */\n urlMode: 'hash',\n\n /**\n * When using 'history' mode, this part of the URL is kept constant\n * @example /details/plato/\n */\n urlHistoryBasePath: '/',\n\n /** Only these params will be reflected onto the URL */\n urlTrackedParams: ['page', 'search', 'mode', 'region', 'highlight', 'view'],\n\n /** If true, don't update the URL when `page == n0 (eg \"/page/n0\")` */\n urlTrackIndex0: false,\n});\n\n/** @override */\nBookReader.prototype.setup = (function(super_) {\n return function(options) {\n super_.call(this, options);\n\n this.bookId = options.bookId;\n this.defaults = options.defaults;\n\n this.locationPollId = null;\n this.oldLocationHash = null;\n this.oldUserHash = null;\n };\n})(BookReader.prototype.setup);\n\n/** @override */\nBookReader.prototype.init = (function(super_) {\n return function() {\n\n if (this.options.enableUrlPlugin) {\n this.bind(BookReader.eventNames.PostInit, () => {\n const { updateWindowTitle, urlMode } = this.options;\n if (updateWindowTitle) {\n document.title = this.shortTitle(this.bookTitle, 50);\n }\n if (urlMode === 'hash') {\n this.urlStartLocationPolling();\n }\n });\n\n this.bind(BookReader.eventNames.fragmentChange,\n this.urlUpdateFragment.bind(this)\n );\n }\n super_.call(this);\n };\n})(BookReader.prototype.init);\n\n/**\n * Returns a shortened version of the title with the maximum number of characters\n * @param {number} maximumCharacters\n * @return {string}\n */\nBookReader.prototype.shortTitle = function(maximumCharacters) {\n if (this.bookTitle.length < maximumCharacters) {\n return this.bookTitle;\n }\n\n const title = `${this.bookTitle.substr(0, maximumCharacters - 3)}...`;\n return title;\n};\n\n/**\n * Starts polling of window.location to see hash fragment changes\n */\nBookReader.prototype.urlStartLocationPolling = function() {\n this.oldLocationHash = this.urlReadFragment();\n\n if (this.locationPollId) {\n clearInterval(this.locationPollId);\n this.locationPollId = null;\n }\n\n const updateHash = () => {\n const newFragment = this.urlReadFragment();\n const hasFragmentChange = (newFragment != this.oldLocationHash) && (newFragment != this.oldUserHash);\n\n if (!hasFragmentChange) { return; }\n\n const params = this.paramsFromFragment(newFragment);\n\n const updateParams = () => this.updateFromParams(params);\n\n this.trigger(BookReader.eventNames.stop);\n if (this.animating) {\n // Queue change if animating\n if (this.autoStop) this.autoStop();\n this.animationFinishedCallback = updateParams;\n } else {\n // update immediately\n updateParams();\n }\n this.oldUserHash = newFragment;\n };\n\n this.locationPollId = setInterval(updateHash, 500);\n};\n\n/**\n * Update URL from the current parameters.\n * Call this instead of manually using window.location.replace\n */\nBookReader.prototype.urlUpdateFragment = function() {\n const allParams = this.paramsFromCurrent();\n const { urlMode, urlTrackIndex0, urlTrackedParams } = this.options;\n\n if (!urlTrackIndex0\n && (typeof(allParams.index) !== 'undefined')\n && allParams.index === 0) {\n delete allParams.index;\n delete allParams.page;\n }\n\n const params = urlTrackedParams.reduce((validParams, paramName) => {\n if (paramName in allParams) {\n validParams[paramName] = allParams[paramName];\n }\n return validParams;\n }, {});\n\n const newFragment = this.fragmentFromParams(params, urlMode);\n const currFragment = this.urlReadFragment();\n const currQueryString = this.getLocationSearch();\n const newQueryString = this.queryStringFromParams(params, currQueryString, urlMode);\n if (currFragment === newFragment && currQueryString === newQueryString) {\n return;\n }\n\n if (urlMode === 'history') {\n if (window.history && window.history.replaceState) {\n const baseWithoutSlash = this.options.urlHistoryBasePath.replace(/\\/+$/, '');\n const newFragmentWithSlash = newFragment === '' ? '' : `/${newFragment}`;\n\n const newUrlPath = `${baseWithoutSlash}${newFragmentWithSlash}${newQueryString}`;\n window.history.replaceState({}, null, newUrlPath);\n this.oldLocationHash = newFragment + newQueryString;\n\n }\n } else {\n const newQueryStringSearch = this.urlParamsFiltersOnlySearch(this.readQueryString());\n window.location.replace('#' + newFragment + newQueryStringSearch);\n this.oldLocationHash = newFragment + newQueryStringSearch;\n\n }\n};\n\n/**\n * @private\n * Filtering query parameters to select only book search param (?q=foo)\n This needs to be updated/URL system modified if future query params are to be added\n * @param {string} url\n * @return {string}\n * */\nBookReader.prototype.urlParamsFiltersOnlySearch = function(url) {\n const params = new URLSearchParams(url);\n return params.has('q') ? `?${new URLSearchParams({ q: params.get('q') })}` : '';\n};\n\n\n/**\n * Will read either the hash or URL and return the bookreader fragment\n * @return {string}\n */\nBookReader.prototype.urlReadFragment = function() {\n const { urlMode, urlHistoryBasePath } = this.options;\n if (urlMode === 'history') {\n return window.location.pathname.substr(urlHistoryBasePath.length);\n } else {\n return window.location.hash.substr(1);\n }\n};\n\n/**\n * Will read the hash return the bookreader fragment\n * @return {string}\n */\nBookReader.prototype.urlReadHashFragment = function() {\n return window.location.hash.substr(1);\n};\nexport class BookreaderUrlPlugin extends BookReader {\n init() {\n if (this.options.enableUrlPlugin) {\n this.urlPlugin = new UrlPlugin(this.options);\n this.bind(BookReader.eventNames.PostInit, () => {\n const { urlMode } = this.options;\n\n if (urlMode === 'hash') {\n this.urlPlugin.listenForHashChanges();\n }\n });\n }\n\n super.init();\n }\n}\n\nwindow.BookReader = BookreaderUrlPlugin;\nexport default BookreaderUrlPlugin;\n","var $ = require('../internals/export');\nvar $entries = require('../internals/object-to-array').entries;\n\n// `Object.entries` method\n// https://tc39.es/ecma262/#sec-object.entries\n$({ target: 'Object', stat: true }, {\n entries: function entries(O) {\n return $entries(O);\n }\n});\n","var $ = require('../internals/export');\nvar iterate = require('../internals/iterate');\nvar createProperty = require('../internals/create-property');\n\n// `Object.fromEntries` method\n// https://github.com/tc39/proposal-object-from-entries\n$({ target: 'Object', stat: true }, {\n fromEntries: function fromEntries(iterable) {\n var obj = {};\n iterate(iterable, function (k, v) {\n createProperty(obj, k, v);\n }, { AS_ENTRIES: true });\n return obj;\n }\n});\n","'use strict';\nvar fixRegExpWellKnownSymbolLogic = require('../internals/fix-regexp-well-known-symbol-logic');\nvar anObject = require('../internals/an-object');\nvar toLength = require('../internals/to-length');\nvar toString = require('../internals/to-string');\nvar requireObjectCoercible = require('../internals/require-object-coercible');\nvar advanceStringIndex = require('../internals/advance-string-index');\nvar regExpExec = require('../internals/regexp-exec-abstract');\n\n// @@match logic\nfixRegExpWellKnownSymbolLogic('match', function (MATCH, nativeMatch, maybeCallNative) {\n return [\n // `String.prototype.match` method\n // https://tc39.es/ecma262/#sec-string.prototype.match\n function match(regexp) {\n var O = requireObjectCoercible(this);\n var matcher = regexp == undefined ? undefined : regexp[MATCH];\n return matcher !== undefined ? matcher.call(regexp, O) : new RegExp(regexp)[MATCH](toString(O));\n },\n // `RegExp.prototype[@@match]` method\n // https://tc39.es/ecma262/#sec-regexp.prototype-@@match\n function (string) {\n var rx = anObject(this);\n var S = toString(string);\n var res = maybeCallNative(nativeMatch, rx, S);\n\n if (res.done) return res.value;\n\n if (!rx.global) return regExpExec(rx, S);\n\n var fullUnicode = rx.unicode;\n rx.lastIndex = 0;\n var A = [];\n var n = 0;\n var result;\n while ((result = regExpExec(rx, S)) !== null) {\n var matchStr = toString(result[0]);\n A[n] = matchStr;\n if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);\n n++;\n }\n return n === 0 ? null : A;\n }\n ];\n});\n"],"names":["super_","UrlPlugin","options","this","bookReaderOptions","urlSchema","name","position","default","deprecated_for","urlState","urlMode","urlHistoryBasePath","urlLocationPollId","oldLocationHash","oldUserHash","searchParams","URLSearchParams","pathParams","Object","keys","forEach","key","schema","find","schemaKey","append","strPathParams","filter","s","map","join","strStrippedTrailingSlash","replace","concatenatedPath","toString","urlString","urlPath","URL","urlSearchParamsObj","fromEntries","entries","splitUrlMatches","pathname","match","urlStrSplitSlashObj","x","split","doesKeyExists","_object","_key","some","value","hasPropertyKey","pushToAddressBar","urlStrPath","urlStateToUrlString","window","history","replaceState","newUrlPath","location","hash","substr","clearInterval","setInterval","newFragment","urlStringToUrlState","path","length","search","extend","BookReader","defaultOptions","enableUrlPlugin","bookId","defaults","updateWindowTitle","urlTrackedParams","urlTrackIndex0","prototype","setup","call","locationPollId","init","bind","eventNames","PostInit","document","title","shortTitle","bookTitle","urlStartLocationPolling","fragmentChange","urlUpdateFragment","maximumCharacters","urlReadFragment","params","paramsFromFragment","updateParams","updateFromParams","trigger","stop","animating","autoStop","animationFinishedCallback","allParams","paramsFromCurrent","index","page","reduce","validParams","paramName","fragmentFromParams","currFragment","currQueryString","getLocationSearch","newQueryString","queryStringFromParams","baseWithoutSlash","newFragmentWithSlash","newQueryStringSearch","urlParamsFiltersOnlySearch","readQueryString","url","has","q","get","urlReadHashFragment","BookreaderUrlPlugin","urlPlugin","listenForHashChanges","$","$entries","target","stat","O","iterate","createProperty","iterable","obj","k","v","AS_ENTRIES","fixRegExpWellKnownSymbolLogic","anObject","toLength","requireObjectCoercible","advanceStringIndex","regExpExec","MATCH","nativeMatch","maybeCallNative","regexp","matcher","undefined","RegExp","string","rx","S","res","done","global","fullUnicode","unicode","lastIndex","result","A","n","matchStr"],"sourceRoot":""}
@@ -16,8 +16,21 @@ body {
16
16
  display: none;
17
17
  }
18
18
 
19
+ body.BRfullscreenActive section.theater {
20
+ height: 100vh;
21
+ }
22
+ ia-bookreader[fullscreen] {
23
+ height: unset;
24
+ }
25
+
26
+ ia-bookreader{
27
+ --br-height: calc(100vh - 100px);
28
+ display: block;
29
+ height: var(--br-height);
30
+ }
31
+
19
32
  .BookReader {
20
- height: 600px;
33
+ height: var(--br-height);
21
34
  overflow: hidden;
22
35
  margin: 0 auto;
23
36
  }
@@ -0,0 +1,104 @@
1
+ /* global BookReader, BookReaderJSIAinit */
2
+
3
+ /**
4
+ * This is how Internet Archive loads bookreader
5
+ */
6
+ const urlParams = new URLSearchParams(window.location.search);
7
+
8
+ const ocaid = urlParams.get('ocaid');
9
+ const openFullImmersionTheater = urlParams.get('view') === 'theater';
10
+ const ui = urlParams.get('ui');
11
+ const autoflip = urlParams.get('autoflip');
12
+ const searchTerm = urlParams.get('q');
13
+
14
+ const iaBookReader = document.querySelector('ia-bookreader');
15
+
16
+ if (openFullImmersionTheater) {
17
+ $(document.body).addClass('BRfullscreenActive');
18
+ iaBookReader.fullscreen = openFullImmersionTheater;
19
+ }
20
+
21
+ // Override options coming from IA
22
+ BookReader.optionOverrides.imagesBaseURL = '/BookReader/images/';
23
+
24
+ const initializeBookReader = (brManifest) => {
25
+ console.log('initializeBookReader', brManifest);
26
+ const br = new BookReader();
27
+
28
+ const customAutoflipParams = {
29
+ autoflip: !!autoflip,
30
+ flipSpeed: urlParams.flipSpeed || 2000,
31
+ flipDelay: urlParams.flipDelay || 5000
32
+ };
33
+
34
+ const options = {
35
+ el: '#BookReader',
36
+ /* Url plugin - IA uses History mode for URL */
37
+ // commenting these out as demo uses hash mode
38
+ // keeping them here for reference
39
+ // urlHistoryBasePath: `/details/{$ocaid}/`,
40
+ // resumeCookiePath: `/details/{$ocaid}/`,
41
+ // urlMode: 'history',
42
+ // Only reflect these params onto the URL
43
+ // urlTrackedParams: ['page', 'search', 'mode'],
44
+ /* End url plugin */
45
+ enableBookTitleLink: false,
46
+ bookUrlText: null,
47
+ startFullscreen: openFullImmersionTheater,
48
+ initialSearchTerm: searchTerm ? searchTerm : '',
49
+ // leaving this option commented out bc we change given user agent on archive.org
50
+ // onePage: { autofit: <?=json_encode($this->ios ? 'width' : 'auto')?> },
51
+ showToolbar: false,
52
+ /* Multiple volumes */
53
+ // To show multiple volumes:
54
+ enableMultipleBooks: false, // turn this on
55
+ multipleBooksList: [], // populate this // TODO: get sample blob and tie into demo
56
+ /* End multiple volumes */
57
+ enableBookmarks: true, // turn this on
58
+ enableFSLogoShortcut: true,
59
+ };
60
+
61
+ // we want to show item as embedded when ?ui=embed is in URI
62
+ if (ui === 'embed') {
63
+ options.mode = 1;
64
+ options.ui = 'embed';
65
+ }
66
+
67
+ // we expect this at the global level
68
+ BookReaderJSIAinit(brManifest.data, options);
69
+
70
+ if (customAutoflipParams.autoflip) {
71
+ br.autoToggle(customAutoflipParams);
72
+ }
73
+ };
74
+
75
+ const fetchBookManifestAndInitializeBookreader = async (iaMetadata) => {
76
+ document.querySelector('input[name="itemMD"]').checked = true;
77
+ iaBookReader.item = iaMetadata;
78
+
79
+ const {
80
+ metadata: {
81
+ identifier
82
+ },
83
+ } = iaMetadata;
84
+
85
+ const locator = `https://archive.org/bookreader/BookReaderJSLocate.php?format=json&subPrefix=&id=${identifier}`;
86
+ // Todo: move from `locator` to create `iaManifestUrl` url from `iaMetadata`
87
+ // so we can support multiple volumes
88
+ // const iaManifestUrl = `https://${server}/BookReader/BookReaderJSIA.php?format=jsonp&itemPath=${dir}&id=${identifier}`;
89
+
90
+ const manifest = await fetch(locator)
91
+ .then(response => response.json());
92
+ document.querySelector('input[name="bookManifest"]').checked = true;
93
+
94
+ initializeBookReader(manifest);
95
+ };
96
+
97
+ // Temp; Circumvent bug in BookReaderJSIA code
98
+ window.Sentry = null;
99
+ window.logError = function(e) {
100
+ console.error(e);
101
+ };
102
+ fetch(`https://archive.org/metadata/${ocaid}`)
103
+ .then(response => response.json())
104
+ .then(iaMetadata => fetchBookManifestAndInitializeBookreader(iaMetadata));
@@ -33,14 +33,17 @@
33
33
  <!-- IA scripts -->
34
34
  <script src="https://archive.org/bookreader/BookReaderJSIA.js"></script>
35
35
 
36
- </head>
36
+ <!-- IA fetch demo -->
37
+ <script type="module" src="IADemoBr.js"></script>
38
+
39
+ </head>
37
40
 
38
41
  <body>
39
- <section style="width: 100%; overflow: hidden; background-color: black;">
42
+ <section class="theater" style="width: 100%; overflow: hidden; background-color: black;">
40
43
  <ia-bookreader
41
44
  baseHost="https://archive.org"
42
45
  >
43
- <div id="IABookReaderWrapper" slot="bookreader">
46
+ <div id="IABookReaderWrapper" slot="theater-main">
44
47
  <div id="BookReader" class="BookReader"></div>
45
48
  </div>
46
49
  <div>
@@ -55,103 +58,67 @@
55
58
  </div>
56
59
  </ia-bookreader>
57
60
  </section>
58
- <section style="height: 800px; width: 200px;">
59
- <p>placeholder div to allow scrolling</p>
60
- </section>
61
-
62
-
63
- <script id="pageUrl" type="text/javascript"></script>
64
-
65
- <script>
66
- // gather params here
67
- const urlParams = new URLSearchParams(window.location.search);
68
-
69
- const ocaid = urlParams.get('ocaid');
70
- const openFullImmersionTheater = urlParams.get('view') === 'theater';
71
- const ui = urlParams.get('ui');
72
- const autoflip = urlParams.get('autoflip');
73
- const searchTerm = urlParams.get('q');
74
-
75
- // Override options coming from IA
76
- BookReader.optionOverrides.imagesBaseURL = '/BookReader/images/';
77
-
78
- const initializeBookReader = (brManifest) => {
79
- console.log('initializeBookReader', brManifest);
80
- const br = new BookReader();
81
- const iaBookManifestUrl = '';
82
-
83
- const customAutoflipParams = {
84
- autoflip: !!autoflip,
85
- flipSpeed: urlParams.flipSpeed || 2000,
86
- flipDelay: urlParams.flipDelay || 5000
87
- };
88
-
89
- const options = {
90
- el: '#BookReader',
91
- /* Url plugin - IA uses History mode for URL */
92
- // commenting these out as demo uses hash mode
93
- // keeping them here for reference
94
- // urlHistoryBasePath: `/details/{$ocaid}/`,
95
- // resumeCookiePath: `/details/{$ocaid}/`,
96
- // urlMode: 'history',
97
- // Only reflect these params onto the URL
98
- // urlTrackedParams: ['page', 'search', 'mode'],
99
- /* End url plugin */
100
- enableBookTitleLink: false,
101
- bookUrlText: null,
102
- startFullscreen: urlParams.view === 'theater',
103
- initialSearchTerm: searchTerm ? searchTerm : '',
104
- // leaving this option commented out bc we change given user agent on archive.org
105
- // onePage: { autofit: <?=json_encode($this->ios ? 'width' : 'auto')?> },
106
- showToolbar: false,
107
- /* Multiple volumes */
108
- // To show multiple volumes:
109
- enableMultipleBooks: false, // turn this on
110
- multipleBooksList: [], // populate this // TODO: get sample blob and tie into demo
111
- /* End multiple volumes */
112
- };
113
-
114
- // we want to show item as embedded when ?ui=embed is in URI
115
- if (ui === 'embed') {
116
- options.mode = 1;
117
- options.ui = 'embed';
118
- }
119
-
120
- // we expect this at the global level
121
- BookReaderJSIAinit(brManifest.data, { ...options });
122
-
123
- if (customAutoflipParams.autoflip) {
124
- br.autoToggle(customAutoflipParams);
125
- }
61
+ <style>
62
+ .demos {
63
+ height: 800px;
64
+ width: inherit;
65
+ padding: 10px;
126
66
  }
127
-
128
- const fetchBookManifestAndInitializeBookreader = async (iaMetadata) => {
129
- const {
130
- metadata: {
131
- identifier
132
- },
133
- } = iaMetadata;
134
-
135
- const locator =`https://archive.org/bookreader/BookReaderJSLocate.php?format=json&subPrefix=&id=${identifier}`;
136
- // Todo: move from `locator` to create `iaManifestUrl` url from `iaMetadata`
137
- // so we can support multiple volumes
138
- // const iaManifestUrl = `https://${server}/BookReader/BookReaderJSIA.php?format=jsonp&itemPath=${dir}&id=${identifier}`;
139
-
140
- const manifest = await fetch(locator)
141
- .then(response => response.json())
142
-
143
- initializeBookReader(manifest);
67
+ .demo {
68
+ border: 1px solid white;
69
+ padding: 10px;
144
70
  }
145
-
146
- // Temp; Circumvent bug in BookReaderJSIA code
147
- window.Sentry = null;
148
- window.logError = function(e) {
149
- console.error(e);
150
- };
151
- fetch(`https://archive.org/metadata/${ocaid}`)
152
- .then(response => response.json())
153
- .then(iaMetadata => fetchBookManifestAndInitializeBookreader(iaMetadata));
71
+ </style>
72
+ <section class="demos">
73
+ <div class="demo">
74
+ <button id="toggle-loggedin">Toggle Logged in view</button>
75
+ <p>Features behind signed in gate: Bookmarks</p>
76
+ <p>Logged In Status: <span id="logged-in-status">Logged Out</span></p>
77
+ </div>
78
+ <div class="demo">
79
+ <button id="start-fs">Start at Fullscreen</button>
80
+ </div>
81
+ <div class="demo">
82
+ <h3 id="placeholder">please wait as we are fetching the following: </h3>
83
+ <input type='checkbox' class='group1' name='itemMD' disabled>Item metadata</input><br>
84
+ <input type='checkbox' class='group1' name='bookManifest' disabled> Book manifest</input><br>
85
+ </div>
86
+ </section>
87
+ <div class="demo">
88
+ <h3>Placeholder div to allow scrolling</h3>
89
+ </div>
90
+
91
+ <script >
92
+ // Set up demo things
93
+ const iaBR = document.querySelector('ia-bookreader');
94
+ const toggleLoginBtn = document.querySelector('#toggle-loggedin');
95
+ toggleLoginBtn.addEventListener('click', () => {
96
+ const itemNav = iaBR.shadowRoot.querySelector('ia-item-navigator');
97
+ const bookNav = itemNav.shadowRoot.querySelector('book-navigator');
98
+
99
+ const currLoggedIn = bookNav.signedIn;
100
+ bookNav.signedIn = !currLoggedIn;
101
+
102
+ document.querySelector('#logged-in-status').innerText = bookNav.signedIn ? 'Logged In' : 'Logged Out';
103
+ console.log("Toggled SignedIn state", bookNav.signedIn, { currLoggedIn })
104
+ });
105
+
106
+ const startFSBtn = document.querySelector('#start-fs');
107
+ startFSBtn.addEventListener('click', () => {
108
+ const urlParams = new URLSearchParams(window.location.search);
109
+ if (urlParams.has('view')) {
110
+ const url = new URL(window.location);
111
+ url.searchParams.delete('view');
112
+ window.history.pushState({}, '', url);
113
+ }
114
+ urlParams.set('view', 'theater');
115
+ window.location.search = urlParams.toString();
116
+ });
117
+
118
+ window.addEventListener('BookReader:PostInit', () => {
119
+ const placeholder = document.querySelector('#placeholder');
120
+ placeholder.innerHTML = 'Dependencies are complete, bookreader has loaded';
121
+ });
154
122
  </script>
155
-
156
123
  </body>
157
124
  </html>
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 5.0.0-29
2
+ - import ia-item-navigator for menu management @iisa
3
+ - url plugin: suppress default state on load @dualcnhq
4
+
5
+ # 5.0.0-28
6
+ Dev: Refactor URLPlugin + sync volumes sorting state to URL @dualcnhq @cdrini
7
+
8
+ # 5.0.0-27
9
+ Dev: eslint fix for $.browser @homewardgamer
10
+ Fix: cache search inside requests @iisa
1
11
  # 5.0.0-26
2
12
  Fix: read aloud play/pause button @nsharma123
3
13
  Dev: strict keyboard shortcuts @mc2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/bookreader",
3
- "version": "5.0.0-26",
3
+ "version": "5.0.0-29",
4
4
  "description": "The Internet Archive BookReader.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,7 +9,7 @@
9
9
  "publishConfig": {
10
10
  "access": "public"
11
11
  },
12
- "module": "src/BookNavigator/BookNavigator.js",
12
+ "module": "src/BookNavigator/book-navigator.js",
13
13
  "keywords": [
14
14
  "online",
15
15
  "bookreader",
@@ -26,17 +26,20 @@
26
26
  "private": false,
27
27
  "dependencies": {
28
28
  "@internetarchive/ia-activity-indicator": "^0.0.1",
29
- "@internetarchive/ia-icons": "^1.2.2",
30
- "@internetarchive/ia-menu-slider": "^1.1.0",
31
- "@internetarchive/ia-sharing-options": "^0.1.2",
29
+ "@internetarchive/ia-item-navigator": "^0.0.2",
30
+ "@internetarchive/ia-menu-slider": "^1.1.1",
31
+ "@internetarchive/ia-sharing-options": "^0.1.3-3",
32
32
  "@internetarchive/icon-bookmark": "^1.1.3",
33
33
  "@internetarchive/icon-collapse-sidebar": "^1.1.0",
34
+ "@internetarchive/icon-dl": "^1.1.3",
34
35
  "@internetarchive/icon-edit-pencil": "1.1.5",
35
36
  "@internetarchive/icon-magnify-minus": "^1.2.3",
36
37
  "@internetarchive/icon-magnify-plus": "^1.2.3",
37
38
  "@internetarchive/icon-search": "^1.2.3",
39
+ "@internetarchive/icon-share": "^1.1.3",
40
+ "@internetarchive/icon-visual-adjustment": "^1.1.3",
38
41
  "@internetarchive/modal-manager": "^0.1.0",
39
- "@juggle/resize-observer": "^3.3.1",
42
+ "@internetarchive/shared-resize-observer": "^0.0.1",
40
43
  "lit-element": "^2.4.0",
41
44
  "lit-html": "^1.3.0"
42
45
  },
@@ -0,0 +1,17 @@
1
+ import { svg } from 'lit-element';
2
+
3
+ export default svg`
4
+ <svg class="ia-logo" width="27" height="30" viewBox="0 0 27 30" xmlns="http://www.w3.org/2000/svg" aria-labelledby="logoTitleID logoDescID">
5
+ <title id="logoTitleID">Internet Archive logo</title>
6
+ <desc id="logoDescID">A line drawing of the Internet Archive headquarters building façade.</desc>
7
+ <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
8
+ <mask id="mask-2" fill="white">
9
+ <path d="M26.6666667,28.6046512 L26.6666667,30 L0,30 L0.000283687943,28.6046512 L26.6666667,28.6046512 Z M25.6140351,26.5116279 L25.6140351,28.255814 L1.05263158,28.255814 L1.05263158,26.5116279 L25.6140351,26.5116279 Z M3.62469203,7.6744186 L3.91746909,7.82153285 L4.0639977,10.1739544 L4.21052632,13.9963932 L4.21052632,17.6725617 L4.0639977,22.255044 L4.03962296,25.3421929 L3.62469203,25.4651163 L2.16024641,25.4651163 L1.72094074,25.3421929 L1.55031755,22.255044 L1.40350877,17.6970339 L1.40350877,14.0211467 L1.55031755,10.1739544 L1.68423854,7.80887484 L1.98962322,7.6744186 L3.62469203,7.6744186 Z M24.6774869,7.6744186 L24.9706026,7.82153285 L25.1168803,10.1739544 L25.2631579,13.9963932 L25.2631579,17.6725617 L25.1168803,22.255044 L25.0927809,25.3421929 L24.6774869,25.4651163 L23.2130291,25.4651163 L22.7736357,25.3421929 L22.602418,22.255044 L22.4561404,17.6970339 L22.4561404,14.0211467 L22.602418,10.1739544 L22.7369262,7.80887484 L23.0420916,7.6744186 L24.6774869,7.6744186 Z M9.94042303,7.6744186 L10.2332293,7.82153285 L10.3797725,10.1739544 L10.5263158,13.9963932 L10.5263158,17.6725617 L10.3797725,22.255044 L10.3556756,25.3421929 L9.94042303,25.4651163 L8.47583122,25.4651163 L8.0362015,25.3421929 L7.86556129,22.255044 L7.71929825,17.6970339 L7.71929825,14.0211467 L7.86556129,10.1739544 L8.00005604,7.80887484 L8.30491081,7.6744186 L9.94042303,7.6744186 Z M18.0105985,7.6744186 L18.3034047,7.82153285 L18.449948,10.1739544 L18.5964912,13.9963932 L18.5964912,17.6725617 L18.449948,22.255044 L18.425851,25.3421929 L18.0105985,25.4651163 L16.5460067,25.4651163 L16.1066571,25.3421929 L15.9357367,22.255044 L15.7894737,17.6970339 L15.7894737,14.0211467 L15.9357367,10.1739544 L16.0702315,7.80887484 L16.3753664,7.6744186 L18.0105985,7.6744186 Z M25.6140351,4.53488372 L25.6140351,6.97674419 L1.05263158,6.97674419 L1.05263158,4.53488372 L25.6140351,4.53488372 Z M13.0806755,0 L25.9649123,2.93331338 L25.4484139,3.8372093 L0.771925248,3.8372093 L0,3.1041615 L13.0806755,0 Z" id="path-1"></path>
10
+ </mask>
11
+ <use fill="#FFFFFF" xlink:href="#path-1"></use>
12
+ <g mask="url(#mask-2)" fill="#FFFFFF">
13
+ <path d="M0,0 L26.6666667,0 L26.6666667,30 L0,30 L0,0 Z" id="swatch"></path>
14
+ </g>
15
+ </g>
16
+ </svg>
17
+ `;