@internetarchive/bookreader 5.0.0-27 → 5.0.0-28
Sign up to get free protection for your applications and to get access to all the features.
- package/.nvmrc +1 -0
- package/BookReader/BookReader.js +1 -1
- package/BookReader/bookreader-component-bundle.js +1 -1
- package/BookReader/bookreader-component-bundle.js.map +1 -1
- package/BookReader/plugins/plugin.url.js +1 -1
- package/BookReader/plugins/plugin.url.js.map +1 -1
- package/CHANGELOG.md +3 -0
- package/package.json +1 -1
- package/src/BookNavigator/volumes/volumes-provider.js +41 -9
- package/src/BookNavigator/volumes/volumes.js +14 -3
- package/src/plugins/url/UrlPlugin.js +185 -0
- package/src/plugins/{plugin.url.js → url/plugin.url.js} +24 -2
- package/tests/jest/BookReader.test.js +1 -1
- package/tests/jest/plugins/url/UrlPlugin.test.js +152 -0
- package/tests/jest/plugins/{plugin.url.test.js → url/plugin.url.test.js} +3 -2
- package/tests/karma/BookNavigator/volumes/volumes-provider.test.js +6 -6
- package/webpack.config.js +1 -1
@@ -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(/\/$/,"")),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){if(!i[t.name]&&t.default)return e[t.name]=t.default;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);if("history"==this.urlMode){if(window.history&&window.history.replaceState){var e="".concat(this.urlHistoryBasePath).concat(t);window.history.replaceState({},null,e)}}else window.location.replace("#"+t);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 m=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=m},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 m=i(f[0]);d[p]=m,""===m&&(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 const strStrippedTrailingSlash = `${strPathParams.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 if (!urlStrSplitSlashObj[schema.name] && schema.default) {\n return urlState[schema.name] = schema.default;\n }\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 if (this.urlMode == 'history') {\n if (window.history && window.history.replaceState) {\n const newUrlPath = `${this.urlHistoryBasePath}${urlStrPath}`;\n window.history.replaceState({}, null, newUrlPath);\n }\n } else {\n window.location.replace('#' + urlStrPath);\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":""}
|
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
@@ -7,8 +7,16 @@ import volumesIcon from '../assets/icon_volumes.js';
|
|
7
7
|
|
8
8
|
import './volumes.js';
|
9
9
|
|
10
|
+
const sortType = {
|
11
|
+
title_asc: 'title_asc',
|
12
|
+
title_desc: 'title_desc',
|
13
|
+
default: 'default'
|
14
|
+
};
|
10
15
|
export default class VolumesProvider {
|
11
16
|
|
17
|
+
/**
|
18
|
+
* @param {import('../../BookReader').default} bookreader
|
19
|
+
*/
|
12
20
|
constructor(baseHost, bookreader, optionChange) {
|
13
21
|
this.optionChange = optionChange;
|
14
22
|
this.component = document.createElement("viewable-files");
|
@@ -17,6 +25,9 @@ export default class VolumesProvider {
|
|
17
25
|
this.viewableFiles = Object.keys(files).map(item => files[item]);
|
18
26
|
this.volumeCount = Object.keys(files).length;
|
19
27
|
|
28
|
+
/** @type {import('../../BookReader').default} */
|
29
|
+
this.bookreader = bookreader;
|
30
|
+
|
20
31
|
this.component.subPrefix = bookreader.options.subPrefix || "";
|
21
32
|
this.component.hostUrl = baseHost;
|
22
33
|
this.component.viewableFiles = this.viewableFiles;
|
@@ -25,20 +36,30 @@ export default class VolumesProvider {
|
|
25
36
|
this.label = `Viewable files (${this.volumeCount})`;
|
26
37
|
this.icon = html`${volumesIcon}`;
|
27
38
|
|
28
|
-
this.sortOrderBy =
|
29
|
-
|
39
|
+
this.sortOrderBy = sortType.default;
|
40
|
+
|
41
|
+
// get sort state from query param
|
42
|
+
if (this.bookreader.urlPlugin) {
|
43
|
+
this.bookreader.urlPlugin.pullFromAddressBar();
|
44
|
+
|
45
|
+
const urlSortValue = this.bookreader.urlPlugin.getUrlParam('sort');
|
46
|
+
if (urlSortValue === sortType.title_asc || urlSortValue === sortType.title_desc) {
|
47
|
+
this.sortOrderBy = urlSortValue;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
this.sortVolumes(this.sortOrderBy);
|
30
51
|
}
|
31
52
|
|
32
53
|
get sortButton() {
|
33
54
|
const sortIcons = {
|
34
|
-
|
55
|
+
default: html`
|
35
56
|
<button class="sort-by neutral-icon" aria-label="Sort volumes in initial order" @click=${() => this.sortVolumes("title_asc")}>${sortNeutralIcon}</button>
|
36
57
|
`,
|
37
58
|
title_asc: html`
|
38
59
|
<button class="sort-by asc-icon" aria-label="Sort volumes in ascending order" @click=${() => this.sortVolumes("title_desc")}>${sortAscIcon}</button>
|
39
60
|
`,
|
40
61
|
title_desc: html`
|
41
|
-
<button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("
|
62
|
+
<button class="sort-by desc-icon" aria-label="Sort volumes in descending order" @click=${() => this.sortVolumes("default")}>${sortDescIcon}</button>
|
42
63
|
`,
|
43
64
|
};
|
44
65
|
|
@@ -46,28 +67,39 @@ export default class VolumesProvider {
|
|
46
67
|
}
|
47
68
|
|
48
69
|
/**
|
49
|
-
* @param {'
|
70
|
+
* @param {'default' | 'title_asc' | 'title_desc'} sortByType
|
50
71
|
*/
|
51
72
|
sortVolumes(sortByType) {
|
52
73
|
let sortedFiles = [];
|
53
74
|
|
54
75
|
const files = this.viewableFiles;
|
55
76
|
sortedFiles = files.sort((a, b) => {
|
56
|
-
if (sortByType ===
|
57
|
-
else if (sortByType ===
|
58
|
-
else return b.
|
77
|
+
if (sortByType === sortType.title_asc) return a.title.localeCompare(b.title);
|
78
|
+
else if (sortByType === sortType.title_desc) return b.title.localeCompare(a.title);
|
79
|
+
else return a.orig_sort - b.orig_sort;
|
59
80
|
});
|
60
81
|
|
61
82
|
this.sortOrderBy = sortByType;
|
83
|
+
this.component.sortOrderBy = sortByType;
|
62
84
|
this.component.viewableFiles = [...sortedFiles];
|
63
85
|
this.actionButton = this.sortButton;
|
86
|
+
|
87
|
+
if (this.bookreader.urlPlugin) {
|
88
|
+
this.bookreader.urlPlugin.pullFromAddressBar();
|
89
|
+
if (this.sortOrderBy !== sortType.default) {
|
90
|
+
this.bookreader.urlPlugin.setUrlParam('sort', sortByType);
|
91
|
+
} else {
|
92
|
+
this.bookreader.urlPlugin.removeUrlParam('sort');
|
93
|
+
}
|
94
|
+
}
|
95
|
+
|
64
96
|
this.optionChange(this.bookreader);
|
65
97
|
|
66
98
|
this.multipleFilesClicked(sortByType);
|
67
99
|
}
|
68
100
|
|
69
101
|
/**
|
70
|
-
* @param {'
|
102
|
+
* @param {'default' | 'title_asc' | 'title_desc'} orderBy
|
71
103
|
*/
|
72
104
|
multipleFilesClicked(orderBy) {
|
73
105
|
if (!window.archive_analytics) {
|
@@ -8,13 +8,15 @@ export class Volumes extends LitElement {
|
|
8
8
|
subPrefix: { type: String },
|
9
9
|
hostUrl: { type: String },
|
10
10
|
viewableFiles: { type: Array },
|
11
|
+
sortOrderBy: { type: String },
|
11
12
|
};
|
12
13
|
}
|
13
14
|
|
14
15
|
constructor() {
|
15
16
|
super();
|
16
|
-
this.subPrefix = '';
|
17
17
|
this.hostUrl = '';
|
18
|
+
this.sortOrderBy = '';
|
19
|
+
this.subPrefix = '';
|
18
20
|
this.viewableFiles = [];
|
19
21
|
}
|
20
22
|
|
@@ -36,10 +38,14 @@ export class Volumes extends LitElement {
|
|
36
38
|
}
|
37
39
|
|
38
40
|
volumeItemWithImageTitle(item) {
|
41
|
+
const hrefUrl = this.sortOrderBy === 'default'
|
42
|
+
? `${this.hostUrl}${item.url_path}`
|
43
|
+
: `${this.hostUrl}${item.url_path}?sort=${this.sortOrderBy}`;
|
44
|
+
|
39
45
|
return html`
|
40
46
|
<li class="content active">
|
41
47
|
<div class="separator"></div>
|
42
|
-
<a class="container" href="${
|
48
|
+
<a class="container" href="${hrefUrl}">
|
43
49
|
<div class="image">
|
44
50
|
<img src="${item.image}">
|
45
51
|
</div>
|
@@ -54,11 +60,16 @@ export class Volumes extends LitElement {
|
|
54
60
|
|
55
61
|
volumeItem(item) {
|
56
62
|
const activeClass = this.subPrefix === item.file_subprefix ? ' active' : '';
|
63
|
+
|
64
|
+
const hrefUrl = this.sortOrderBy === 'default'
|
65
|
+
? `${this.hostUrl}${item.url_path}`
|
66
|
+
: `${this.hostUrl}${item.url_path}?sort=${this.sortOrderBy}`;
|
67
|
+
|
57
68
|
return html`
|
58
69
|
<li>
|
59
70
|
<div class="separator"></div>
|
60
71
|
<div class="content${activeClass}">
|
61
|
-
<a href="https://${
|
72
|
+
<a href="https://${hrefUrl}">
|
62
73
|
<p class="item-title">${item.title}</p>
|
63
74
|
</a>
|
64
75
|
</div>
|
@@ -0,0 +1,185 @@
|
|
1
|
+
export class UrlPlugin {
|
2
|
+
constructor(options = {}) {
|
3
|
+
this.bookReaderOptions = options;
|
4
|
+
|
5
|
+
// the canonical order of elements is important in the path and query string
|
6
|
+
this.urlSchema = [
|
7
|
+
{ name: 'page', position: 'path', default: 'n0' },
|
8
|
+
{ name: 'mode', position: 'path', default: '2up' },
|
9
|
+
{ name: 'search', position: 'path', deprecated_for: 'q' },
|
10
|
+
{ name: 'q', position: 'query_param' },
|
11
|
+
{ name: 'sort', position: 'query_param' },
|
12
|
+
{ name: 'view', position: 'query_param' },
|
13
|
+
{ name: 'admin', position: 'query_param' },
|
14
|
+
];
|
15
|
+
|
16
|
+
this.urlState = {};
|
17
|
+
this.urlMode = this.bookReaderOptions.urlMode || 'hash';
|
18
|
+
this.urlHistoryBasePath = this.bookReaderOptions.urlHistoryBasePath || '/';
|
19
|
+
this.urlLocationPollId = null;
|
20
|
+
this.oldLocationHash = null;
|
21
|
+
this.oldUserHash = null;
|
22
|
+
}
|
23
|
+
|
24
|
+
/**
|
25
|
+
* Parse JSON object URL state to string format
|
26
|
+
* Arrange path names in an order that it is positioned on the urlSchema
|
27
|
+
* @param {Object} urlState
|
28
|
+
* @returns {string}
|
29
|
+
*/
|
30
|
+
urlStateToUrlString(urlState) {
|
31
|
+
const searchParams = new URLSearchParams();
|
32
|
+
const pathParams = {};
|
33
|
+
|
34
|
+
Object.keys(urlState).forEach(key => {
|
35
|
+
let schema = this.urlSchema.find(schema => schema.name === key);
|
36
|
+
if (schema?.deprecated_for) {
|
37
|
+
schema = this.urlSchema.find(schemaKey => schemaKey.name === schema.deprecated_for);
|
38
|
+
}
|
39
|
+
if (schema?.position == 'path') {
|
40
|
+
pathParams[schema?.name] = urlState[key];
|
41
|
+
} else {
|
42
|
+
searchParams.append(schema?.name || key, urlState[key]);
|
43
|
+
}
|
44
|
+
});
|
45
|
+
|
46
|
+
const strPathParams = this.urlSchema
|
47
|
+
.filter(s => s.position == 'path')
|
48
|
+
.map(schema => pathParams[schema.name] ? `${schema.name}/${pathParams[schema.name]}` : '')
|
49
|
+
.join('/');
|
50
|
+
|
51
|
+
const strStrippedTrailingSlash = `${strPathParams.replace(/\/$/, '')}`;
|
52
|
+
const concatenatedPath = `${strStrippedTrailingSlash}?${searchParams.toString()}`;
|
53
|
+
return searchParams.toString() ? concatenatedPath : `${strStrippedTrailingSlash}`;
|
54
|
+
}
|
55
|
+
|
56
|
+
/**
|
57
|
+
* Parse string URL and add it in the current urlState
|
58
|
+
* Example:
|
59
|
+
* /page/n7/mode/2up => {page: 'n7', mode: '2up'}
|
60
|
+
* /page/n7/mode/2up/search/hello => {page: 'n7', mode: '2up', q: 'hello'}
|
61
|
+
* @param {string} urlString
|
62
|
+
* @returns {object}
|
63
|
+
*/
|
64
|
+
urlStringToUrlState(urlString) {
|
65
|
+
const urlState = {};
|
66
|
+
|
67
|
+
// Fetch searchParams from given {str}
|
68
|
+
// Note: whole URL path is needed for URL parsing
|
69
|
+
const urlPath = new URL(urlString, 'http://example.com');
|
70
|
+
const urlSearchParamsObj = Object.fromEntries(urlPath.searchParams.entries());
|
71
|
+
const splitUrlMatches = urlPath.pathname.match(/[^\\/]+\/[^\\/]+/g);
|
72
|
+
const urlStrSplitSlashObj = splitUrlMatches ? Object.fromEntries(splitUrlMatches.map(x => x.split('/'))) : {};
|
73
|
+
|
74
|
+
const doesKeyExists = (_object, _key) => {
|
75
|
+
return Object.keys(_object).some(value => value == _key);
|
76
|
+
};
|
77
|
+
|
78
|
+
// Add path objects to urlState
|
79
|
+
this.urlSchema
|
80
|
+
.filter(schema => schema.position == 'path')
|
81
|
+
.forEach(schema => {
|
82
|
+
if (!urlStrSplitSlashObj[schema.name] && schema.default) {
|
83
|
+
return urlState[schema.name] = schema.default;
|
84
|
+
}
|
85
|
+
const hasPropertyKey = doesKeyExists(urlStrSplitSlashObj, schema.name);
|
86
|
+
const hasDeprecatedKey = doesKeyExists(schema, 'deprecated_for') && hasPropertyKey;
|
87
|
+
|
88
|
+
if (hasDeprecatedKey) {
|
89
|
+
urlState[schema.deprecated_for] = urlStrSplitSlashObj[schema.name];
|
90
|
+
return;
|
91
|
+
}
|
92
|
+
|
93
|
+
if (hasPropertyKey) {
|
94
|
+
urlState[schema.name] = urlStrSplitSlashObj[schema.name];
|
95
|
+
return;
|
96
|
+
}
|
97
|
+
});
|
98
|
+
|
99
|
+
// Add searchParams to urlState
|
100
|
+
Object.entries(urlSearchParamsObj).forEach(([key, value]) => {
|
101
|
+
urlState[key] = value;
|
102
|
+
});
|
103
|
+
|
104
|
+
return urlState;
|
105
|
+
}
|
106
|
+
|
107
|
+
/**
|
108
|
+
* Add or update key-value to the urlState
|
109
|
+
* @param {string} key
|
110
|
+
* @param {string} val
|
111
|
+
*/
|
112
|
+
setUrlParam(key, value) {
|
113
|
+
this.urlState[key] = value;
|
114
|
+
|
115
|
+
this.pushToAddressBar();
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Delete key-value to the urlState
|
120
|
+
* @param {string} key
|
121
|
+
*/
|
122
|
+
removeUrlParam(key) {
|
123
|
+
delete this.urlState[key];
|
124
|
+
|
125
|
+
this.pushToAddressBar();
|
126
|
+
}
|
127
|
+
|
128
|
+
/**
|
129
|
+
* Get key-value from the urlState
|
130
|
+
* @param {string} key
|
131
|
+
* @return {string}
|
132
|
+
*/
|
133
|
+
getUrlParam(key) {
|
134
|
+
return this.urlState[key];
|
135
|
+
}
|
136
|
+
|
137
|
+
/**
|
138
|
+
* Push URL params to addressbar
|
139
|
+
*/
|
140
|
+
pushToAddressBar() {
|
141
|
+
const urlStrPath = this.urlStateToUrlString(this.urlState);
|
142
|
+
if (this.urlMode == 'history') {
|
143
|
+
if (window.history && window.history.replaceState) {
|
144
|
+
const newUrlPath = `${this.urlHistoryBasePath}${urlStrPath}`;
|
145
|
+
window.history.replaceState({}, null, newUrlPath);
|
146
|
+
}
|
147
|
+
} else {
|
148
|
+
window.location.replace('#' + urlStrPath);
|
149
|
+
}
|
150
|
+
this.oldLocationHash = urlStrPath;
|
151
|
+
}
|
152
|
+
|
153
|
+
/**
|
154
|
+
* Get the url and check if it has changed
|
155
|
+
* If it was changeed, update the urlState
|
156
|
+
*/
|
157
|
+
listenForHashChanges() {
|
158
|
+
this.oldLocationHash = window.location.hash.substr(1);
|
159
|
+
if (this.urlLocationPollId) {
|
160
|
+
clearInterval(this.urlLocationPollId);
|
161
|
+
this.urlLocationPollId = null;
|
162
|
+
}
|
163
|
+
|
164
|
+
// check if the URL changes
|
165
|
+
const updateHash = () => {
|
166
|
+
const newFragment = window.location.hash.substr(1);
|
167
|
+
const hasFragmentChange = newFragment != this.oldLocationHash;
|
168
|
+
|
169
|
+
if (!hasFragmentChange) { return; }
|
170
|
+
|
171
|
+
this.urlState = this.urlStringToUrlState(newFragment);
|
172
|
+
};
|
173
|
+
this.urlLocationPollId = setInterval(updateHash, 500);
|
174
|
+
}
|
175
|
+
|
176
|
+
/**
|
177
|
+
* Will read either the hash or URL and return the bookreader fragment
|
178
|
+
*/
|
179
|
+
pullFromAddressBar (location = window.location) {
|
180
|
+
const path = this.urlMode === 'history'
|
181
|
+
? (location.pathname.substr(this.urlHistoryBasePath.length) + location.search)
|
182
|
+
: location.hash.substr(1);
|
183
|
+
this.urlState = this.urlStringToUrlState(path);
|
184
|
+
}
|
185
|
+
}
|
@@ -1,4 +1,7 @@
|
|
1
1
|
/* global BookReader */
|
2
|
+
|
3
|
+
import { UrlPlugin } from "./UrlPlugin";
|
4
|
+
|
2
5
|
/**
|
3
6
|
* Plugin for URL management in BookReader
|
4
7
|
* Note read more about the url "fragment" here:
|
@@ -50,7 +53,7 @@ BookReader.prototype.init = (function(super_) {
|
|
50
53
|
this.bind(BookReader.eventNames.PostInit, () => {
|
51
54
|
const { updateWindowTitle, urlMode } = this.options;
|
52
55
|
if (updateWindowTitle) {
|
53
|
-
document.title = this.shortTitle(50);
|
56
|
+
document.title = this.shortTitle(this.bookTitle, 50);
|
54
57
|
}
|
55
58
|
if (urlMode === 'hash') {
|
56
59
|
this.urlStartLocationPolling();
|
@@ -86,7 +89,7 @@ BookReader.prototype.urlStartLocationPolling = function() {
|
|
86
89
|
this.oldLocationHash = this.urlReadFragment();
|
87
90
|
|
88
91
|
if (this.locationPollId) {
|
89
|
-
clearInterval(this.
|
92
|
+
clearInterval(this.locationPollId);
|
90
93
|
this.locationPollId = null;
|
91
94
|
}
|
92
95
|
|
@@ -196,3 +199,22 @@ BookReader.prototype.urlReadFragment = function() {
|
|
196
199
|
BookReader.prototype.urlReadHashFragment = function() {
|
197
200
|
return window.location.hash.substr(1);
|
198
201
|
};
|
202
|
+
export class BookreaderUrlPlugin extends BookReader {
|
203
|
+
init() {
|
204
|
+
if (this.options.enableUrlPlugin) {
|
205
|
+
this.urlPlugin = new UrlPlugin(this.options);
|
206
|
+
this.bind(BookReader.eventNames.PostInit, () => {
|
207
|
+
const { urlMode } = this.options;
|
208
|
+
|
209
|
+
if (urlMode === 'hash') {
|
210
|
+
this.urlPlugin.listenForHashChanges();
|
211
|
+
}
|
212
|
+
});
|
213
|
+
}
|
214
|
+
|
215
|
+
super.init();
|
216
|
+
}
|
217
|
+
}
|
218
|
+
|
219
|
+
window.BookReader = BookreaderUrlPlugin;
|
220
|
+
export default BookreaderUrlPlugin;
|