diff --git a/LICENSE b/LICENSE index c3d0605f..c0f8faa5 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 Satoru MATSUSHIMA +Copyright (c) 2013 Satoru Matsushima Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index 16b2897a..3fd57920 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,19 @@ And you can embed it in other webpages (like YouTube videos). ### Spec. -* EPUB 3+ and 2 compliant. Supports both fixed-layout and reflowable books of various language. +* EPUB 3+ and 2 compliant. Supports both reflowable and fixed-layout books of various language. * Made with JavaScript. Works compatibly with all major web browsers on various OS/devices. -* Open source software released under the MIT License. +* Free. Open source software released under the MIT License. ### Table of Contents -1. [How to Setup & Read/Publish EPUB Books](#how-to-setup--readpublish-epub-books) (for Users/Publishers) +1. [How to Setup & Read/Publish EPUB Books](#how-to-setup--readpublish-epub-books) (for Users/Publishers) 2. [How to Arrange Development Environment](#how-to-arrange-development-environment) (for Developers) 3. [License](#license) 4. [Special Thanks](#special-thanks) -5. [Supported and Sponsored by](#supported-and-sponsored-by) +5. [Supported by](#supported-by) @@ -38,7 +38,7 @@ And you can embed it in other webpages (like YouTube videos). -How to Setup & Read/Publish EPUB Books +How to Setup & Read/Publish EPUB Books -------------------------------------------------------------------------------------------------------------------------------- Bibi supports 3 ways to read/publish EPUB books: @@ -124,6 +124,7 @@ If you want to terminate this feature, set "no" to "accept-files" in `bibi/prese + How to Arrange Development Environment -------------------------------------------------------------------------------------------------------------------------------- @@ -144,8 +145,6 @@ How to Arrange Development Environment 2. $ `cd ` 3. $ `npm install` -And files and folders are generated in `__dist` folder. - ### How to Develop @@ -183,9 +182,9 @@ If you want to modify them, please edit files in the `Source` column instead of ### How to Make a Ditribution Package -* $ `npm run make:distribution-package` - 1. webpack generates production version of Bibi to `__dist` folder. - 2. gulp generates zipped archive to `__archives` folder. +* $ `npm run make:package` + 1. webpack generates temporary files of production version of Bibi. + 2. gulp generates zipped archive to `__archives` folder. After that, the temporary files are removed. @@ -197,31 +196,35 @@ License ### Bibi ([ja](https://bibi.epub.link) / [en](https://github.com/satorumurmur/bibi)) -* Copyright © [Satoru MATSUSHIMA](https://string-letters.com) ([@satorumurmur](https://twitter.com/satorumurmur)) -* Licensed under [the MIT License](https://github.com/satorumurmur/bibi/blob/master/LICENSE). +* © [Satoru Matsushima](https://string-letters.com) ([@satorumurmur](https://twitter.com/satorumurmur)) +* Open source under [the MIT License](https://github.com/satorumurmur/bibi/blob/master/LICENSE) ### Bibi is including and powered by these open source softwares: * Core: - - [sML.js](https://github.com/satorumurmur/sML) ... Copyright © Satoru MATSUSHIMA (Licensed under [the MIT License](https://github.com/satorumurmur/sML/blob/master/LICENSE).) - - [Material Icons](https://material.io/icons/) ... Copyright © Material Design Authors / Google Inc. (Licensed under [the Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html).) + - [sML.js](https://github.com/satorumurmur/sML) : © Satoru Matsushima / Licensed under [the MIT License](https://github.com/satorumurmur/sML/blob/master/LICENSE) + - [Material Icons](https://material.io/icons/) : © Material Design Authors & Google Inc. / Licensed under [the Apache License version 2.0](https://www.apache.org/licenses/LICENSE-2.0.html) * Extensions: - - [Bibi Zip Loader](https://github.com/lunascape/bibi-zip-loader) ... Copyright © Lunascape (Licensed under [the MIT License](https://github.com/lunascape/bibi-zip-loader/blob/master/LICENSE).) - - [JSZip](http://stuk.github.io/jszip) ... Copyright © Stuart Knightley (Dual licensed under [the MIT License or the GPLv3](https://github.com/Stuk/jszip/blob/HEAD/LICENSE.markdown).) - - [JSZipUtils](http://stuk.github.io/jszip-utils) ... Copyright © Stuart Knightley (Dual licensed under [the the MIT License or the GPLv3](https://github.com/Stuk/jszip-utils/blob/master/LICENSE.markdown).) - - [JS-YAML](http://nodeca.github.io/js-yaml/) ... Copyright © Vitaly Puzrin (Licensed under [the MIT License](https://github.com/nodeca/js-yaml/blob/master/LICENSE).) + - [Bibi Zip Loader](https://github.com/lunascape/bibi-zip-loader) : © Lunascape / Licensed under [the MIT License](https://github.com/lunascape/bibi-zip-loader/blob/master/LICENSE) + - [JSZip](http://stuk.github.io/jszip) : © Stuart Knightley / Dual licensed under [the MIT License or the GPLv3](https://github.com/Stuk/jszip/blob/HEAD/LICENSE.markdown) + - [JSZipUtils](http://stuk.github.io/jszip-utils) : © Stuart Knightley / Dual licensed under [the MIT License or the GPLv3](https://github.com/Stuk/jszip-utils/blob/master/LICENSE.markdown) + - [JS-YAML](http://nodeca.github.io/js-yaml/) : © Vitaly Puzrin / Licensed under [the MIT License](https://github.com/nodeca/js-yaml/blob/master/LICENSE) + - [DOMPurify](https://github.com/cure53/DOMPurify) : © Mario Heiderich / Dual licensed under [the Apache License Version 2.0 or the Mozilla Public License Version 2.0](https://github.com/cure53/DOMPurify/blob/master/LICENSE) * Polyfills: - - [classlist-polyfill](https://github.com/yola/classlist-polyfill) ... by Yola Inc. (Released into the public domain under [the Unlicense](https://github.com/yola/classlist-polyfill/blob/master/LICENSE)) - - [text-encoding-utf-8](https://github.com/arv/text-encoding-utf-8) ... by Erik Arvidsson (Released into the public domain under [the Unlicense](https://github.com/arv/text-encoding-utf-8/blob/master/LICENSE.md).) - - [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver) ... Copyright (c) W3C (Licensed under [the W3C Software and Document License](https://github.com/w3c/IntersectionObserver/blob/master/LICENSE.md).) - - [document.currentScript Polyfill](https://github.com/amiller-gh/currentScript-polyfill) ... Copyright © Adam Miller (Licensed under [the MIT License](https://github.com/amiller-gh/currentScript-polyfill/blob/master/LICENSE).) - - [custom-event-polyfill](https://github.com/kumarharsh/custom-event-polyfill) ... Copyright © Evan Krambuhl (Licensed under [the MIT License](https://github.com/kumarharsh/custom-event-polyfill/blob/master/LICENSE).) - - [Native Promise Only (NPO)](https://github.com/getify/native-promise-only) ... Copyright © Kyle Simpson (Licensed under [the MIT License](http://getify.mit-license.org/).) - - [Polyfill Array.prototype.includes](https://github.com/latusinski/polyfill-array-includes) ... Copyright © Kevin Latusinski (Licensed under [the MIT License](https://www.npmjs.com/package/polyfill-array-includes).) - - [String.prototype.padStart](https://github.com/KhaledElAnsari/String.prototype.padStart) ... Copyright © Khaled Al-Ansari (Licensed under [the MIT License](https://github.com/KhaledElAnsari/String.prototype.padStart/blob/master/LICENSE).) - - [url-polyfill](https://github.com/lifaon74/url-polyfill) ... Copyright © Valentin Richard (Licensed under [the MIT License](https://github.com/lifaon74/url-polyfill/blob/master/LICENSE).) + - [classlist-polyfill](https://github.com/yola/classlist-polyfill) : by Yola Inc. / Released into the public domain under [the Unlicense](https://github.com/yola/classlist-polyfill/blob/master/LICENSE) + - [text-encoding-utf-8](https://github.com/arv/text-encoding-utf-8) : by Erik Arvidsson / Released into the public domain under [the Unlicense](https://github.com/arv/text-encoding-utf-8/blob/master/LICENSE.md) + - [IntersectionObserver polyfill](https://github.com/w3c/IntersectionObserver) : © W3C / Licensed under [the W3C Software and Document License](https://github.com/w3c/IntersectionObserver/blob/master/LICENSE.md) + - [document.currentScript Polyfill](https://github.com/amiller-gh/currentScript-polyfill) : © Adam Miller / Licensed under [the MIT License](https://github.com/amiller-gh/currentScript-polyfill/blob/master/LICENSE) + - [custom-event-polyfill](https://github.com/kumarharsh/custom-event-polyfill) : © Evan Krambuhl / Licensed under [the MIT License](https://github.com/kumarharsh/custom-event-polyfill/blob/master/LICENSE) + - [Native Promise Only (NPO)](https://github.com/getify/native-promise-only) : © Kyle Simpson / Licensed under [the MIT License](http://getify.mit-license.org/) + - [ES6 Object.assign()](https://github.com/rubennorte/es6-object-assign) : © Rubén Norte / Licensed under [the MIT License](https://github.com/rubennorte/es6-object-assign/blob/master/LICENSE) + - [Polyfill Array.prototype.includes](https://github.com/latusinski/polyfill-array-includes) : © Kevin Latusinski / Licensed under [the MIT License](https://www.npmjs.com/package/polyfill-array-includes) + - [String.prototype.padStart](https://github.com/KhaledElAnsari/String.prototype.padStart) : © Khaled Al-Ansari / Licensed under [the MIT License](https://github.com/KhaledElAnsari/String.prototype.padStart/blob/master/LICENSE) + - [url-polyfill](https://github.com/lifaon74/url-polyfill) : © Valentin Richard / Licensed under [the MIT License](https://github.com/lifaon74/url-polyfill/blob/master/LICENSE) + + Special Thanks @@ -230,10 +233,15 @@ Special Thanks Thanks to the contributors and the users around the world. -Supported and Sponsored by + + +Supported by -------------------------------------------------------------------------------------------------------------------------------- -### [Lunascape](https://lunascape.org) +### [Lunascape](https://www.lunascape.tv) -* Bibi v1.0.0 was developed with greateful support and sponsoring from [Lunascape](https://lunascape.org) (and their parent company [MEDIA DO Co.,Ltd.](https://mediado.jp/mediado) who absorbed and merged them). +* Bibi v1.0.0 was developed with greateful support and sponsoring from [Lunascape](https://www.lunascape.tv). * Lunascape also developed [Bibi Zip Loader](https://github.com/lunascape/bibi-zip-loader) especially for Bibi, which realized fast progressive loading of zipped EPUBs. + + + diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR.epub b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR.epub new file mode 100644 index 00000000..c11f1ad7 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR.epub differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/cov.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/cov.png new file mode 100644 index 00000000..eae75a1d Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/cov.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/nav.xhtml b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/nav.xhtml new file mode 100644 index 00000000..8661ed9c --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/nav.xhtml @@ -0,0 +1,26 @@ + + + + Nav + + +
+

ToC

+
+ + + + diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p01.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p01.png new file mode 100644 index 00000000..eae6e020 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p01.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p02.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p02.png new file mode 100644 index 00000000..9312e2c0 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p02.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p03.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p03.png new file mode 100644 index 00000000..fbf0cc7d Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p03.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p04.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p04.png new file mode 100644 index 00000000..13f7578d Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p04.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p05.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p05.png new file mode 100644 index 00000000..623d70ae Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p05.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p06.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p06.png new file mode 100644 index 00000000..ee3bcce9 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p06.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p07.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p07.png new file mode 100644 index 00000000..a65eff71 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/p07.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/package.opf b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/package.opf new file mode 100644 index 00000000..3fa98e0e --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/CONTENT/package.opf @@ -0,0 +1,35 @@ + + + + urn:uuid:C3171445-62C6-470B-814B-14D15745BA8E + Sample: Pre-Paginated LtR + ja-jp + Bibi Lalalu + bibi.epub.link + 2020-03-11T12:00:00Z + pre-paginated + portrait + landscape + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/META-INF/container.xml b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/META-INF/container.xml new file mode 100644 index 00000000..9c242ff7 --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/mimetype b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/mimetype new file mode 100644 index 00000000..57ef03f2 --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_LtR/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL.epub b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL.epub new file mode 100644 index 00000000..ca972ff7 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL.epub differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/cov.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/cov.png new file mode 100644 index 00000000..9cdbcdfb Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/cov.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/nav.xhtml b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/nav.xhtml new file mode 100644 index 00000000..8661ed9c --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/nav.xhtml @@ -0,0 +1,26 @@ + + + + Nav + + +
+

ToC

+
+ + + + diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p01.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p01.png new file mode 100644 index 00000000..eae6e020 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p01.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p02.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p02.png new file mode 100644 index 00000000..9312e2c0 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p02.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p03.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p03.png new file mode 100644 index 00000000..fbf0cc7d Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p03.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p04.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p04.png new file mode 100644 index 00000000..13f7578d Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p04.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p05.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p05.png new file mode 100644 index 00000000..623d70ae Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p05.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p06.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p06.png new file mode 100644 index 00000000..ee3bcce9 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p06.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p07.png b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p07.png new file mode 100644 index 00000000..a65eff71 Binary files /dev/null and b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/p07.png differ diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/package.opf b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/package.opf new file mode 100644 index 00000000..e0100c9f --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/CONTENT/package.opf @@ -0,0 +1,35 @@ + + + + urn:uuid:D7F57A3B-8D31-4004-A5BF-543B40DA21E8 + Sample: Pre-Paginated RtL + ja-jp + Bibi Lalalu + bibi.epub.link + 2020-03-11T12:00:00Z + pre-paginated + portrait + landscape + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/META-INF/container.xml b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/META-INF/container.xml new file mode 100644 index 00000000..9c242ff7 --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/META-INF/container.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/mimetype b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/mimetype new file mode 100644 index 00000000..57ef03f2 --- /dev/null +++ b/__src/bibi-bookshelf/__samples/Sample_Pre-Paginated_RtL/mimetype @@ -0,0 +1 @@ +application/epub+zip \ No newline at end of file diff --git a/__src/bibi-demo/embedding/index.html b/__src/bibi-demo/embedding/index.html new file mode 100644 index 00000000..d58ceda4 --- /dev/null +++ b/__src/bibi-demo/embedding/index.html @@ -0,0 +1,84 @@ + + + + + + + Bibi Demo: Embedding + + + +
+ + + + + +
+ + + diff --git a/__src/bibi-demo/embedding/index.scss b/__src/bibi-demo/embedding/index.scss new file mode 100644 index 00000000..716af01a --- /dev/null +++ b/__src/bibi-demo/embedding/index.scss @@ -0,0 +1,50 @@ +@charset "utf-8"; +@import "../../bibi/resources/styles/#globals"; + +* { + margin: 0; + border: none 0; + padding: 0; + font-size: 100%; + font-weight: inheirt; + font-style: inherit; +} + +html { + font-size: 12px; + @include size(100%); + background: white; +} + +body { + display: flex; + flex-direction: column; + justify-content: space-between; + margin: 10px; + @include size(calc(100% - 20px)); +} + +main { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: space-between; + @include size(auto, 100%); + > iframe.bibi-frame:nth-of-type(1) { + margin-bottom: 10px; + @include size(100%, calc(50% - 5px)); + box-shadow: 0 0 0 4px rgba(hotpink, 0); animation: FLASH-R .222s linear; + } + > iframe.bibi-frame:nth-of-type(2) { + @include size(calc(50% - 5px)); + box-shadow: 0 0 0 4px rgba(turquoise, 0); animation: FLASH-G .222s linear; + } + > iframe.bibi-frame:nth-of-type(3) { + @include size(calc(50% - 5px)); + box-shadow: 0 0 0 4px rgba(steelblue, 0); animation: FLASH-B .222s linear; + } +} + +@keyframes FLASH-R { 7% { box-shadow: 0 0 0 4px hotpink; } } +@keyframes FLASH-G { 7% { box-shadow: 0 0 0 4px turquoise; } } +@keyframes FLASH-B { 7% { box-shadow: 0 0 0 4px steelblue; } } diff --git a/__src/bibi/and/jo.js b/__src/bibi/and/jo.js index 7267ba72..c2a7cf36 100644 --- a/__src/bibi/and/jo.js +++ b/__src/bibi/and/jo.js @@ -1,185 +1,191 @@ -(() => { - 'use strict'; - if(window['bibi:jo']) return; - if(!Array.prototype.includes) Array.prototype.includes = function(I) { for(let l = this.length, i = 0; i < l; i++) if(this[i] == I) return true; return false; }; - const Jo = window['bibi:jo'] = { 'version': '____Bibi-Version____', - CSS: require('./jo.scss'), - Status: '', - Bibis: [], - Anchors: [], - Holders: [], - Frames: [], - TrustworthyOrigins: [location.origin], - Loaded: 0 - }; - Jo.Path = (() => { - if(document.currentScript) return document.currentScript.src; - const Scripts = document.getElementsByTagName('script'); - return Scripts[Scripts.length - 1].src; - })(); - Jo.embed = () => { - Jo.Status = 'Started'; - const As = document.body.querySelectorAll('a[data-bibi]'); - for(let l = As.length, i = 0; i < l; i++) { - if(!As[i].getAttribute('href') || As[i].Bibi) continue; - // Bibi Object - const Bibi = { Index: i, Number: i + 1 }; - // Anchor - const Anchor = Bibi.Anchor = As[i]; - if(!/ bibi-anchor /.test(' ' + Anchor.className + ' ')) Anchor.className = 'bibi-anchor' + (Anchor.className ? ' ' + Anchor.className : ''); - if(Anchor.origin != location.origin) Jo.TrustworthyOrigins.push(Anchor.origin); - Anchor.addEventListener('bibi:loaded', function(Eve) { console.log('Bibi: Loaded. - #' + Eve.detail.Number + ': ' + Eve.detail.Anchor.href); }, false); - Jo.Anchors.push(Anchor); - // Holder - const BibiClass = Anchor.getAttribute('data-bibi-class'); - const BibiID = Anchor.getAttribute('data-bibi-id'); - const BibiStyle = Anchor.getAttribute('data-bibi-style'); - const Holder = Bibi.Holder = Jo.create('span', { - className: 'bibi-holder' + (BibiClass ? ' ' + BibiClass : ''), - id: (BibiID ? BibiID : 'bibi-holder-' + (i + 1)), - title: (Anchor.innerText ? Anchor.innerText + ' ' : '') + '(powered by Bibi)' - }); - if(BibiStyle) Holder.setAttribute('style', BibiStyle); - Jo.Holders.push(Holder); - // Fragments - const Fragments = new Jo.Fragments(); - Fragments.add('parent-title', document.title); - Fragments.add('parent-uri', location.href); - Fragments.add('parent-origin', location.origin); - Fragments.add('parent-jo-path', Jo.Path); - Fragments.add('parent-bibi-label', Anchor.innerHTML); - Fragments.add('parent-holder-id', Holder.id); - [ - 'to', - 'nav', - 'autostart', 'autostart-embedded', - 'fix-reader-view-mode', - 'preprocess-html-always', - 'reader-view-mode', - 'single-page-always', - 'start-in-new-window', 'start-embedded-in-new-window', - 'use-arrows', - 'use-font-size-changer', - 'use-full-height', - 'use-keys', - 'use-loupe', - 'use-menubar', //'place-menubar-at-top', - 'use-nombre' - ].forEach(PresetKey => { - const PresetValue = Anchor.getAttribute('data-bibi-' + PresetKey); - if(!PresetValue) return; - let RE; - switch(PresetKey) { - case 'to': RE = /^[1-9][\d\-\.]*$/; break; - case 'nav': RE = /^[1-9]\d*$/; break; - case 'reader-view-mode': RE = /^(horizontal|vertical|paged)$/; break; - default: RE = /^(true|false|yes|no|mobile|desktop)?$/; break; - } - if(/^(autostart|start-in-new-window)$/.test(PresetKey)) PresetKey = PresetKey.replace('start', 'start-embedded'); - if(RE.test(PresetValue)) Fragments.add(PresetKey, PresetValue); - }); - // Frame - const BibiSrc = Anchor.getAttribute('href'); - const Frame = Bibi.Frame = Holder.appendChild( - Jo.create('iframe', { - className: 'bibi-frame', - frameborder: '0', - scrolling: 'auto', - allowfullscreen: 'true', - src: BibiSrc + (/#/.test(BibiSrc) ? ',' : '#') + Fragments.make() - }) - ); - Frame.addEventListener('load', () => { - Jo.Loaded++; - Frame.Bibi.Anchor.dispatchEvent(new CustomEvent('bibi:loaded', { detail: Frame.Bibi })); - if(Jo.Status != 'TimedOut' && Jo.Loaded == Jo.Bibis.length) { - Jo.Status = 'Loaded'; - document.dispatchEvent(new CustomEvent('bibi:loaded', { detail: Jo })); - } - }, false); - Jo.Frames.push(Frame); - // Add - Jo.Bibis.push(Bibi); - Frame.Bibi = Holder.Bibi = Anchor.Bibi = Bibi; - } - // Put - for(let l = Jo.Bibis.length, i = 0; i < l; i++) { - if(Jo.Bibis[i].Embedded) continue; - const Bibi = Jo.Bibis[i]; - Bibi.move = (Distance) => { - if(typeof Target != 'number') return; - Bibi.Frame.contentWindow.postMessage(`{'bibi:commands:move':'${ Distance }'}`, Bibi.Anchor.origin); - }; - Bibi.focus = (Target) => { - if(typeof Target != 'string' && typeof Target != 'number') return; - Bibi.Frame.contentWindow.postMessage(`{'bibi:commands:focus':'${ Target }'}`, Bibi.Anchor.origin); - }; - Bibi.changeView = (BDM) => { - if(typeof Target != 'string') return; - Bibi.Frame.contentWindow.postMessage(`{'bibi:commands:change-view':'${ BDM }'}`, Bibi.Anchor.origin); - }; - Bibi.togglePanel = () => { - Bibi.Frame.contentWindow.postMessage(`{'bibi:command:toggle-panel':''}`, Bibi.Anchor.origin); - }; - Bibi.Anchor.style.display = 'none'; - Bibi.Anchor.parentNode.insertBefore(Bibi.Holder, Bibi.Anchor); - Bibi.Anchor.dispatchEvent(new CustomEvent('bibi:readied', { detail: Bibi })); +(() => { 'use strict'; if(window['bibi:jo']) return; + + + + +const Jo = window['bibi:jo'] = { 'version': '____Bibi-Version____', + CSS: require('./jo.scss'), + Status: '', + Bibis: [], + TrustworthyOrigins: [location.origin], + Loaded: 0, +}; + +const BibiEventRE = /^bibi:[a-z][a-z:\-]*$/; + + + + +Jo.Bibi = function() { return Jo.callBibi.apply(Jo, arguments); }; + +Jo.callBibi = (Love) => { + let Anchor = null, Frame = null, Receiver = null, ToReceive = []; + try { + if(!(Love instanceof HTMLElement)) { + if(Love && typeof Love == 'object') { + if(!Love['bibi-href']) return null; + Anchor = Jo.create('a', { href: Love['bibi-href'] }); + Frame = Jo.create('iframe'); + Receiver = Frame; + } + if(Love['bibi-receive'] instanceof Array) ToReceive = Love['bibi-receive']; + } else { + if(/^iframe$/i.test(Love.tagName)) { + const BibiHref = Love.getAttribute('data-bibi-href'); + if(!BibiHref) return null; + Anchor = Love.parentNode.insertBefore(Jo.create('a', { href: BibiHref }), Love); + Frame = Love.parentNode.removeChild(Love); + } else if(/^a$/i.test(Love.tagName)) { + if(!Love.href) return null; + Anchor = Love; + Frame = Jo.create('iframe'); + (BibiClass => BibiClass ? BibiClass.trim().replace(/\s+/, ' ').split(' ').forEach(CN => CN ? Frame.classList.add(CN) : false) : false)(Anchor.getAttribute('data-bibi-class')); + (BibiID => BibiID ? Frame.setAttribute('id', BibiID ) : false)(Anchor.getAttribute('data-bibi-id' )); + (BibiStyle => BibiStyle ? Frame.setAttribute('style', BibiStyle) : false)(Anchor.getAttribute('data-bibi-style')); + } + Receiver = Love; + let BibiReceive = Receiver.getAttribute('data-bibi-receive'); + if(BibiReceive && (BibiReceive = BibiReceive.replace(/\s+/, ''))) ToReceive = BibiReceive.split(','); + } - setTimeout(() => { - if(Jo.Status == 'Loaded') return; - Jo.Status = 'TimedOut'; - document.dispatchEvent(new CustomEvent('bibi:timed-out', { detail: Jo })); - }, 12000); - Jo.Status = 'Readied'; - document.dispatchEvent(new CustomEvent('bibi:readied', { detail: Jo })); - return Jo.Bibis; + } catch(Err) { return null; } if(!Anchor || !Frame || !Receiver) return null; + const Bibi = Anchor.Bibi = Frame.Bibi = { Jo: Jo, Anchor: Anchor, Frame: Frame, Receiver: Receiver, Index: Jo.Bibis.length, Status: '' }; + Bibi.listen = (EN, fun) => !BibiEventRE.test(EN) ? false : Receiver.addEventListener(EN, Eve => fun.call(Receiver, Eve.detail), false); + Bibi.dispatch = (EN, Det = Bibi) => !BibiEventRE.test(EN) ? false : Receiver.dispatchEvent(new CustomEvent(EN, { detail: Det })); + Bibi.receive = (EN) => !BibiEventRE.test(EN) ? false : Frame.contentWindow.E.add(EN, Det => Bibi.dispatch(EN, Det)); + Bibi.post = (EN, V) => !BibiEventRE.test(EN) ? false : Frame.contentWindow.postMessage(`{ "${ EN }" : "${ V }" }`, Anchor.origin); + Bibi.listen('bibi:initialized', (Status) => Bibi.Status = Bibi.Initialized = Status); if(ToReceive.length) Bibi.listen('bibi:initialized', () => ToReceive.forEach(EN => Bibi.receive('' + EN.trim()))); + Bibi.listen('bibi:readied', (Status) => Bibi.Status = Bibi.Readied = Status); + Bibi.listen('bibi:prepared', (Status) => Bibi.Status = Bibi.Prepared = Status); + Bibi.listen('bibi:opened', (Status) => Bibi.Status = Bibi.Opened = Status); + Bibi.listen('bibi:opened', () => { + Bibi.move = (Distance) => Bibi.post('bibi:commands:move', Distance); + Bibi.focus = (Target) => Bibi.post('bibi:commands:focus', Target); + Bibi.changeView = (RVM) => Bibi.post('bibi:commands:change-view', RVM); + Bibi.togglePanel = () => Bibi.post('bibi:commands:toggle-panel', ''); + }); + Anchor.style.display = 'none'; + if(!Jo.TrustworthyOrigins.includes(Anchor.origin)) Jo.TrustworthyOrigins.push(Anchor.origin); // It is NOT reflected to S['trustworthy-origins']. + Anchor.href += (/#/.test(Anchor.href) ? ',' : '#') + (() => { + const Fragments = new Jo.Fragments(); + Fragments.add('parent-bibi-index', Bibi.Index); + [ + 'autostart-embedded', 'autostart', + 'fix-reader-view-mode', 'fix-view', 'view-unchangeable', + 'full-breadth-layout-in-scroll', + 'iipp', + 'nav', + 'reader-view-mode', 'rvm', 'view', + 'start-embedded-in-new-window', 'start-in-new-window' + ].forEach(K => { let V = '' + (Love.ownerDocument ? Love.getAttribute('data-bibi-' + K) || '' : Love['bibi-' + K]); + if(V && (() => { switch(K) { + case 'iipp': return /^(\d*\.)?\d+$/; + case 'nav': return /^[1-9][0-9]*$/; + case 'rvm': case 'view': K = 'reader-view-mode'; + case 'reader-view-mode': return /^(paged|horizontal|vertical)$/; + case 'autostart': case 'start-in-new-window': K = K.replace('start', 'start-embedded'); break; + case 'view-unchangeable': K = 'fix-reader-view-mode'; break; + } return /^(true|false|1|0|yes|no|mobile|desktop)$/; })().test(V)) Fragments.add(K, V); + }); + return Fragments.make(); + })(); + Frame.classList.add('bibi-frame'); + Frame.setAttribute('frameborder', '0'); + Frame.setAttribute('scrolling', 'auto'); + Frame.setAttribute('allowfullscreen', 'true'); + Frame.src = Anchor.href; + Jo.Bibis.push(Bibi); + return Bibi; +}; + + + + +Jo.embed = () => { + const BibisToBeLoaded = [], BibisLoaded = []; + Array.prototype.forEach.call(document.body.querySelectorAll('*[data-bibi]'), Bed => { + if(Bed.getAttribute('data-bibi-processed')) return; + Bed.setAttribute('data-bibi-processed', 'true'); + const Bibi = new Jo.Bibi(Bed); + if(Bibi) BibisToBeLoaded.push(Bibi); + }); + if(!BibisToBeLoaded.length) return; + //Jo.listen('bibi:jo:embedded', Bibis => console.log(`[Bibi:Jo] Embedded. - ${ Bibis.length } of ${ Jo.Bibis.length }`)); + BibisToBeLoaded.forEach(Bibi => { + const Anchor = Bibi.Anchor, Frame = Bibi.Frame; + Bibi.listen('bibi:initialized', () => (BibisLoaded.push(Bibi) < BibisToBeLoaded.length) ? false : Jo.dispatch('bibi:jo:embedded', BibisLoaded)); + Anchor.parentNode.insertBefore(Frame, Anchor); + }); +}; + + + + +document.addEventListener('DOMContentLoaded', Jo.embed), window.addEventListener('load', Jo.embed); + + + + +window.addEventListener('message', Eve => { + if(!Eve || !Jo.judge(Eve.data, Eve.origin)) return false; try { + Data = JSON.parse(Data); + if(typeof Data != 'object' || !Data) return false; + for(let EN in Data) Jo.dispatch(EN, Data[EN]); + return true; } catch(Err) {} return false; +}, false); + + + + +// Utility + +Jo.create = (TagName, Properties) => { + const Ele = document.createElement(TagName); + for(let Attribute in Properties) Ele[Attribute] = Properties[Attribute]; + return Ele; +}; + +Jo.encode = (Str) => encodeURIComponent(Str).replace('(', '_BibiKakkoOpen_').replace(')', '_BibiKakkoClose_'); + +Jo.Fragments = function() { // constructor + this.FragmentKeys = []; + this.FragmentKeysAndValues = {}; + this.add = function(Key, Value) { + if(!this.FragmentKeys.includes(Key)) this.FragmentKeys.push(Key); + this.FragmentKeysAndValues[Key] = Value; }; - Jo.encode = (Str) => encodeURIComponent(Str).replace('(', '_BibiKakkoOpen_').replace(')', '_BibiKakkoClose_'); - Jo.create = (TagName, Properties) => { - const Ele = document.createElement(TagName); - for(let Attribute in Properties) Ele[Attribute] = Properties[Attribute]; - return Ele; + this.make = function() { + if(!this.FragmentKeys.length) return ''; + const Fragments = []; + for(let l = this.FragmentKeys.length, i = 0; i < l; i++) Fragments.push(`${ this.FragmentKeys[i] }:${ Jo.encode(this.FragmentKeysAndValues[this.FragmentKeys[i]]) }`); + return `jo(${ Fragments.join(',') })`; }; - Jo.Fragments = function() { // constructor - this.FragmentKeys = []; - this.FragmentKeysAndValues = {}; - this.add = function(Key, Value) { - if(!this.FragmentKeys.includes(Key)) this.FragmentKeys.push(Key); - this.FragmentKeysAndValues[Key] = Value; - }; - this.make = function() { - if(!this.FragmentKeys.length) return ''; - const Fragments = []; - for(let l = this.FragmentKeys.length, i = 0; i < l; i++) Fragments.push(`${ this.FragmentKeys[i] }:${ Jo.encode(this.FragmentKeysAndValues[this.FragmentKeys[i]]) }`); - return `jo(${ Fragments.join(',') })`; - }; - return this; + return this; +}; + +Jo.judge = (Msg, Origin) => (Msg && typeof Msg == 'string' && Origin && typeof Origin == 'string' && Jo.TrustworthyOrigins.includes(Origin)); + +Jo.listen = (EN, fun) => !BibiEventRE.test(EN) ? false : document.addEventListener(EN, Eve => fun.call(document, Eve.detail)); +Jo.dispatch = (EN, Det = Jo) => !BibiEventRE.test(EN) ? false : document.dispatchEvent(new CustomEvent(EN, { detail: Det })); + + + + +// Polyfill + +if(!Array.prototype.includes) Array.prototype.includes = function(I) { for(let l = this.length, i = 0; i < l; i++) if(this[i] == I) return true; return false; }; + +if(!window.CustomEvent || (typeof window.CustomEvent !== 'function') && (window.CustomEvent.toString().indexOf('CustomEventConstructor') === -1)) { + window.CustomEvent = function(EventName, Arguments) { // constructor + Arguments = Arguments || { bubbles: false, cancelable: false, detail: undefined }; + const Eve = document.createEvent('CustomEvent'); + Eve.initCustomEvent(EventName, Arguments.bubbles, Arguments.cancelable, Arguments.detail); + return Eve; }; - if(!window.CustomEvent || (typeof window.CustomEvent !== 'function') && (window.CustomEvent.toString().indexOf('CustomEventConstructor') === -1)) { - window.CustomEvent = function(EventName, Arguments) { // constructor - Arguments = Arguments || { bubbles: false, cancelable: false, detail: undefined }; - const Eve = document.createEvent('CustomEvent'); - Eve.initCustomEvent(EventName, Arguments.bubbles, Arguments.cancelable, Arguments.detail); - return Eve; - }; - window.CustomEvent.prototype = window.Event.prototype; - } - window.addEventListener('message', Eve => { - if(!Eve || !Eve.data) return; - for(let l = Jo.TrustworthyOrigins.length, i = 0; i < l; i++) { - if(Eve.origin != Jo.TrustworthyOrigins[i]) continue; - let Data = Eve.data; - try { - Data = JSON.parse(Data); - if(typeof Data != 'object' || !Data) return false; - for(let EventName in Data) if(/^bibi:commands:/.test(EventName)) document.dispatchEvent(new CustomEvent(EventName, { detail: Data[EventName] })); - return true; - } catch(Err) {} - return false; - } - }, false); - document.addEventListener('bibi:readied', Eve => console.log(`Bibi: Readied. - ${ Eve.detail.Bibis.length } Bibi${ Eve.detail.Bibis.length > 1 ? 's' : '' }.`)); - document.addEventListener('bibi:loaded', Eve => console.log(`Bibi: Loaded. - ${ Eve.detail.Bibis.length } Bibi${ Eve.detail.Bibis.length > 1 ? 's' : '' }.`)); - document.addEventListener('bibi:timed-out', Eve => console.log(`Bibi: Timed Out.` )); - document.addEventListener('DOMContentLoaded', Jo.embed, false); + window.CustomEvent.prototype = window.Event.prototype; +} + + + + })(); \ No newline at end of file diff --git a/__src/bibi/and/jo.scss b/__src/bibi/and/jo.scss index ccb63416..1a2546de 100644 --- a/__src/bibi/and/jo.scss +++ b/__src/bibi/and/jo.scss @@ -1,32 +1,19 @@ -// ================================================================================ -// + Pipi (Bibi Putter) -// -------------------------------------------------------------------------------- +/*! Bibi Frame Style */ - -// - Reset -// -------------------------------------------------------------------------------- - -span.bibi-holder { - &, - >iframe.bibi-frame { - display: block; - box-sizing: border-box; - position: relative; - margin: 0; - padding: 0; - border: none 0 transparent; - vertical-align: top; - line-height: 1; - text-decoration: none; - background: transparent; - } - & { - max-width: 100%; - max-height: 100%; - } - >iframe.bibi-frame { - border: solid 1px rgb(216,216,216); - width: 100%; - height: 100%; - } +iframe.bibi-frame { + display: inline-block; + box-sizing: border-box; + position: relative; + margin: 0; + padding: 0; + border: solid 1px rgb(222,222,222); + border-radius: 1px; + max-width: 100%; + max-height: 100%; + width: 100%; + height: 100%; + vertical-align: top; + line-height: 1; + text-decoration: none; + background: transparent; } \ No newline at end of file diff --git a/__src/bibi/extensions/extractor/at-once.js b/__src/bibi/extensions/extractor/at-once.js index 3f819e8e..68122d2c 100644 --- a/__src/bibi/extensions/extractor/at-once.js +++ b/__src/bibi/extensions/extractor/at-once.js @@ -50,10 +50,14 @@ Bibi.x({ } if(!FilesToBeExtract.length) return reject(`Does Not Contain Any Resources`); let FolderName = '', FolderNameRE = undefined; - if(!FilesToBeExtract.includes(B.Container.Path) && !FilesToBeExtract.includes(B.ZineData.Path)) { - [B.Container.Path, B.ZineData.Path].forEach(ToBeFound => { + const PathsToBeChecked = []; + if(B.Type != 'Zine') PathsToBeChecked.push(B.Container.Path); // EPUB or unknown. + if(B.Type != 'EPUB') PathsToBeChecked.push(B.ZineData.Path ); // Zine or unknown. + if(!PathsToBeChecked.filter(PathToBeChecked => FilesToBeExtract.includes(PathToBeChecked)).length) { + PathsToBeChecked.forEach(PathToBeChecked => { + if(!PathToBeChecked) return; if(FolderName) return; - const RE = new RegExp('^(.+?\\/)' + ToBeFound.replace(/\//g, '\\/').replace(/\./g, '\\.') + '$'); + const RE = new RegExp('^(.+?\\/)' + PathToBeChecked.replace(/\//g, '\\/').replace(/\./g, '\\.') + '$'); for(let l = FilesToBeExtract.length, i = 0; i < l; i++) { const FileName = FilesToBeExtract[i]; if(RE.test(FileName)) { @@ -64,15 +68,11 @@ Bibi.x({ } }); } - if(FilesToBeExtract.includes(FolderName + B.Container.Path)) { - if(!B.Type) B.Type = 'EPUB'; - else if(B.Type == 'Zine') reject({ BookTypeError: `It Seems to Be an EPUB. Not a Zine.` }); - } else if(FilesToBeExtract.includes(FolderName + B.ZineData.Path)) { - if(!B.Type) B.Type = 'Zine'; - else if(B.Type == 'EPUB') reject({ BookTypeError: `It Seems to Be a Zine. Not an EPUB.` }); - } else { - reject(`Required Metafile Is Not Contained.`); - } + let RootFileFound = false; + if(B.Type) RootFileFound = FilesToBeExtract.includes(FolderName + PathsToBeChecked[0]); + else if(FilesToBeExtract.includes(FolderName + B.Container.Path)) B.Type = 'EPUB', RootFileFound = true; + else if(FilesToBeExtract.includes(FolderName + B.ZineData.Path )) B.Type = 'Zine', RootFileFound = true; + if(!RootFileFound) return reject(`Required Metafile${ B.Type ? ' (' + (B.Type != 'EPUB' ? B.ZineData.Path : B.Container.Path).split('/').slice(-1)[0] + ')' : '' } Is Not Contained.`); const FileCount = { Particular: 0 }; const FileTypesToBeCounted = { 'Meta XML': 'xml|opf|ncx', @@ -95,7 +95,7 @@ Bibi.x({ O.log(`Extracting Book Data...`, ''); const Promises = []; FilesToBeExtract.forEach(FileName => { - if(FolderName) FileName = FileName.replace(FolderNameRE, ''); + if(FolderNameRE) FileName = FileName.replace(FolderNameRE, ''); const IsBin = O.isBin({ Path: FileName }); Promises.push( BookDataArchive.file(FolderName + FileName).async(IsBin ? 'blob' : 'string').then(FileContent => { diff --git a/__src/bibi/extensions/sanitizer.js b/__src/bibi/extensions/sanitizer.js new file mode 100644 index 00000000..21681e33 --- /dev/null +++ b/__src/bibi/extensions/sanitizer.js @@ -0,0 +1,48 @@ +import DOMPurify from 'dompurify/dist/purify.min.js'; + +Bibi.x({ + + id: 'Sanitizer', + description: 'Content Sanitizer.', + author: 'Satoru Matsushima (@satorumurmur)', + version: '1.0.0' + +})(function() { + + O.sanitizeItemContent = (Item, Opt) => { + if(Item && typeof Item.Content == 'string' && Opt && typeof Opt.As == 'string') { + const Settings = O.sanitizeItemContent.Settings[Opt.As]; + if(Settings) { + (pp => pp ? pp(Item) : true)(Settings.preprocess); + Item.Content = DOMPurify.sanitize(Item.Content, Settings.Options); + (pp => pp ? pp(Item) : true)(Settings.postprocess); + return Item.Content; + } + } + const ErrorMessage = `Sanitizer: Invalid Arguments.`; + I.note(ErrorMessage, 99999999999, 'ERROR'); + O.error(ErrorMessage); + throw new Error(ErrorMessage); + }; + + O.sanitizeItemContent.Settings = { + 'HTML': { + Options: { WHOLE_DOCUMENT: true, ADD_TAGS: ['link'] }, + preprocess: (Item) => { + const ContentHTMLAttributes = (Matched => Matched ? Matched[1] : '')(Item.Content.match(/]+)>/)); + if(!ContentHTMLAttributes) return; + Item.HasContentHTMLAttributes = true; + Item.Content += `bibi:html-attributes`; + }, + postprocess: (Item) => { + if(!Item.HasContentHTMLAttributes) return; + delete Item.HasContentHTMLAttributes; + const HolderRE = /]+)>bibi:html-attributes<\/div>/; + const ContentHTMLAttributes = (Matched => Matched ? Matched[1] : '')(Item.Content.match(HolderRE)); + if(ContentHTMLAttributes) Item.Content = Item.Content.replace(HolderRE, '').replace('', ''); + } + }, + 'SVG' : {} + }; + +}); diff --git a/__src/bibi/extensions/zine.js b/__src/bibi/extensions/zine.js index b05fe75d..cf80ef71 100644 --- a/__src/bibi/extensions/zine.js +++ b/__src/bibi/extensions/zine.js @@ -5,50 +5,69 @@ Bibi.x({ id: 'Zine', description: 'Utilities for BibiZine.', author: 'Satoru MATSUSHIMA (@satorumurmur)', - version: '1.1.1' + version: '1.2.0' })(function() { 'use strict'; - B.ZineData = { Path: 'zine.yaml', Dir: '' }; + this.loadZineData = () => this.setZineMode().then(this.openYAML).then(this.createPackageDocument).then(L.loadPackage.process); - this.loadZineData = () => this.openYAML(B.ZineData.Path).then(this.processZineData).then(L.loadPackage.process); + this.setZineMode = () => { + delete B.Container; + B.Package.Path = B.ZineData.Path; + Object.defineProperty(B, 'ZineData', { get: () => B.Package }); + return Promise.resolve(); + }; - this.openYAML = (Path) => O.file(Path).then(jsyaml.safeLoad); + this.openYAML = () => O.file(B.ZineData).then(ZineFile => jsyaml.safeLoad(ZineFile.Content)); - this.processZineData = (Data) => { - sML.edit(B.Package, B.ZineData); - const Doc = document.createElement('bibi:zine'); + this.createPackageDocument = (YAML) => { + const NS = { + OPF: 'http://www.idpf.org/2007/opf', + DC: 'http://purl.org/dc/elements/1.1/' + }; + const Doc = document.implementation.createDocument(NS.OPF, 'package'); + // Package + const Package = Doc.documentElement; + Package.setAttribute('xmlns', NS.OPF); + Package.setAttribute('xmlns:dc', NS.DC); // Metadata - const Metadata = Doc.appendChild(document.createElement('metadata')); - ['identifier', 'title', 'creator', 'publisher', 'language', 'rendition-layout', 'rendition-orientation', 'rendition-spread'].forEach(Pro => { - if(!Data[Pro]) return; - const Meta = Metadata.appendChild(document.createElement('meta')); + const Metadata = Package.appendChild(document.createElementNS(NS.OPF, 'metadata')); + ['identifier', 'language', 'title', 'creator', 'publisher'].forEach(Pro => { + if(!YAML[Pro]) return; + const Meta = Metadata.appendChild(document.createElementNS(NS.DC, 'dc:' + Pro)); + Meta.textContent = YAML[Pro]; + }); + ['rendition-layout', 'rendition-orientation', 'rendition-spread'].forEach(Pro => { + if(!YAML[Pro]) return; + const Meta = Metadata.appendChild(document.createElementNS(NS.OPF, 'meta')); Meta.setAttribute('property', Pro.replace('-', ':')); - Meta.textContent = Data[Pro]; + Meta.textContent = YAML[Pro]; }); // Manifest & Spine - const Manifest = Doc.appendChild(document.createElement('manifest')); + const Manifest = Package.appendChild(document.createElementNS(NS.OPF, 'manifest')); ['cover-image', 'nav'].forEach(Pro => { - if(!Data[Pro]) return; - const Item = Manifest.appendChild(document.createElement('item')); + if(!YAML[Pro]) return; + const Item = Manifest.appendChild(document.createElementNS(NS.OPF, 'item')); Item.setAttribute('id', Pro + '-item'); Item.setAttribute('properties', Pro); - Item.setAttribute('href', Data[Pro]); + Item.setAttribute('media-type', O.getContentType(YAML[Pro])); + Item.setAttribute('href', YAML[Pro]); }); - const Spine = Doc.appendChild(document.createElement('spine')); - if(Data['page-progression-direction']) Spine.setAttribute('page-progression-direction', Data['page-progression-direction']); - Data['spine'].forEach((ItemrefData, i) => { - if(!ItemrefData) return; - ItemrefData = ItemrefData.trim().replace(/\s+/, ' ').split(' '); + const Spine = Package.appendChild(document.createElementNS(NS.OPF, 'spine')); + if(YAML['page-progression-direction']) Spine.setAttribute('page-progression-direction', YAML['page-progression-direction']); + YAML['spine'].forEach((ItemRefData, i) => { + if(!ItemRefData) return; const ID = 'spine-item-' + (i + 1 + '').padStart(3, 0); - const Item = Manifest.appendChild(document.createElement('item')); + const [Href, PageSpread] = ItemRefData.trim().replace(/\s+/, ' ').split(' '); + const Item = Manifest.appendChild(document.createElementNS(NS.OPF, 'item')); Item.setAttribute('id', ID); - Item.setAttribute('href', ItemrefData[0]); - const Itemref = Spine.appendChild(document.createElement('itemref')); - Itemref.setAttribute('idref', ID); - if(ItemrefData[1]) Itemref.setAttribute('properties', 'page-spread-' + ItemrefData[1]); + Item.setAttribute('media-type', O.getContentType(Href)); + Item.setAttribute('href', Href); + const ItemRef = Spine.appendChild(document.createElementNS(NS.OPF, 'itemref')); + ItemRef.setAttribute('idref', ID); + if(PageSpread) ItemRef.setAttribute('properties', 'page-spread-' + PageSpread); }); return Promise.resolve(Doc); }; diff --git a/__src/bibi/index.html b/__src/bibi/index.html index d3cab2cf..ffef0319 100644 --- a/__src/bibi/index.html +++ b/__src/bibi/index.html @@ -8,7 +8,7 @@ - + This E-Book is Published with Bibi | EPUB Reader on your website. diff --git a/__src/bibi/presets/default.js b/__src/bibi/presets/default.js index 8067569d..5a3fed67 100644 --- a/__src/bibi/presets/default.js +++ b/__src/bibi/presets/default.js @@ -12,18 +12,16 @@ Bibi.preset({ //-- Behavior //---------------------------------------------------------------------------------------------------------------------------------------------- -"reader-view-mode" : "paged", // "paged" or "vertical" or "horizontal" ("paged" is for flipping, "vertical" and "horizontal" are for scrolling) -"fix-reader-view-mode" : "no", // "yes" or "no" or "desktop" or "mobile" +"reader-view-mode" : "paged", // "paged" or "horizontal" or "vertical" ("paged" is for flipping, "horizontal" and "vertical" are for scrolling) +"full-breadth-layout-in-scroll" : "no", // "yes" or "no" or "desktop" or "mobile" +"fix-reader-view-mode" : "no", // "yes" or "no" or "desktop" or "mobile" -"keep-settings" : "yes", // "yes" or "no" or "desktop" or "mobile" -"resume-from-last-position" : "yes", // "yes" or "no" or "desktop" or "mobile" +"keep-settings" : "yes", // "yes" or "no" or "desktop" or "mobile" +"resume-from-last-position" : "yes", // "yes" or "no" or "desktop" or "mobile" -"autostart" : "yes", // "yes" or "no" or "desktop" or "mobile" -"autostart-embedded" : "no", // "yes" or "no" or "desktop" or "mobile" (It takes priority over "autostart" when the book is embedded in a webpage) -"start-embedded-in-new-window" : "mobile", // "yes" or "no" or "desktop" or "mobile" (It is used only when "autostart" (or "autostart-embedded") is NOT enabled) - -"double-spread-for-reflowable" : "yes", // "yes" or "no" or "desktop" or "mobile" -"allow-placeholders" : "yes", // "yes" or "no" or "desktop" or "mobile" +"autostart" : "yes", // "yes" or "no" or "desktop" or "mobile" +"autostart-embedded" : "no", // "yes" or "no" or "desktop" or "mobile" (It takes priority over "autostart" when the book is embedded in a webpage) +"start-embedded-in-new-window" : "mobile", // "yes" or "no" or "desktop" or "mobile" (It is used only when "autostart" (or "autostart-embedded") is NOT enabled) //============================================================================================================================================== @@ -33,15 +31,24 @@ Bibi.preset({ "use-menubar" : "yes", // "yes" or "no" or "desktop" or "mobile" "use-full-height" : "yes", // "yes" or "no" or "desktop" or "mobile". If "use-menubar" is interpreted as "no", "use-full-height" is always treated as "yes". -"use-font-size-changer" : "yes", // "yes" or "no" or "desktop" or "mobile" -"use-loupe" : "desktop", // "yes" or "no" or "desktop" or "mobile" (Note: Loupe buttons will not appear in touch-devices even if it is set "yes" or "mobile".) -"use-nombre" : "yes", // "yes" or "no" or "desktop" or "mobile" - "use-arrows" : "yes", // "yes" or "no" or "desktop" or "mobile" +"flipper-width" : 0.25, // Number of ratio (less than 1) or pixel (1 or greater) + "use-keys" : "yes", // "yes" or "no" or "desktop" or "mobile" -"accept-orthogonal-input" : "no", // "yes" or "no" or "desktop" or "mobile" -"animate-page-flipping" : "no", // "yes" or "no" or "desktop" or "mobile". Animate page flipping on paged view mode, or not. +"use-slider" : "yes", // "yes" or "no" or "desktop" or "mobile" +"flip-pages-during-sliding" : "yes", // "yes" or "no" or "desktop" or "mobile" + +"use-nombre" : "yes", // "yes" or "no" or "desktop" or "mobile" + +"use-font-size-changer" : "yes", // "yes" or "no" or "desktop" or "mobile" +"base-font-size" : "auto", // Number of pixel or "auto" +"font-size-scale-per-step" : 1.25, // Number of scale + +"use-loupe" : "desktop", // "yes" or "no" or "desktop" or "mobile" (Note: Loupe buttons will not appear in touch-devices even if it is set "yes" or "mobile".) +"loupe-max-scale" : 4, // Number of scale (greater than 1) +"loupe-scale-per-step" : 1.6, // Number of scale (greater than 1, less than or equal to "loupe-max-scale") +"zoom-out-for-utilities" : "yes", // "yes" or "no" or "desktop" or "mobile" "use-history" : "yes", // "yes" or "no" or "desktop" or "mobile" "max-history" : 19, // Number (0-19). If larger than 19, treated as 19. If 0, "use-history" is treated as "no". @@ -49,34 +56,33 @@ Bibi.preset({ "use-bookmarks" : "yes", // "yes" or "no" or "desktop" or "mobile" "max-bookmarks" : 3, // Number (0-9). If larger than 9, treated as 9. If 0, "use-bookmarks" is treated as "no" (but old data is kept in localStorage). -"use-slider" : "yes", -"slider-mode" : "auto", // "edgebar" or "bookmap" or "auto" -"zoom-out-for-utilities" : "yes", // "yes" or "no" or "desktop" or "mobile" - "orientation-border-ratio" : 1 * 2 / 1.5, // Number (Width per Height) -"base-font-size" : "auto", // Number of pixel or "auto" -"font-size-scale-per-step" : 1.25, // Number of scale -"loupe-max-scale" : 4, // Number of scale (minimum: 2) - "ui-font-family" : "", // CSS font-family value as "'Helvetica', sans-serif" or "" -"flipper-width" : 0.3, // Number of ratio (lower than 1) or pixel (1 or higher) - -"item-padding-left" : 28, // Number of pixel (It is used only if the book is reflowable.) -"item-padding-right" : 28, // Number of pixel (It is used only if the book is reflowable.) -"item-padding-top" : 40, // Number of pixel (It is used only if the book is reflowable.) -"item-padding-bottom" : 20, // Number of pixel (It is used only if the book is reflowable.) +"item-padding-left" : 24, // Number of pixel (It affects only for reflowable books.) +"item-padding-right" : 24, // Number of pixel (It affects only for reflowable books.) +"item-padding-top" : 48, // Number of pixel (It affects only for reflowable books.) +"item-padding-bottom" : 24, // Number of pixel (It affects only for reflowable books.) -"spread-gap" : 2, // Number of pixel +"spread-gap" : 48, // Number of pixel (It affects only in paged view mode.) "spread-margin" : 0, // Number of pixel "fix-nav-ttb" : "no", // "yes" or "no" or "desktop" or "mobile" -"spread-border-radius" : "", // CSS border-radius value or "" -"spread-box-shadow" : "", // CSS box-shadow value or "" - -"book-background" : "", // CSS background value or "" +"content-draggable" : [true, true], // [, ] +"orthogonal-arrow-keys" : ["move", "switch"], // [, ] +"orthogonal-edges" : ["utilities", "utilities"], // [, ] +"orthogonal-touch-moves" : ["move", "switch"], // [, ] +"orthogonal-wheelings" : ["move", "across"], // [, ] +// ^ Each item of the arrays corresponds to the view mode: +// * the first is for the "paged" view mode, and +// * the second is for the "horizontal"/"vertical" scroll view modes. +// ^ Types of the values: +// * : true or false +// * : "" (ignore) or "utilities" or "move" +// * : "" (ignore) or "utilities" or "move" or "switch" +// * : "" (ignore) or "utilities" or "move" or "switch" or "across" //============================================================================================================================================== @@ -113,17 +119,35 @@ Bibi.preset({ "accept-blob-converted-data" : true, // true or false (If true, Bibi accepts BLOB object converted from a EPUB File. If you are interested in it, please contact the author) "accept-base64-encoded-data" : true, // true or false (If true, Bibi accepts Base64 string encoded from a EPUB File. If you are interested in it, please contact the author) +"pagination-method" : "auto", // "auto" or "x". (It affects only for vertical-text reflowable books. More info is <04> at the bottom of this preset file.) +"allow-placeholders" : true, // true or false. (true is highly recommended.) "prioritise-fallbacks" : false, // true or false (If true, Bibi uses at the end of the fallback-chain. -"trustworthy-origins" : [], + +//============================================================================================================================================== +//-- DANGER ZONE +//---------------------------------------------------------------------------------------------------------------------------------------------- +// If you publish Bibi online, +// * keep these options as default, or/and +// * keep your Bibi and website not to open files which you can not guarantee its security. +//---------------------------------------------------------------------------------------------------------------------------------------------- + +/* !!!! BE CAREFUL !!!! */ "allow-scripts-in-content" : false, // true or false (false is recommended). +// If you change its value `true`, Bibi does not remove scripts natively-included in EPUB. +// It makes Bibi to be able to open EPUBs including useful scripts. +// But on the other hand, it may also allow XSS of malicious EPUB in some cases. + +/* !!!! BE CAREFUL !!!! */ "trustworthy-origins" : [], // origins you trust other than where this Bibi is installed. (blank is recommended). +// If you add origins to it, Bibi is made to open not only EPUBs in the same origin as Bibi itself is installed but also EPUBs in remote origins. +// It is useful for some cases like that you want to set directory on the other storaging server as "bookshelf". +// But note that not to set an origin where someone else also can publish files. +// If you do so, someone else can publish one's EPUB as it is on your website, and it may also allow XSS of malicious EPUB in some cases. /* //============================================================================================================================================== -//-- Additional Info. -//---------------------------------------------------------------------------------------------------------------------------------------------- ## <01> You can use a path begins with "http(s)://" for "bookshelf" option in cases of the below: @@ -162,7 +186,18 @@ Bibi.preset({ * Extension of the file is required even if "extract-if-necessary" is "" (or includes "*"). +## <04> Setting "x" for "pagination-method" option + + It affects only for reflowable vertical-text books. + If "x" is set for "pagination-method", Bibi tries to use an experimental layout method on modern web-browsers. + It realizes more prettier layout for simple books like novels. + But sometime causes bad result for some books with figures or floating objects. + + +//============================================================================================================================================== + + */ -"bibi": "EPUB Reader on your website." }); \ No newline at end of file +"bibi": "EPUB Reader on your website." }); diff --git a/__src/bibi/resources/scripts/bibi.book.scss b/__src/bibi/resources/scripts/bibi.book.scss index 8c75de49..3cd31776 100644 --- a/__src/bibi/resources/scripts/bibi.book.scss +++ b/__src/bibi/resources/scripts/bibi.book.scss @@ -18,13 +18,15 @@ html { overflow: hidden; }*/ -img.bibi-spine-item-image { - display: block; - margin: 0; - border: none 0; - padding: 0; - width: auto; - height: auto; +html { + img.bibi-spine-item-image { + display: block; + margin: 0; + border: none 0; + padding: 0; + width: auto; + height: auto; + } } html.bibi-columned { @@ -33,6 +35,13 @@ html.bibi-columned { } } +html.bibi-flick-active { + &.bibi-flick-hot { user-select: none; } + img { + pointer-events: none; + } +} + html.bibi-with-gutters { > head { display: block; diff --git a/__src/bibi/resources/scripts/bibi.heart.js b/__src/bibi/resources/scripts/bibi.heart.js index 06cf50d3..dac5ee5e 100644 --- a/__src/bibi/resources/scripts/bibi.heart.js +++ b/__src/bibi/resources/scripts/bibi.heart.js @@ -7,7 +7,194 @@ -export const Bibi = { 'version': '____Bibi-Version____', 'href': 'https://bibi.epub.link', TimeOrigin: Date.now() }; +export const Bibi = { 'version': '____Bibi-Version____', 'href': 'https://bibi.epub.link', Status: '', TimeOrigin: Date.now() }; + + +Bibi.SettingTypes = { + 'boolean': [ + 'allow-placeholders', + 'prioritise-fallbacks' + ], + 'yes-no': [ + 'autostart', + 'autostart-embedded', + 'fix-nav-ttb', + 'fix-reader-view-mode', + 'flip-pages-during-sliding', + 'full-breadth-layout-in-scroll', + 'start-embedded-in-new-window', + 'use-arrows', + 'use-bookmarks', + 'use-font-size-changer', + 'use-full-height', + 'use-history', + 'use-keys', + 'use-loupe', + 'use-menubar', + 'use-nombre', + 'use-slider', + 'zoom-out-for-utilities' + ], + 'string': [ + 'book', + 'default-page-progression-direction', + 'pagination-method', + 'reader-view-mode' + ], + 'integer': [ + 'item-padding-bottom', + 'item-padding-left', + 'item-padding-right', + 'item-padding-top', + 'spread-gap', + 'spread-margin' + ], + 'number': [ + 'base-font-size', + 'flipper-width', + 'font-size-scale-per-step', + 'loupe-max-scale', + 'loupe-scale-per-step', + 'orientation-border-ratio' + ], + 'array': [ + 'content-draggable', + 'orthogonal-edges', + 'orthogonal-arrow-keys', + 'orthogonal-touch-moves', + 'orthogonal-wheelings' + ] +}; + +Bibi.SettingTypes_PresetOnly = { + 'boolean': [ + 'accept-base64-encoded-data', + 'accept-blob-converted-data', + 'allow-scripts-in-content', + 'remove-bibi-website-link' + ], + 'yes-no': [ + 'accept-local-file', + 'keep-settings', + 'resume-from-last-position' + ], + 'string': [ + 'bookshelf' + ], + 'integer': [ + 'max-bookmarks', + 'max-history' + ], + 'number': [ + ], + 'array': [ + 'extensions', + 'extract-if-necessary', + 'trustworthy-origins' + ] +}; + +Bibi.SettingTypes_UserOnly = { + 'boolean': [ + 'debug', + 'wait', + 'zine' + ], + 'yes-no': [ + ], + 'string': [ + 'epubcfi', + 'bibidi' + ], + 'integer': [ + 'log', + 'nav', + 'parent-bibi-index' + ], + 'number': [ + 'iipp', + 'sipp' + ], + 'array': [ + ] +}; + +Bibi.verifySettingValue = (SettingType, _P, _V, Fill) => Bibi.verifySettingValue[SettingType](_P, _V, Fill); (Verifiers => { for(const SettingType in Verifiers) Bibi.verifySettingValue[SettingType] = Verifiers[SettingType]; })({ + 'boolean': (_P, _V, Fill) => { + if(typeof _V == 'boolean') return _V; + if(_V === 'true' || _V === '1' || _V === 1) return true; + if(_V === 'false' || _V === '0' || _V === 0) return false; + if(Fill) return false; + }, + 'yes-no': (_P, _V, Fill) => { + if(/^(yes|no|mobile|desktop)$/.test(_V)) return _V; + if(_V === 'true' || _V === '1' || _V === 1) return 'yes'; + if(_V === 'false' || _V === '0' || _V === 0) return 'no'; + if(Fill) return 'no'; + }, + 'string': (_P, _V, Fill) => { + if(typeof _V == 'string') { + switch(_P) { + case 'bibidi' : return R.getBibiToDestination(_V) || undefined; + case 'book' : return decodeURIComponent(_V).trim() || undefined; + case 'default-page-progression-direction' : return /^(ltr|rtl)$/.test(_V) ? _V : 'ltr'; + case 'pagination-method' : return _V == 'x' ? _V : 'auto'; + case 'reader-view-mode' : return /^(paged|horizontal|vertical)$/.test(_V) ? _V : 'paged'; + } + return _V; + } + if(Fill) return ''; + }, + 'integer': (_P, _V, Fill) => { + if(typeof (_V *= 1) == 'number' && isFinite(_V)) { + _V = Math.max(Math.round(_V), 0); + switch(_P) { + case 'log' : return Math.min(_V, 9); + case 'max-bookmarks' : return Math.min(_V, 9); + case 'max-history' : return Math.min(_V, 19); + } + return _V; + } + if(Fill) return 0; + }, + 'number': (_P, _V, Fill) => { + if(typeof (_V *= 1) == 'number' && isFinite(_V) && _V >= 0) return _V; + if(Fill) return 0; + }, + 'array': (_P, _V, Fill) => { + if(Array.isArray(_V)) { + switch(_P) { + case 'content-draggable' : _V.length = 2; for(let i = 0; i < 2; i++) _V[i] = _V[i] === false || _V[i] === 'false' || _V[i] === '0' || _V[i] === 0 ? false : true; return _V; + case 'extensions' : return _V.filter(_I => typeof _I['src'] == 'string' && (_I['src'] = _I['src'].trim())); + case 'extract-if-necessary' : return (_V = _V.map(_I => typeof _I == 'string' ? _I.trim().toLowerCase() : '')).includes('*') ? ['*'] : _V.filter(_I => /^(\.[\w\d]+)*$/.test(_I)); + case 'orthogonal-arrow-keys' : + case 'orthogonal-edges' : + case 'orthogonal-touch-moves' : + case 'orthogonal-wheelings' : _V.length = 2; for(let i = 0; i < 2; i++) _V[i] = typeof _V[i] == 'string' ? _V[i] : ''; return _V; + case 'trustworthy-origins' : return _V.reduce((_VN, _I) => typeof _I == 'string' && /^https?:\/\/[^\/]+$/.test(_I = _I.trim().replace(/\/$/, '')) && !_VN.includes(_I) ? _VN.push(_I) && _VN : false, []); + } + return _V.filter(_I => typeof _I != 'function'); + } + if(Fill) return []; + } +}); + +Bibi.applyFilteredSettingsTo = (To, From, ListOfSettingTypes, Fill) => { + ListOfSettingTypes.forEach(STs => { + for(const ST in STs) { + STs[ST].forEach(_P => { + const VSV = Bibi.verifySettingValue[ST](_P, From[_P]); + if(Fill) { + To[_P] = Bibi.verifySettingValue[ST](_P, To[_P]); + if(typeof VSV != 'undefined' || typeof To[_P] == 'undefined') To[_P] = Bibi.verifySettingValue[ST](_P, From[_P], true); + } else if(From.hasOwnProperty(_P)) { + if(typeof VSV != 'undefined') To[_P] = VSV; + } + }); + } + }); + return To; +}; @@ -20,7 +207,10 @@ export const Bibi = { 'version': '____Bibi-Version____', 'href': 'https://bibi.e //---------------------------------------------------------------------------------------------------------------------------------------------- +Bibi.at1st = () => Bibi.at1st.List.forEach(fn => typeof fn == 'function' ? fn() : true), Bibi.at1st.List = []; + Bibi.hello = () => new Promise(resolve => { + Bibi.at1st(); O.log.initialize(); O.log(`Hello!`, ''); O.log(`[ja] ${ Bibi['href'] }`); @@ -58,7 +248,7 @@ Bibi.initialize = () => { } { // Environments O.HTML.classList.add(...sML.Environments, 'Bibi', 'welcome'); - if(O.TouchOS = (sML.OS.iOS || sML.OS.Android)) { // Touch Device + if(O.TouchOS = (sML.OS.iOS || sML.OS.Android) ? true : false) { // Touch Device O.HTML.classList.add('touch'); if(sML.OS.iOS) { O.Head.appendChild(sML.create('meta', { name: 'apple-mobile-web-app-capable', content: 'yes' })); @@ -68,7 +258,7 @@ Bibi.initialize = () => { if(Bibi.Dev) O.HTML.classList.add('dev'); if(Bibi.Debug) O.HTML.classList.add('debug'); O.HTML.classList.add('default-lang-' + (O.Language = (NLs => { // Language - if(navigator.languages instanceof Array) NLs = NLs.concat(navigator.languages); + if(Array.isArray(navigator.languages)) NLs = NLs.concat(navigator.languages); if(navigator.language && navigator.language != NLs[0]) NLs.unshift(navigator.language); for(let l = NLs.length, i = 0; i < l; i++) { const Lan = NLs[i].split ? NLs[i].split('-')[0] : ''; @@ -77,29 +267,38 @@ Bibi.initialize = () => { } return 'en'; })([]))); } - E.initialize(); O.Biscuits.initialize(); - R.initialize(); - I.initialize(); - P.initialize(); - H.initialize(); - U.initialize(); - S.initialize( - () => O.Embedded = (() => { // Window Embedded or Not + { // Modules + E.initialize(); O.Biscuits.initialize(); + R.initialize(); + I.initialize(); + B.initialize(); + P.initialize(); + U.initialize(); + S.initialize(); + } + { // Embedding, Window, Fullscreen + O.Embedded = (() => { // Window Embedded or Not if(window.parent == window) { O.HTML.classList.add('window-direct' ); return 0; } // false else { O.HTML.classList.add('window-embedded'); try { if(location.host == parent.location.host || parent.location.href) return 1; } catch(Err) {} return -1; } // true (1:Reachable or -1:Unreachable) - })(), - () => O.FullscreenTarget = (() => { // Fullscreen Target + })(); + O.ParentBibi = O.Embedded == 1 && typeof S['parent-bibi-index'] == 'number' ? window.parent['bibi:jo'].Bibis[S['parent-bibi-index']] || null : null; + O.ParentOrigin = O.ParentBibi ? window.parent.location.origin : ''; + O.FullscreenTarget = (() => { // Fullscreen Target const FsT = (() => { - if(O.Embedded == 0) { sML.Fullscreen.polyfill(window ); return O.HTML; } - else if(O.Embedded == 1) { sML.Fullscreen.polyfill(window.parent); try { return window.parent.document.getElementById(S['parent-holder-id']).Bibi.Frame; } catch(Err) {} } + if(!O.Embedded) { sML.Fullscreen.polyfill(window ); return O.HTML; } + if(O.ParentBibi) { sML.Fullscreen.polyfill(window.parent); return O.ParentBibi.Frame; } })() || null; if(FsT && FsT.ownerDocument.fullscreenEnabled) { O.HTML.classList.add('fullscreen-enabled' ); return FsT; } else { O.HTML.classList.add('fullscreen-disabled'); return null; } - })() - ); + })(); + if(O.ParentBibi) { + O.ParentBibi.Window = window, O.ParentBibi.Document = document, O.ParentBibi.HTML = O.HTML, O.ParentBibi.Body = O.Body; + ['bibi:initialized', 'bibi:readied', 'bibi:prepared', 'bibi:opened'].forEach(EN => E.add(EN, Det => O.ParentBibi.dispatch(EN, Det))); + } + } if(sML.UA.Trident && !(sML.UA.Trident[0] >= 7)) { // Say Bye-bye I.note(`Your Browser Is Not Compatible`, 99999999999, 'ErrorOccured'); - return O.error(I.Veil.byebye({ + O.error(I.Veil.byebye({ 'en': `Sorry.... Your Browser Is Not Compatible.`, 'ja': `大変申し訳ありません。 お使いのブラウザでは、動作しません。` })); @@ -131,7 +330,7 @@ Bibi.initialize = () => { } O.HTML.classList.toggle('book-full-height', S['use-full-height']); O.HTML.classList.remove('welcome'); - E.dispatch('bibi:initialized'); + E.dispatch('bibi:initialized', Bibi.Status = Bibi.Initialized = 'Initialized'); //return PromiseTryingRangeRequest; }; @@ -139,6 +338,7 @@ Bibi.initialize = () => { Bibi.loadExtensions = () => { return new Promise((resolve, reject) => { const AdditionalExtensions = []; + if(!S['allow-scripts-in-content']) AdditionalExtensions.push('sanitizer.js'); let ReadyForExtraction = false, ReadyForBibiZine = false; if(S['book']) { if(O.isToBeExtractedIfNecessary(S['book'])) ReadyForExtraction = true; @@ -173,15 +373,15 @@ Bibi.ready = () => new Promise(resolve => { O.HTML.classList.add('ready'); O.ReadiedURL = location.href; E.add('bibi:readied', resolve); - E.dispatch('bibi:readied'); + setTimeout(() => E.dispatch('bibi:readied', Bibi.Status = Bibi.Readied = 'Readied'), (O.TouchOS && !O.Embedded) ? 1234 : 0); }).then(() => { O.HTML.classList.remove('ready'); }); Bibi.getBookData = () => + B.Data ? Promise.resolve({ BookData: B.Data, BookDataType: B.DataType }) : S['book'] ? Promise.resolve({ BookData: S['book'] }) : - S.BookDataElement ? Promise.resolve({ BookData: S.BookDataElement.innerText.trim(), BookDataType: S.BookDataElement.getAttribute('data-bibi-book-mimetype') }) : S['accept-local-file'] ? new Promise(resolve => { Bibi.getBookData.resolve = (Par) => { resolve(Par), O.HTML.classList.remove('waiting-file'); }; O.HTML.classList.add('waiting-file'); }) : Promise.reject (`Tell me EPUB name via ${ O.Embedded ? 'embedding tag' : 'URI' }.`); @@ -224,10 +424,7 @@ Bibi.loadBook = (BookDataParam) => Promise.resolve().then(() => { return L.createCover(); // ← loading is async }).then(() => { // Load Navigation - if(!B.NavItem) { - O.log(`No Navigation.`) - return resolve(); - } + if(!B.NavItem) return O.log(`No Navigation.`) O.log(`Loading Navigation...`, ''); return L.loadNavigation().then(PNav => { O.log(`${ B.NavItem.NavType }: %O`, B.NavItem); @@ -236,7 +433,7 @@ Bibi.loadBook = (BookDataParam) => Promise.resolve().then(() => { }); }).then(() => { // Announce "Prepared" (and Wait, sometime) - E.dispatch('bibi:prepared'); + E.dispatch('bibi:prepared', Bibi.Status = Bibi.Prepared = 'Prepared'); if(!S['autostart'] && !L.Played) return L.wait(); }).then(() => { // Background Preparing @@ -252,13 +449,30 @@ Bibi.loadBook = (BookDataParam) => Promise.resolve().then(() => { addResetter: () => { window .addEventListener('resize', LayoutOption.resetter); }, removeResetter: () => { window.removeEventListener('resize', LayoutOption.resetter); } }; - if(typeof S['to'] == 'object') { - LayoutOption.TargetSpreadIndex = - (typeof S['to'].SpreadIndex == 'number') ? S['to'].SpreadIndex : - (typeof S['to'].ItemIndexInSpine == 'number') ? B.Package.Spine.Items[S['to'].ItemIndexInSpine].Spread.Index : - ( S['to']['SI-PPiS'] ) ? S['to']['SI-PPiS'].split('-')[0] : - 0; - LayoutOption.Destination = S['to']; + if(typeof R.StartOn == 'object') { + const Item = typeof R.StartOn.Item == 'object' ? R.StartOn.Item : (() => { + if(typeof R.StartOn.IIPP == 'number') { + let II = Math.floor(R.StartOn.IIPP); + if(II >= R.Items.length) { + const PP = R.StartOn.IIPP - II; + II = R.Items.length - 1; + R.StartOn.IIPP = II + PP; + } + return R.Items[II]; + } + if(typeof R.StartOn.ItemIndex == 'number') { + if(R.StartOn.ItemIndex >= R.Items.length) R.StartOn.ItemIndex = R.Items.length - 1; + return R.Items[R.StartOn.ItemIndex]; + } + if(typeof R.StartOn.ItemIndexInSpine == 'number') { + if(R.StartOn.ItemIndexInSpine >= B.Package.Spine.Items.length) R.StartOn.ItemIndexInSpine = B.Package.Spine.Items.length - 1; + const Item = B.Package.Spine.Items[R.StartOn.ItemIndexInSpine]; + if(Item.Spread) return Item; + R.StartOn = { ItemIndex: 0 }; + } + })(); + LayoutOption.TargetSpreadIndex = Item && Item.Spread ? Item.Spread.Index : 0; + LayoutOption.Destination = R.StartOn; } LayoutOption.addResetter(); let LoadedItems = 0; @@ -281,8 +495,7 @@ Bibi.bindBook = (LayoutOption) => { } return R.layOut(LayoutOption).then(() => { LayoutOption.removeResetter(); - R.IntersectingPages = [R.Spreads[LayoutOption.TargetSpreadIndex].Pages[0]]; - Bibi.Eyes.wearGlasses(); + E.dispatch('bibi:laid-out-for-the-first-time', LayoutOption); return LayoutOption }); }; @@ -296,31 +509,28 @@ Bibi.openBook = (LayoutOption) => new Promise(resolve => { document.body.click(); // To responce for user scrolling/keypressing immediately I.note(''); O.log(`Enjoy Readings!`, ''); - E.dispatch('bibi:opened'); + E.dispatch('bibi:opened', Bibi.Status = Bibi.Opened = 'Opened'); + E.dispatch('bibi:scrolled'); resolve(); }).then(() => { - E.bind(['bibi:changed-intersection', 'bibi:scrolled'], R.updateCurrent); - R.updateCurrent(); - const LandingPage = R.hatchPage(LayoutOption.Destination); + const LandingPage = R.hatchPage(LayoutOption.Destination) || R.Pages[0]; if(!I.History.List.length) { - I.History.List = [{ UI: Bibi, Spread: LandingPage.Spread, PageProgressInSpread: LandingPage.IndexInSpread / LandingPage.Spread.Pages.length }]; + I.History.List = [{ UI: Bibi, Item: LandingPage.Item, PageProgressInItem: LandingPage.IndexInItem / LandingPage.Item.Pages.length }]; I.History.update(); } if(S['allow-placeholders']) { R.turnSpreads({ Origin: LandingPage.Spread }); - setTimeout(() => { R.turnSpreads(); }, 123); - E.add('bibi:scrolled', () => setTimeout(() => R.turnSpreads(), 123)); - E.add('bibi:changed-intersection', () => !I.Slider.Touching ? R.turnSpreads() : false); + setTimeout(() => R.turnSpreads(), 123); + E.add(['bibi:scrolled', 'bibi:changed-intersection'], () => R.turnSpreads()); } if(S['resume-from-last-position']) E.add('bibi:changed-intersection', () => { try { - const CurrentPage = R.Current.List[0].Page; - O.Biscuits.memorize('Book', { 'Position': { 'SI-PPiS': CurrentPage.Spread.Index + '-' + (CurrentPage.IndexInSpread / CurrentPage.Spread.Pages.length) } }); + const CurrentPage = I.PageObserver.Current.List[0].Page; + O.Biscuits.memorize('Book', { Position: { IIPP: CurrentPage.Item.Index + CurrentPage.IndexInItem / CurrentPage.Item.Pages.length } }); } catch(Err) {} }); E.add('bibi:commands:move-by', R.moveBy); E.add('bibi:commands:scroll-by', R.scrollBy); E.add('bibi:commands:focus-on', R.focusOn); E.add('bibi:commands:change-view', R.changeView); - window.addEventListener('message', M.gate, false); (Bibi.Dev && !/:61671/.test(location.href)) ? Bibi.createDevNote() : delete Bibi.createDevNote; /* alert((Alert => { @@ -361,48 +571,6 @@ Bibi.createDevNote = () => { }; -Bibi.Eyes = { - watch: (Ent) => { - const Page = Ent.target; - let IntersectionChanging = false; - //const IntersectionRatio = Math.round(Ent.intersectionRatio * 10000) / 100; - if(Ent.isIntersecting) { - if(!R.IntersectingPages.includes(Page)) { - IntersectionChanging = true; - R.IntersectingPages.push(Page); - } - } else { - if( R.IntersectingPages.includes(Page)) { - IntersectionChanging = true; - R.IntersectingPages = R.IntersectingPages.filter(IntersectingPage => IntersectingPage != Page); - } - } - if(IntersectionChanging) { - if(R.IntersectingPages.length) R.IntersectingPages.sort((A, B) => A.Index - B.Index); - E.dispatch('bibi:changes-intersection', R.IntersectingPages); - clearTimeout(Bibi.Eyes.Timer_IntersectionChange); - Bibi.Eyes.Timer_IntersectionChange = setTimeout(() => { - E.dispatch('bibi:changed-intersection', R.IntersectingPages); - }, 9); - } - }, - wearGlasses: () => { - Bibi.Glasses = new IntersectionObserver(Ents => Ents.forEach(Bibi.Eyes.watch), { - root: R.Main, - rootMargin: '0px', - threshold: [0, 0.5, 1] - }); - Bibi.Eyes.observe = (Page) => Bibi.Glasses.observe(Page); - Bibi.Eyes.unobserve = (Page) => Bibi.Glasses.unobserve(Page); - Bibi.Eyes.PagesToBeObserved.forEach(PageToBeObserved => Bibi.Glasses.observe(PageToBeObserved)); - delete Bibi.Eyes.PagesToBeObserved; - }, - PagesToBeObserved: [], - observe: (Page) => !Bibi.Eyes.PagesToBeObserved.includes(Page) ? Bibi.Eyes.PagesToBeObserved.push(Page) : Bibi.Eyes.PagesToBeObserved.length, - unobserve: (Page) => (Bibi.Eyes.PagesToBeObserved = Bibi.Eyes.PagesToBeObserved.filter(PageToBeObserved => PageToBeObserved != Page)).length -}; - - Bibi.createElement = (...Args) => { const TagName = Args[0]; if(!Bibi.Elements) Bibi.Elements = {}; @@ -430,14 +598,29 @@ export const B = { // Bibi.Book Path: '', PathDelimiter: ' > ', Container: { Path: 'META-INF/container.xml' }, - Package: { - Manifest: { Items: {} }, - Spine: { Items: [] } - }, + Package: { Metadata: {}, Manifest: { Items: {} }, Spine: { Items: [] } }, FileDigit: 0 }; +B.initialize = () => { + const BookDataElement = document.getElementById('bibi-book-data'); + if(BookDataElement) { + const BookData = BookDataElement.innerText.trim(); + if(BookData) { + const BookDataType = BookDataElement.getAttribute('data-bibi-book-mimetype'); + if(/^application\/(epub\+zip|zip|x-zip(-compressed)?)$/i.test(BookDataType)) B.Data = BookData, B.DataType = BookDataType; + } + if(!B.Data) { + BookDataElement.innerHTML = ''; + BookDataElement.parentNode.removeChild(BookDataElement); + } + } + B.Type = !U['book'] ? '' : U['zine'] ? 'Zine' : 'EPUB'; + if(B.Type != 'EPUB') B.ZineData = { Path: 'zine.yaml' }; +}; + + //============================================================================================================================================== @@ -491,37 +674,36 @@ L.initializeBook = (Par) => new Promise((resolve, reject) => { if(!Par || !Par.BookData) return reject(`Book Data Is Undefined.`); let BookData = Par.BookData; const BookDataFormat = - typeof BookData == 'string' ? (/^https?:\/\//.test(BookData) || /^ms-local-stream:\/\//.test(BookData) ? 'URI' : 'Base64') : + typeof BookData == 'string' ? (/^data:/.test(BookData) ? 'Base64' : 'URI') : typeof BookData == 'object' ? (BookData instanceof File ? 'File' : BookData instanceof Blob ? 'BLOB' : '') : ''; if(!BookDataFormat) return reject(`Book Data Is Unknown.`); if(BookDataFormat == 'URI') { // Online B.Path = BookData; - if(!S['trustworthy-origins'].includes(new URL(B.Path).origin)) return reject(`The Origin of the Path of the Book Is Not Allowed.`); let RootFile; switch(B.Type) { case 'EPUB': RootFile = B.Container; break; // Online EPUB - case 'Zine': RootFile = B.ZineData ; break; // Online Zine + case 'Zine': RootFile = B.ZineData; break; // Online Zine } const initialize_as = (FileOrFolder) => ({ Promised: ( FileOrFolder == 'Folder' ? O.download(RootFile).then(() => (B.PathDelimiter = '/') && '') : O.RangeLoader ? O.extract(RootFile).then(() => 'on-the-fly') : - O.loadZippedBookData(B.Path) .then(() => 'at-once') + O.loadZippedBookData( B.Path ).then(() => 'at-once') ).then(ExtractionPolicy => { B.ExtractionPolicy = ExtractionPolicy; //O.log(`Succeed to Open as ${ B.Type } ${ FileOrFolder }.`); resolve(`${ B.Type} ${ FileOrFolder }`); }).catch(ErrorDetail => { if(ErrorDetail.BookTypeError) return reject(ErrorDetail.BookTypeError); - O.log(`Failed to Open as ${ B.Type } ${ FileOrFolder }.` + '\n' + `... ${ ErrorDetail }`); + O.log(`Failed to Open as ${ B.Type } ${ FileOrFolder }: ${ ErrorDetail }`); return Promise.reject(); }), or: function(fun) { return this.Promised.catch(ErrorDetail => fun(ErrorDetail)); }, or_reject: function(Msg) { return this.or(() => reject(Msg)); } }); O.isToBeExtractedIfNecessary(B.Path) - ? initialize_as('File').or(() => initialize_as('Folder').or_reject(`Failed to Open Both as ${ B.Type } File and ${ B.Type } Folder.` )) + ? initialize_as('File').or(() => initialize_as('Folder').or_reject(`(Both as ${ B.Type } File/Folder)`)) : initialize_as('Folder').or_reject(`Changing "extract-if-necessary" May Be Required to Open This Book as ${ B.Type } File.`); } else { let FileOrData; @@ -582,34 +764,30 @@ L.initializeBook = (Par) => new Promise((resolve, reject) => { return InitializedAs; })).catch(Log => { //if(S['accept-local-file']) O.HTML.classList.add('waiting-file'); - const Message = `Failed to Open the Book.`; - O.error(Message + '\n* ' + Log); - return Promise.reject(Message); + O.error(`Failed to Open the Book:` + '\n' + Log); }); L.loadContainer = () => O.openDocument(B.Container).then(L.loadContainer.process).then(() => E.dispatch('bibi:loaded-container')); - L.loadContainer.process = (Doc) => { - B.Package.Path = Doc.getElementsByTagName('rootfile')[0].getAttribute('full-path'); - B.Package.Dir = B.Package.Path.replace(/\/?[^\/]+$/, ''); - }; + L.loadContainer.process = (Doc) => B.Package.Path = Doc.getElementsByTagName('rootfile')[0].getAttribute('full-path'); L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then(() => E.dispatch('bibi:loaded-package-document')); L.loadPackage.process = (Doc) => { // This is Used also from the Zine Extention. - const _Metadata = Doc.getElementsByTagName('metadata')[0], Metadata = B.Package.Metadata = {};// = { 'identifier': [], 'title': [], 'creator': [], 'publisher': [], 'language': [] }; + const _Package = Doc.getElementsByTagName('package' )[0]; + const _Metadata = Doc.getElementsByTagName('metadata')[0], Metadata = B.Package.Metadata; const _Manifest = Doc.getElementsByTagName('manifest')[0], Manifest = B.Package.Manifest; const _Spine = Doc.getElementsByTagName('spine' )[0], Spine = B.Package.Spine; const _ItemPaths = {}; // ================================================================================ // METADATA // -------------------------------------------------------------------------------- - const DCNS = _Metadata.getAttribute('xmlns:dc'); + const DCNS = _Package.getAttribute('xmlns:dc') || _Metadata.getAttribute('xmlns:dc'); ['identifier', 'language', 'title', 'creator', 'publisher'].forEach(Pro => sML.forEach(Doc.getElementsByTagNameNS(DCNS, Pro))(_Meta => (Metadata[Pro] ? Metadata[Pro] : Metadata[Pro] = []).push(_Meta.textContent.trim()))); sML.forEach(_Metadata.getElementsByTagName('meta'))(_Meta => { - if(_Meta.getAttribute('refines')) return; // It's BAD and Wanted to Be FIXed. + if(_Meta.getAttribute('refines')) return; // Should be solved. let Property = _Meta.getAttribute('property'); if(Property) { if(/^dcterms:/.test(Property)) { @@ -641,6 +819,7 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then // ================================================================================ // MANIFEST // -------------------------------------------------------------------------------- + const PackageDir = B.Package.Path.replace(/\/?[^\/]+$/, ''); sML.forEach(_Manifest.getElementsByTagName('item'))(_Item => { let Item = { 'id': _Item.getAttribute('id'), @@ -648,7 +827,7 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then 'media-type': _Item.getAttribute('media-type') }; if(!Item['id'] || !Item['href'] || (!Item['media-type'] && B.Type == 'EPUB')) return false; - Item.Path = O.getPath(B.Package.Dir, Item['href']); + Item.Path = O.getPath(PackageDir, Item['href']); if(Manifest.Items[Item.Path]) Item = sML.edit(Manifest.Items[Item.Path], Item); if(!Item.Content) Item.Content = ''; let Properties = _Item.getAttribute('properties'); @@ -662,6 +841,12 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then Manifest.Items[Item.Path] = Item; _ItemPaths[Item['id']] = Item.Path; }); + [B.Container, B.Package].forEach(MetaItem => { + if(!MetaItem) return; + const MetaItemPath = MetaItem.Path; + (Item => ['Path', 'Content', 'DataType'].forEach(Pro => { Item[Pro] = undefined; delete Item[Pro]; }))(Manifest.Items[MetaItemPath]); + delete Manifest.Items[MetaItemPath]; + }); // ================================================================================ // SPINE // -------------------------------------------------------------------------------- @@ -678,7 +863,7 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then let SpreadBefore, SpreadAfter; if(B.PPD == 'rtl') SpreadBefore = 'right', SpreadAfter = 'left'; else SpreadBefore = 'left', SpreadAfter = 'right'; - Spine.SpreadsDocumentFragment = document.createDocumentFragment(); + const SpreadsDocumentFragment = document.createDocumentFragment(); sML.forEach(_Spine.getElementsByTagName('itemref'))(_ItemRef => { const ItemRef = { 'idref': _ItemRef.getAttribute('idref') @@ -726,9 +911,9 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then Item.Index = R.Items.length; R.Items.push(Item); let Spread = null; - if(ItemRef['rendition:page-spread'] == SpreadAfter && Item.Index > 0) { + if(ItemRef['rendition:layout'] == 'pre-paginated' && ItemRef['rendition:page-spread'] == SpreadAfter && Item.Index > 0) { const PreviousItem = R.Items[Item.Index - 1]; - if(PreviousItem.Ref['rendition:page-spread'] == SpreadBefore) { + if(ItemRef['rendition:layout'] == 'pre-paginated' && PreviousItem.Ref['rendition:page-spread'] == SpreadBefore) { PreviousItem.SpreadPair = Item; Item.SpreadPair = PreviousItem; Spread = Item.Spread = PreviousItem.Spread; @@ -749,7 +934,7 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then case SpreadAfter: Spread.Box.classList.add('single-item-spread-after' ); break; } } - R.Spreads.push(Spine.SpreadsDocumentFragment.appendChild(Spread.Box).appendChild(Spread)); + R.Spreads.push(SpreadsDocumentFragment.appendChild(Spread.Box).appendChild(Spread)); } Item.IndexInSpread = Spread.Items.length; Spread.Items.push(Item); @@ -762,13 +947,13 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then IndexInItem: 0 }); Item.Pages.push(Item.Box.appendChild(Page)); - Bibi.Eyes.observe(Page); + I.PageObserver.observePageIntersection(Page); } else { Item.PrePaginated = Spread.PrePaginated = false; } } }); - R.Main.Book.appendChild(B.Package.Spine.SpreadsDocumentFragment); + R.Main.Book.appendChild(SpreadsDocumentFragment); // -------------------------------------------------------------------------------- B.FileDigit = (Spine.Items.length + '').length; // ================================================================================ @@ -780,7 +965,7 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then const FullTitleFragments = [B.Title]; if(B.Creator) FullTitleFragments.push(B.Creator); if(B.Publisher) FullTitleFragments.push(B.Publisher); - B.FullTitle = FullTitleFragments.join(' - ').replace(/&?/gi, '&').replace(/<?/gi, '<').replace(/>?/gi, '>') + B.FullTitle = FullTitleFragments.join(' - ').replace(/&?/gi, '&').replace(/<?/gi, '<').replace(/>?/gi, '>'); O.Title.innerHTML = ''; O.Title.appendChild(document.createTextNode(B.FullTitle + ' | ' + (S['website-name-in-title'] ? S['website-name-in-title'] : 'Published with Bibi'))); try { O.Info.querySelector('h1').innerHTML = document.title; } catch(_) {} @@ -789,11 +974,6 @@ L.loadPackage = () => O.openDocument(B.Package).then(L.loadPackage.process).then : /^(mo?n)$/.test(B.Language) ? 'tb-lr' : 'lr-tb'; B.AllowPlaceholderItems = (B.ExtractionPolicy != 'at-once' && Metadata['rendition:layout'] == 'pre-paginated'); - [B.Container.Path, B.Package.Path].forEach(Path => { - const Item = B.Package.Manifest.Items[Path]; - delete Item.Path, delete Item.Content, delete Item.DataType; - delete B.Package.Manifest.Items[Path]; - }); // ================================================================================ E.dispatch('bibi:processed-package'); }; @@ -901,7 +1081,12 @@ L.coordinateLinkages = (BasePath, RootElement, InNav) => { const HrefHashInSource = HrefPathInSource.split('#')[1]; HrefPathInSource = (HrefHashInSource ? '#' + HrefHashInSource : R.Items[0].RefChain[0]) } else { - A.setAttribute('target', A.getAttribute('target') || '_blank'); + A.addEventListener('click', Eve => { + Eve.preventDefault(); + Eve.stopPropagation(); + window.open(A.href); + return false; + }); continue; } } @@ -914,12 +1099,9 @@ L.coordinateLinkages = (BasePath, RootElement, InNav) => { A.setAttribute('data-bibi-original-href', HrefPathInSource); A.setAttribute(HrefAttribute, B.Path + '/' + HrefPath); A.InNav = InNav; - A.Destination = (Item.Ref['rendition:layout'] == 'pre-paginated') ? { - Page: Item.Pages[0] - } : { - Item: Item, - ElementSelector: (HrefHash ? '#' + HrefHash : undefined) - }; + A.Destination = { ItemIndex: Item.Index }; // not IIPP. ElementSelector may be added. + if(Item.Ref['rendition:layout'] == 'pre-paginated') A.Destination.PageIndexInItem = 0; + else if(HrefHash) A.Destination.ElementSelector = '#' + HrefHash; L.coordinateLinkages.setJump(A); return 'break'; //// break sML.forEach() } @@ -940,7 +1122,7 @@ L.coordinateLinkages = (BasePath, RootElement, InNav) => { }); } } - if(InNav && typeof S['nav'] == (i + 1) && A.Destination) S['to'] = A.Destination; + if(InNav && R.StartOn && R.StartOn.Nav == (i + 1) && A.Destination) R.StartOn = A.Destination; } }; @@ -948,10 +1130,13 @@ L.coordinateLinkages = (BasePath, RootElement, InNav) => { Eve.preventDefault(); Eve.stopPropagation(); if(A.Destination) new Promise(resolve => A.InNav ? I.Panel.toggle().then(resolve) : resolve()).then(() => { - if(L.Opened) return R.focusOn({ Destination: A.Destination, Duration: 0 }).then(Destination => I.History.add({ UI: B, SumUp: false, Destination: Destination })); + if(L.Opened) { + I.History.add(); + return R.focusOn({ Destination: A.Destination, Duration: 0 }).then(Destination => I.History.add({ UI: B, SumUp: false, Destination: Destination })); + } if(!L.Waiting) return false; if(S['start-in-new-window']) return L.openNewWindow(location.href + (location.hash ? ',' : '#') + 'jo(nav:' + A.NavANumber + ')'); - S['to'] = A.Destination; + R.StartOn = A.Destination; L.play(); }); return false; @@ -1011,84 +1196,68 @@ L.loadItem = (Item, Opt = {}) => { // !!!! Don't Call Directly. Use L.loadSpread } ItemBox.classList.remove('loaded'); return new Promise((resolve, reject) => { - if(Item.BlobURL) { resolve({}); return; } - if(/\.(html?|xht(ml)?|xml)$/i.test(Item.Path)) { // (X)HTML - if(!B.ExtractionPolicy /*!!!!!!!!*/ && !sML.UA.Gecko /*!!!!!!!!*/ ) { // Extracted (exclude Gecko from here, because of such books as styled only with -webkit/epub- prefixed properties. It's NOT Gecko's fault.) - resolve({ - URL: O.fullPath(Item.Path) - }); return; - } - O.file(Item, { Preprocess: true }).then(Item => { // Archived (or Gecko. It's NOT Gecko's fault...) - resolve({ - HTML: Item.Content.replace(/^<\?.+?\?>/, '') - }) - }).catch(reject); return; - } - if(/\.(gif|jpe?g|png)$/i.test(Item.Path)) { // Bitmap-in-Spine - O.file(Item, { URI: true }).then(Item => { - resolve({ - Head: (Item.Ref['rendition:layout'] == 'pre-paginated' && B.ICBViewport) ? `` : '', - Body: `` // URI is BlobURL or URI - }) - }).catch(reject); return; - } - if(/\.(svg)$/i.test(Item.Path)) { // SVG-in-Spine - O.file(Item, { Preprocess: true }).then(Item => { - const StyleSheetRE = /<\?xml-stylesheet\s*(.+?)\s*\?>/g, MatchedStyleSheets = Item.Content.match(StyleSheetRE); - let StyleSheets = '', Content = Item.Content; - if(MatchedStyleSheets) StyleSheets = MatchedStyleSheets.map(SS => SS.replace(StyleSheetRE, ``)).join(''), Content = Content.replace(StyleSheetRE, ''); - resolve({ - Head: (!B.ExtractionPolicy ? `` : '') + StyleSheets, - Body: Content - }); - }).catch(reject); return; - } - resolve({}); + if(Item.BlobURL) { + resolve(); + } else if(/\.(html?|xht(ml)?|xml)$/i.test(Item.Path)) { // (X)HTML + O.file(Item, { + Preprocess: (B.ExtractionPolicy || sML.UA.Gecko), // Preprocess if archived (or Gecko. For such books as styled only with -webkit/epub- prefixed properties. It's NOT Gecko's fault but requires preprocessing.) + initialize: () => { + if(!S['allow-scripts-in-content']) O.sanitizeItemContent(Item, { As: 'HTML' }); + } + }).then(Item => resolve(Item.Content)); + } else if(/\.(gif|jpe?g|png)$/i.test(Item.Path)) { // Bitmap-in-Spine + O.file(Item, { + URI: true + }).then(Item => resolve([ + (Item.Ref['rendition:layout'] == 'pre-paginated' && B.ICBViewport) ? `` : '', + `` // URI is BlobURL or URI + ])); + } else if(/\.(svg)$/i.test(Item.Path)) { // SVG-in-Spine + O.file(Item, { + Preprocess: (B.ExtractionPolicy ? true : false), + initialize: () => { + const StyleSheetRE = /<\?xml-stylesheet\s*(.+?)\s*\?>/g, MatchedStyleSheets = Item.Content.match(StyleSheetRE); + if(!S['allow-scripts-in-content']) O.sanitizeItemContent(Item, { As: 'SVG' }); + Item.Content = (MatchedStyleSheets ? MatchedStyleSheets.map(SS => SS.replace(StyleSheetRE, ``)).join('') : '') + '' + Item.Content; // Join for preprocessing. + } + }).then(Item => resolve(Item.Content.split(''))); + } else reject(); + }).catch(() => { + Item.Skipped = true; + return []; }).then(Source => new Promise(resolve => { const DefaultStyleID = 'bibi-default-style'; - if(Source.URL) { - Item.onload = () => { - const Head = Item.contentDocument.getElementsByTagName('head')[0]; - const Link = sML.create('link', { rel: 'stylesheet', id: DefaultStyleID, href: Bibi.BookStyleURL, onload: resolve }); - Head.insertBefore(Link, Head.firstChild); - }; - Item.src = Source.URL; - } else { - if(!Item.BlobURL) { - let HTML = Source.HTML || `\n${ B.FullTitle } - #${ Item.Index + 1 }/${ R.Items.length }${ Source.Head || '' }${ Source.Body || '' }`; - HTML = HTML.replace(/(]+)?>)/i, `$1`); - if(sML.UA.Trident || sML.UA.EdgeHTML) { - // Legacy Microsoft Browsers do not accept DataURIs for src of