diff --git a/examples/examples.css b/examples/examples.css index 2f5d2fb..2bbf1bb 100644 --- a/examples/examples.css +++ b/examples/examples.css @@ -7,6 +7,7 @@ body { position: absolute; height: 100%; width: 100%; + min-height: 800px; } #title { @@ -16,6 +17,7 @@ body { text-align: center; font-size: 16px; color: #E2E2E2; + font-weight: 400; } #title:hover { @@ -31,6 +33,7 @@ body { position: relative; margin: 10px auto; background: white url('ajax-loader.gif') center center no-repeat; + top: calc(50vh - 400px); } #viewer.spreads .epub-view > iframe { @@ -125,6 +128,7 @@ body { margin: 28px auto; background: #fff; border-radius: 0 5px 5px 0; + position: absolute; } #book-viewer { @@ -153,3 +157,118 @@ body { #controls > input[type=range] { width: 400px; } + +#navigation { + width: 400px; + height: 100vh; + position: absolute; + overflow: auto; + top: 0; + left: 0; + background: #777; + -webkit-transition: -webkit-transform .25s ease-out; + -moz-transition: -moz-transform .25s ease-out; + -ms-transition: -moz-transform .25s ease-out; + transition: transform .25s ease-out; + +} + +#navigation.fixed { + position: fixed; +} + +#navigation h1 { + width: 200px; + font-size: 16px; + font-weight: normal; + color: #fff; + margin-bottom: 10px; +} + +#navigation h2 { + font-size: 14px; + font-weight: normal; + color: #B0B0B0; + margin-bottom: 20px; +} + +#navigation ul { + padding-left: 36px; + margin-left: 0; + margin-top: 12px; + margin-bottom: 12px; + width: 340px; +} + +#navigation ul li { + list-style: decimal; + margin-bottom: 10px; + color: #cccddd; + font-size: 12px; + padding-left: 0; + margin-left: 0; +} + +#navigation ul li a { + color: #ccc; + text-decoration: none; +} + +#navigation ul li a:hover { + color: #fff; + text-decoration: underline; +} + +#navigation ul li a.active { + color: #fff; +} + +#navigation #cover { + display: block; + margin: 24px auto; +} + +#navigation #closer { + position: absolute; + top: 0; + right: 0; + padding: 12px; + color: #cccddd; + width: 24px; +} + +#navigation.closed { + -webkit-transform: translate(-400px, 0); + -moz-transform: translate(-400px, 0); + -ms-transform: translate(-400px, 0); +} + +svg { + display: block; +} + +.close-x { + stroke: #cccddd; + fill: transparent; + stroke-linecap: round; + stroke-width: 5; +} + +.close-x:hover { + stroke: #fff; +} + +#opener { + position: absolute; + top: 0; + left: 0; + padding: 10px; + stroke: #E2E2E2; + fill: #E2E2E2; + +} + +#opener:hover { + stroke: #777; + fill: #777; +} diff --git a/examples/manifest.html b/examples/manifest.html new file mode 100644 index 0000000..651aa3b --- /dev/null +++ b/examples/manifest.html @@ -0,0 +1,179 @@ + + + + + + EPUB.js Spreads Example + + + + + + + +

...

+
+ + + +
+
+ + + + + + + + diff --git a/examples/single-full.html b/examples/single-full.html index 8033102..194e47b 100644 --- a/examples/single-full.html +++ b/examples/single-full.html @@ -14,16 +14,22 @@ background: #fafafa; font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; color: #333; - display: flex; - justify-content: center; } #navigation { - width: 200px; - margin-top: 60px; + width: 300px; + position: absolute; + overflow: auto; + top: 60px; + left: 1000px + } + + #navigation.fixed { + position: fixed; } #navigation h1 { + width: 200px; font-size: 16px; font-weight: normal; color: #777; @@ -38,8 +44,10 @@ } #navigation ul { - padding-left: 0; + padding-left: 18px; margin-left: 0; + margin-top: 12px; + margin-bottom: 12px; } #navigation ul li { @@ -77,13 +85,32 @@ #viewer .epub-view { background: white; box-shadow: 0 0 4px #ccc; - margin: 10px; - padding: 20px; + /*margin: 10px;*/ + /*padding: 40px 80px;*/ + } + + #main { + position: absolute; + top: 50px; + left: 50px; + width: 800px; + z-index: 2; + transition: left .15s cubic-bezier(.55, 0, .2, .8) .08s; + } + + #main.open { + left: 0; + } + + #pagination { + text-align: center; + margin-left: 80px; + /*padding: 0 50px;*/ } .arrow { margin: 14px; - display: block; + display: inline-block; text-align: center; text-decoration: none; color: #ccc; @@ -97,33 +124,43 @@ color: #000; } + #prev { + float: left; + } + + #next { + float: right; + } + #toc { display: block; margin: 10px auto; } -
-
- +
diff --git a/src/archive.js b/src/archive.js index 2e2ccb8..7dd867b 100644 --- a/src/archive.js +++ b/src/archive.js @@ -247,6 +247,7 @@ class Archive { } destroy() { + var _URL = window.URL || window.webkitURL || window.mozURL; for (let fromCache in this.urlCache) { _URL.revokeObjectURL(fromCache); } diff --git a/src/book.js b/src/book.js index 82eddd1..dd99cbd 100644 --- a/src/book.js +++ b/src/book.js @@ -201,6 +201,9 @@ class Book { } else if(type == "opf") { this.url = new Url(input); opening = this.openPackaging(this.url.Path.toString()); + } else if(type == "json") { + this.url = new Url(input); + opening = this.openManifest(this.url.Path.toString()); } else { this.url = new Url(input); opening = this.openContainer(CONTAINER_PATH) @@ -256,6 +259,22 @@ class Book { }); } + /** + * Open the manifest JSON + * @private + * @param {string} url + * @return {Promise} + */ + openManifest(url) { + this.path = new Path(url); + return this.load(url) + .then((json) => { + this.packaging = new Packaging(); + this.packaging.load(json); + return this.unpack(this.packaging); + }); + } + /** * Load a resource from the Book * @param {string} path path to the resource to load @@ -335,6 +354,10 @@ class Book { if(extension === "opf"){ return "opf"; } + + if(extension === "json"){ + return "json"; + } } @@ -359,6 +382,7 @@ class Book { this.toc = this.navigation.toc; this.loading.navigation.resolve(this.navigation); }); + if (this.package.coverPath) { this.cover = this.resolve(this.package.coverPath); } @@ -370,7 +394,6 @@ class Book { this.loading.resources.resolve(this.resources); this.loading.pageList.resolve(this.pageList); - this.isOpen = true; if(this.archived || this.settings.replacements && this.settings.replacements != "none") { @@ -393,10 +416,26 @@ class Book { * @param {document} opf XML Document */ loadNavigation(opf) { - var navPath = opf.navPath || opf.ncxPath; + let navPath = opf.navPath || opf.ncxPath; + let toc = opf.toc; + + if (toc) { + return new Promise((resolve, reject) => { + this.navigation = new Navigation(toc); + + this.pageList = new PageList(); // TODO: handle page lists + + resolve(this.navigation); + }); + } if (!navPath) { - return; + return new Promise((resolve, reject) => { + this.navigation = new Navigation(); + this.pageList = new PageList(); + + resolve(this.navigation); + }); } return this.load(navPath, "xml") diff --git a/src/managers/views/iframe.js b/src/managers/views/iframe.js index 6f8ff4c..0dfeaf2 100644 --- a/src/managers/views/iframe.js +++ b/src/managers/views/iframe.js @@ -102,7 +102,7 @@ class IframeView { // Firefox has trouble with baseURI and srcdoc // TODO: Disable for now in firefox - if(!("srcdoc" in this.iframe)) { + if("srcdoc" in this.iframe) { this.supportsSrcdoc = true; } else { this.supportsSrcdoc = false; @@ -369,6 +369,10 @@ class IframeView { this.onResize(this, size); + if (this.contents) { + this.settings.layout.format(this.contents); + } + this.emit("resized", size); } diff --git a/src/navigation.js b/src/navigation.js index 1ee0b66..9cdb92c 100644 --- a/src/navigation.js +++ b/src/navigation.js @@ -20,10 +20,15 @@ class Navigation { * @param {document} xml navigation html / xhtml / ncx */ parse(xml) { - var html = qs(xml, "html"); - var ncx = qs(xml, "ncx"); + let isXml = xml.nodeValue; + if (isXml) { + let html = qs(xml, "html"); + let ncx = qs(xml, "ncx"); + } - if(html) { + if (!isXml) { + this.toc = this.load(xml); + } else if(html) { this.toc = this.parseNav(xml); } else if(ncx){ this.toc = this.parseNcx(xml); @@ -203,6 +208,20 @@ class Navigation { }; } + /** + * Load Spine Items + * @param {object} json the items to be loaded + */ + load(json) { + return json.map((item) => { + item.label = item.title; + if (item.children) { + item.subitems = this.load(item.children); + } + return item; + }); + } + /** * forEach pass through * @param {Function} fn function to run on each item diff --git a/src/packaging.js b/src/packaging.js index 6f56124..2a3a059 100644 --- a/src/packaging.js +++ b/src/packaging.js @@ -282,6 +282,46 @@ class Packaging { return ""; } + /** + * Load JSON Manifest + * @param {document} packageDocument OPF XML + * @return {object} parsed package parts + */ + load(json) { + this.metadata = json.metadata; + + this.spine = json.spine.map((item, index) =>{ + item.index = index; + return item; + }); + + json.resources.forEach((item, index) => { + this.manifest[index] = item; + + if (item.rel && item.rel[0] === "cover") { + this.coverPath = item.href; + } + }); + + this.spineNodeIndex = 0; + + this.toc = json.toc.map((item, index) =>{ + item.label = item.title; + return item; + }); + + return { + "metadata" : this.metadata, + "spine" : this.spine, + "manifest" : this.manifest, + "navPath" : this.navPath, + "ncxPath" : this.ncxPath, + "coverPath": this.coverPath, + "spineNodeIndex" : this.spineNodeIndex, + "toc" : this.toc + }; + } + destroy() { this.manifest = undefined; this.navPath = undefined; diff --git a/src/spine.js b/src/spine.js index ceb972d..052b05e 100644 --- a/src/spine.js +++ b/src/spine.js @@ -50,10 +50,13 @@ class Spine { item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.idref); + if (item.href) { + item.url = resolver(item.href, true); + } + if(manifestItem) { item.href = manifestItem.href; item.url = resolver(item.href, true); - if(manifestItem.properties.length){ item.properties.push.apply(item.properties, manifestItem.properties); }