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);
}