1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-03 14:59:18 +02:00

Added pageList, initial documentation

This commit is contained in:
Fred Chasen 2016-11-10 23:08:39 +01:00
parent 52fd6fa7d3
commit d14280b917
42 changed files with 7799 additions and 259 deletions

6
documentation.yml Normal file
View file

@ -0,0 +1,6 @@
toc:
- ePub
- name: ePubJS
description: |
main entry
- EpubCFI

View file

@ -0,0 +1,197 @@
/*!
* AnchorJS - v1.2.1 - 2015-07-02
* https://github.com/bryanbraun/anchorjs
* Copyright (c) 2015 Bryan Braun; Licensed MIT
*/
function AnchorJS(options) {
'use strict';
this.options = options || {};
this._applyRemainingDefaultOptions = function(opts) {
this.options.icon = this.options.hasOwnProperty('icon') ? opts.icon : '\ue9cb'; // Accepts characters (and also URLs?), like '#', '¶', '❡', or '§'.
this.options.visible = this.options.hasOwnProperty('visible') ? opts.visible : 'hover'; // Also accepts 'always'
this.options.placement = this.options.hasOwnProperty('placement') ? opts.placement : 'right'; // Also accepts 'left'
this.options.class = this.options.hasOwnProperty('class') ? opts.class : ''; // Accepts any class name.
};
this._applyRemainingDefaultOptions(options);
this.add = function(selector) {
var elements,
elsWithIds,
idList,
elementID,
i,
roughText,
tidyText,
index,
count,
newTidyText,
readableID,
anchor;
this._applyRemainingDefaultOptions(this.options);
// Provide a sensible default selector, if none is given.
if (!selector) {
selector = 'h1, h2, h3, h4, h5, h6';
} else if (typeof selector !== 'string') {
throw new Error('The selector provided to AnchorJS was invalid.');
}
elements = document.querySelectorAll(selector);
if (elements.length === 0) {
return false;
}
this._addBaselineStyles();
// We produce a list of existing IDs so we don't generate a duplicate.
elsWithIds = document.querySelectorAll('[id]');
idList = [].map.call(elsWithIds, function assign(el) {
return el.id;
});
for (i = 0; i < elements.length; i++) {
if (elements[i].hasAttribute('id')) {
elementID = elements[i].getAttribute('id');
} else {
roughText = elements[i].textContent;
// Refine it so it makes a good ID. Strip out non-safe characters, replace
// spaces with hyphens, truncate to 32 characters, and make toLowerCase.
//
// Example string: // '⚡⚡⚡ Unicode icons are cool--but they definitely don't belong in a URL fragment.'
tidyText = roughText.replace(/[^\w\s-]/gi, '') // ' Unicode icons are cool--but they definitely dont belong in a URL fragment'
.replace(/\s+/g, '-') // '-Unicode-icons-are-cool--but-they-definitely-dont-belong-in-a-URL-fragment'
.replace(/-{2,}/g, '-') // '-Unicode-icons-are-cool-but-they-definitely-dont-belong-in-a-URL-fragment'
.substring(0, 64) // '-Unicode-icons-are-cool-but-they-definitely-dont-belong-in-a-URL'
.replace(/^-+|-+$/gm, '') // 'Unicode-icons-are-cool-but-they-definitely-dont-belong-in-a-URL'
.toLowerCase(); // 'unicode-icons-are-cool-but-they-definitely-dont-belong-in-a-url'
// Compare our generated ID to existing IDs (and increment it if needed)
// before we add it to the page.
newTidyText = tidyText;
count = 0;
do {
if (index !== undefined) {
newTidyText = tidyText + '-' + count;
}
// .indexOf is supported in IE9+.
index = idList.indexOf(newTidyText);
count += 1;
} while (index !== -1);
index = undefined;
idList.push(newTidyText);
// Assign it to our element.
// Currently the setAttribute element is only supported in IE9 and above.
elements[i].setAttribute('id', newTidyText);
elementID = newTidyText;
}
readableID = elementID.replace(/-/g, ' ');
// The following code builds the following DOM structure in a more effiecient (albeit opaque) way.
// '<a class="anchorjs-link ' + this.options.class + '" href="#' + elementID + '" aria-label="Anchor link for: ' + readableID + '" data-anchorjs-icon="' + this.options.icon + '"></a>';
anchor = document.createElement('a');
anchor.className = 'anchorjs-link ' + this.options.class;
anchor.href = '#' + elementID;
anchor.setAttribute('aria-label', 'Anchor link for: ' + readableID);
anchor.setAttribute('data-anchorjs-icon', this.options.icon);
if (this.options.visible === 'always') {
anchor.style.opacity = '1';
}
if (this.options.icon === '\ue9cb') {
anchor.style.fontFamily = 'anchorjs-icons';
anchor.style.fontStyle = 'normal';
anchor.style.fontVariant = 'normal';
anchor.style.fontWeight = 'normal';
anchor.style.lineHeight = 1;
}
if (this.options.placement === 'left') {
anchor.style.position = 'absolute';
anchor.style.marginLeft = '-1em';
anchor.style.paddingRight = '0.5em';
elements[i].insertBefore(anchor, elements[i].firstChild);
} else { // if the option provided is `right` (or anything else).
anchor.style.paddingLeft = '0.375em';
elements[i].appendChild(anchor);
}
}
return this;
};
this.remove = function(selector) {
var domAnchor,
elements = document.querySelectorAll(selector);
for (var i = 0; i < elements.length; i++) {
domAnchor = elements[i].querySelector('.anchorjs-link');
if (domAnchor) {
elements[i].removeChild(domAnchor);
}
}
return this;
};
this._addBaselineStyles = function() {
// We don't want to add global baseline styles if they've been added before.
if (document.head.querySelector('style.anchorjs') !== null) {
return;
}
var style = document.createElement('style'),
linkRule =
' .anchorjs-link {' +
' opacity: 0;' +
' text-decoration: none;' +
' -webkit-font-smoothing: antialiased;' +
' -moz-osx-font-smoothing: grayscale;' +
' }',
hoverRule =
' *:hover > .anchorjs-link,' +
' .anchorjs-link:focus {' +
' opacity: 1;' +
' }',
anchorjsLinkFontFace =
' @font-face {' +
' font-family: "anchorjs-icons";' +
' font-style: normal;' +
' font-weight: normal;' + // Icon from icomoon; 10px wide & 10px tall; 2 empty below & 4 above
' src: url(data:application/x-font-ttf;charset=utf-8;base64,AAEAAAALAIAAAwAwT1MvMg8SBTUAAAC8AAAAYGNtYXAWi9QdAAABHAAAAFRnYXNwAAAAEAAAAXAAAAAIZ2x5Zgq29TcAAAF4AAABNGhlYWQEZM3pAAACrAAAADZoaGVhBhUDxgAAAuQAAAAkaG10eASAADEAAAMIAAAAFGxvY2EAKACuAAADHAAAAAxtYXhwAAgAVwAAAygAAAAgbmFtZQ5yJ3cAAANIAAAB2nBvc3QAAwAAAAAFJAAAACAAAwJAAZAABQAAApkCzAAAAI8CmQLMAAAB6wAzAQkAAAAAAAAAAAAAAAAAAAABEAAAAAAAAAAAAAAAAAAAAABAAADpywPA/8AAQAPAAEAAAAABAAAAAAAAAAAAAAAgAAAAAAADAAAAAwAAABwAAQADAAAAHAADAAEAAAAcAAQAOAAAAAoACAACAAIAAQAg6cv//f//AAAAAAAg6cv//f//AAH/4xY5AAMAAQAAAAAAAAAAAAAAAQAB//8ADwABAAAAAAAAAAAAAgAANzkBAAAAAAEAAAAAAAAAAAACAAA3OQEAAAAAAQAAAAAAAAAAAAIAADc5AQAAAAACADEARAJTAsAAKwBUAAABIiYnJjQ/AT4BMzIWFxYUDwEGIicmND8BNjQnLgEjIgYPAQYUFxYUBw4BIwciJicmND8BNjIXFhQPAQYUFx4BMzI2PwE2NCcmNDc2MhcWFA8BDgEjARQGDAUtLXoWOR8fORYtLTgKGwoKCjgaGg0gEhIgDXoaGgkJBQwHdR85Fi0tOAobCgoKOBoaDSASEiANehoaCQkKGwotLXoWOR8BMwUFLYEuehYXFxYugC44CQkKGwo4GkoaDQ0NDXoaShoKGwoFBe8XFi6ALjgJCQobCjgaShoNDQ0NehpKGgobCgoKLYEuehYXAAEAAAABAACiToc1Xw889QALBAAAAAAA0XnFFgAAAADRecUWAAAAAAJTAsAAAAAIAAIAAAAAAAAAAQAAA8D/wAAABAAAAAAAAlMAAQAAAAAAAAAAAAAAAAAAAAUAAAAAAAAAAAAAAAACAAAAAoAAMQAAAAAACgAUAB4AmgABAAAABQBVAAIAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAADgCuAAEAAAAAAAEADgAAAAEAAAAAAAIABwCfAAEAAAAAAAMADgBLAAEAAAAAAAQADgC0AAEAAAAAAAUACwAqAAEAAAAAAAYADgB1AAEAAAAAAAoAGgDeAAMAAQQJAAEAHAAOAAMAAQQJAAIADgCmAAMAAQQJAAMAHABZAAMAAQQJAAQAHADCAAMAAQQJAAUAFgA1AAMAAQQJAAYAHACDAAMAAQQJAAoANAD4YW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzVmVyc2lvbiAxLjAAVgBlAHIAcwBpAG8AbgAgADEALgAwYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzUmVndWxhcgBSAGUAZwB1AGwAYQByYW5jaG9yanMtaWNvbnMAYQBuAGMAaABvAHIAagBzAC0AaQBjAG8AbgBzRm9udCBnZW5lcmF0ZWQgYnkgSWNvTW9vbi4ARgBvAG4AdAAgAGcAZQBuAGUAcgBhAHQAZQBkACAAYgB5ACAASQBjAG8ATQBvAG8AbgAuAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==) format("truetype");' +
' }',
pseudoElContent =
' [data-anchorjs-icon]::after {' +
' content: attr(data-anchorjs-icon);' +
' }',
firstStyleEl;
style.className = 'anchorjs';
style.appendChild(document.createTextNode('')); // Necessary for Webkit.
// We place it in the head with the other style tags, if possible, so as to
// not look out of place. We insert before the others so these styles can be
// overridden if necessary.
firstStyleEl = document.head.querySelector('[rel="stylesheet"], style');
if (firstStyleEl === undefined) {
document.head.appendChild(style);
} else {
document.head.insertBefore(style, firstStyleEl);
}
style.sheet.insertRule(linkRule, style.sheet.cssRules.length);
style.sheet.insertRule(hoverRule, style.sheet.cssRules.length);
style.sheet.insertRule(pseudoElContent, style.sheet.cssRules.length);
style.sheet.insertRule(anchorjsLinkFontFace, style.sheet.cssRules.length);
};
}
var anchors = new AnchorJS();

View file

@ -0,0 +1,12 @@
.input {
font-family: inherit;
display: block;
width: 100%;
height: 2rem;
padding: .5rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
font-size: .875rem;
border-radius: 3px;
box-sizing: border-box;
}

View file

@ -0,0 +1,543 @@
/*! Basscss | http://basscss.com | MIT License */
.h1{ font-size: 2rem }
.h2{ font-size: 1.5rem }
.h3{ font-size: 1.25rem }
.h4{ font-size: 1rem }
.h5{ font-size: .875rem }
.h6{ font-size: .75rem }
.font-family-inherit{ font-family:inherit }
.font-size-inherit{ font-size:inherit }
.text-decoration-none{ text-decoration:none }
.bold{ font-weight: bold; font-weight: bold }
.regular{ font-weight:normal }
.italic{ font-style:italic }
.caps{ text-transform:uppercase; letter-spacing: .2em; }
.left-align{ text-align:left }
.center{ text-align:center }
.right-align{ text-align:right }
.justify{ text-align:justify }
.nowrap{ white-space:nowrap }
.break-word{ word-wrap:break-word }
.line-height-1{ line-height: 1 }
.line-height-2{ line-height: 1.125 }
.line-height-3{ line-height: 1.25 }
.line-height-4{ line-height: 1.5 }
.list-style-none{ list-style:none }
.underline{ text-decoration:underline }
.truncate{
max-width:100%;
overflow:hidden;
text-overflow:ellipsis;
white-space:nowrap;
}
.list-reset{
list-style:none;
padding-left:0;
}
.inline{ display:inline }
.block{ display:block }
.inline-block{ display:inline-block }
.table{ display:table }
.table-cell{ display:table-cell }
.overflow-hidden{ overflow:hidden }
.overflow-scroll{ overflow:scroll }
.overflow-auto{ overflow:auto }
.clearfix:before,
.clearfix:after{
content:" ";
display:table
}
.clearfix:after{ clear:both }
.left{ float:left }
.right{ float:right }
.fit{ max-width:100% }
.max-width-1{ max-width: 24rem }
.max-width-2{ max-width: 32rem }
.max-width-3{ max-width: 48rem }
.max-width-4{ max-width: 64rem }
.border-box{ box-sizing:border-box }
.align-baseline{ vertical-align:baseline }
.align-top{ vertical-align:top }
.align-middle{ vertical-align:middle }
.align-bottom{ vertical-align:bottom }
.m0{ margin:0 }
.mt0{ margin-top:0 }
.mr0{ margin-right:0 }
.mb0{ margin-bottom:0 }
.ml0{ margin-left:0 }
.mx0{ margin-left:0; margin-right:0 }
.my0{ margin-top:0; margin-bottom:0 }
.m1{ margin: .5rem }
.mt1{ margin-top: .5rem }
.mr1{ margin-right: .5rem }
.mb1{ margin-bottom: .5rem }
.ml1{ margin-left: .5rem }
.mx1{ margin-left: .5rem; margin-right: .5rem }
.my1{ margin-top: .5rem; margin-bottom: .5rem }
.m2{ margin: 1rem }
.mt2{ margin-top: 1rem }
.mr2{ margin-right: 1rem }
.mb2{ margin-bottom: 1rem }
.ml2{ margin-left: 1rem }
.mx2{ margin-left: 1rem; margin-right: 1rem }
.my2{ margin-top: 1rem; margin-bottom: 1rem }
.m3{ margin: 2rem }
.mt3{ margin-top: 2rem }
.mr3{ margin-right: 2rem }
.mb3{ margin-bottom: 2rem }
.ml3{ margin-left: 2rem }
.mx3{ margin-left: 2rem; margin-right: 2rem }
.my3{ margin-top: 2rem; margin-bottom: 2rem }
.m4{ margin: 4rem }
.mt4{ margin-top: 4rem }
.mr4{ margin-right: 4rem }
.mb4{ margin-bottom: 4rem }
.ml4{ margin-left: 4rem }
.mx4{ margin-left: 4rem; margin-right: 4rem }
.my4{ margin-top: 4rem; margin-bottom: 4rem }
.mxn1{ margin-left: -.5rem; margin-right: -.5rem; }
.mxn2{ margin-left: -1rem; margin-right: -1rem; }
.mxn3{ margin-left: -2rem; margin-right: -2rem; }
.mxn4{ margin-left: -4rem; margin-right: -4rem; }
.ml-auto{ margin-left:auto }
.mr-auto{ margin-right:auto }
.mx-auto{ margin-left:auto; margin-right:auto; }
.p0{ padding:0 }
.pt0{ padding-top:0 }
.pr0{ padding-right:0 }
.pb0{ padding-bottom:0 }
.pl0{ padding-left:0 }
.px0{ padding-left:0; padding-right:0 }
.py0{ padding-top:0; padding-bottom:0 }
.p1{ padding: .5rem }
.pt1{ padding-top: .5rem }
.pr1{ padding-right: .5rem }
.pb1{ padding-bottom: .5rem }
.pl1{ padding-left: .5rem }
.py1{ padding-top: .5rem; padding-bottom: .5rem }
.px1{ padding-left: .5rem; padding-right: .5rem }
.p2{ padding: 1rem }
.pt2{ padding-top: 1rem }
.pr2{ padding-right: 1rem }
.pb2{ padding-bottom: 1rem }
.pl2{ padding-left: 1rem }
.py2{ padding-top: 1rem; padding-bottom: 1rem }
.px2{ padding-left: 1rem; padding-right: 1rem }
.p3{ padding: 2rem }
.pt3{ padding-top: 2rem }
.pr3{ padding-right: 2rem }
.pb3{ padding-bottom: 2rem }
.pl3{ padding-left: 2rem }
.py3{ padding-top: 2rem; padding-bottom: 2rem }
.px3{ padding-left: 2rem; padding-right: 2rem }
.p4{ padding: 4rem }
.pt4{ padding-top: 4rem }
.pr4{ padding-right: 4rem }
.pb4{ padding-bottom: 4rem }
.pl4{ padding-left: 4rem }
.py4{ padding-top: 4rem; padding-bottom: 4rem }
.px4{ padding-left: 4rem; padding-right: 4rem }
.col{
float:left;
box-sizing:border-box;
}
.col-right{
float:right;
box-sizing:border-box;
}
.col-1{
width:8.33333%;
}
.col-2{
width:16.66667%;
}
.col-3{
width:25%;
}
.col-4{
width:33.33333%;
}
.col-5{
width:41.66667%;
}
.col-6{
width:50%;
}
.col-7{
width:58.33333%;
}
.col-8{
width:66.66667%;
}
.col-9{
width:75%;
}
.col-10{
width:83.33333%;
}
.col-11{
width:91.66667%;
}
.col-12{
width:100%;
}
@media (min-width: 40em){
.sm-col{
float:left;
box-sizing:border-box;
}
.sm-col-right{
float:right;
box-sizing:border-box;
}
.sm-col-1{
width:8.33333%;
}
.sm-col-2{
width:16.66667%;
}
.sm-col-3{
width:25%;
}
.sm-col-4{
width:33.33333%;
}
.sm-col-5{
width:41.66667%;
}
.sm-col-6{
width:50%;
}
.sm-col-7{
width:58.33333%;
}
.sm-col-8{
width:66.66667%;
}
.sm-col-9{
width:75%;
}
.sm-col-10{
width:83.33333%;
}
.sm-col-11{
width:91.66667%;
}
.sm-col-12{
width:100%;
}
}
@media (min-width: 52em){
.md-col{
float:left;
box-sizing:border-box;
}
.md-col-right{
float:right;
box-sizing:border-box;
}
.md-col-1{
width:8.33333%;
}
.md-col-2{
width:16.66667%;
}
.md-col-3{
width:25%;
}
.md-col-4{
width:33.33333%;
}
.md-col-5{
width:41.66667%;
}
.md-col-6{
width:50%;
}
.md-col-7{
width:58.33333%;
}
.md-col-8{
width:66.66667%;
}
.md-col-9{
width:75%;
}
.md-col-10{
width:83.33333%;
}
.md-col-11{
width:91.66667%;
}
.md-col-12{
width:100%;
}
}
@media (min-width: 64em){
.lg-col{
float:left;
box-sizing:border-box;
}
.lg-col-right{
float:right;
box-sizing:border-box;
}
.lg-col-1{
width:8.33333%;
}
.lg-col-2{
width:16.66667%;
}
.lg-col-3{
width:25%;
}
.lg-col-4{
width:33.33333%;
}
.lg-col-5{
width:41.66667%;
}
.lg-col-6{
width:50%;
}
.lg-col-7{
width:58.33333%;
}
.lg-col-8{
width:66.66667%;
}
.lg-col-9{
width:75%;
}
.lg-col-10{
width:83.33333%;
}
.lg-col-11{
width:91.66667%;
}
.lg-col-12{
width:100%;
}
}
.flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex }
@media (min-width: 40em){
.sm-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex }
}
@media (min-width: 52em){
.md-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex }
}
@media (min-width: 64em){
.lg-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex }
}
.flex-column{ -webkit-box-orient:vertical; -webkit-box-direction:normal; -webkit-flex-direction:column; -ms-flex-direction:column; flex-direction:column }
.flex-wrap{ -webkit-flex-wrap:wrap; -ms-flex-wrap:wrap; flex-wrap:wrap }
.items-start{ -webkit-box-align:start; -webkit-align-items:flex-start; -ms-flex-align:start; -ms-grid-row-align:flex-start; align-items:flex-start }
.items-end{ -webkit-box-align:end; -webkit-align-items:flex-end; -ms-flex-align:end; -ms-grid-row-align:flex-end; align-items:flex-end }
.items-center{ -webkit-box-align:center; -webkit-align-items:center; -ms-flex-align:center; -ms-grid-row-align:center; align-items:center }
.items-baseline{ -webkit-box-align:baseline; -webkit-align-items:baseline; -ms-flex-align:baseline; -ms-grid-row-align:baseline; align-items:baseline }
.items-stretch{ -webkit-box-align:stretch; -webkit-align-items:stretch; -ms-flex-align:stretch; -ms-grid-row-align:stretch; align-items:stretch }
.self-start{ -webkit-align-self:flex-start; -ms-flex-item-align:start; align-self:flex-start }
.self-end{ -webkit-align-self:flex-end; -ms-flex-item-align:end; align-self:flex-end }
.self-center{ -webkit-align-self:center; -ms-flex-item-align:center; align-self:center }
.self-baseline{ -webkit-align-self:baseline; -ms-flex-item-align:baseline; align-self:baseline }
.self-stretch{ -webkit-align-self:stretch; -ms-flex-item-align:stretch; align-self:stretch }
.justify-start{ -webkit-box-pack:start; -webkit-justify-content:flex-start; -ms-flex-pack:start; justify-content:flex-start }
.justify-end{ -webkit-box-pack:end; -webkit-justify-content:flex-end; -ms-flex-pack:end; justify-content:flex-end }
.justify-center{ -webkit-box-pack:center; -webkit-justify-content:center; -ms-flex-pack:center; justify-content:center }
.justify-between{ -webkit-box-pack:justify; -webkit-justify-content:space-between; -ms-flex-pack:justify; justify-content:space-between }
.justify-around{ -webkit-justify-content:space-around; -ms-flex-pack:distribute; justify-content:space-around }
.content-start{ -webkit-align-content:flex-start; -ms-flex-line-pack:start; align-content:flex-start }
.content-end{ -webkit-align-content:flex-end; -ms-flex-line-pack:end; align-content:flex-end }
.content-center{ -webkit-align-content:center; -ms-flex-line-pack:center; align-content:center }
.content-between{ -webkit-align-content:space-between; -ms-flex-line-pack:justify; align-content:space-between }
.content-around{ -webkit-align-content:space-around; -ms-flex-line-pack:distribute; align-content:space-around }
.content-stretch{ -webkit-align-content:stretch; -ms-flex-line-pack:stretch; align-content:stretch }
.flex-auto{
-webkit-box-flex:1;
-webkit-flex:1 1 auto;
-ms-flex:1 1 auto;
flex:1 1 auto;
min-width:0;
min-height:0;
}
.flex-none{ -webkit-box-flex:0; -webkit-flex:none; -ms-flex:none; flex:none }
.order-0{ -webkit-box-ordinal-group:1; -webkit-order:0; -ms-flex-order:0; order:0 }
.order-1{ -webkit-box-ordinal-group:2; -webkit-order:1; -ms-flex-order:1; order:1 }
.order-2{ -webkit-box-ordinal-group:3; -webkit-order:2; -ms-flex-order:2; order:2 }
.order-3{ -webkit-box-ordinal-group:4; -webkit-order:3; -ms-flex-order:3; order:3 }
.order-last{ -webkit-box-ordinal-group:100000; -webkit-order:99999; -ms-flex-order:99999; order:99999 }
.relative{ position:relative }
.absolute{ position:absolute }
.fixed{ position:fixed }
.top-0{ top:0 }
.right-0{ right:0 }
.bottom-0{ bottom:0 }
.left-0{ left:0 }
.z1{ z-index: 1 }
.z2{ z-index: 2 }
.z3{ z-index: 3 }
.z4{ z-index: 4 }
.border{
border-style:solid;
border-width: 1px;
}
.border-top{
border-top-style:solid;
border-top-width: 1px;
}
.border-right{
border-right-style:solid;
border-right-width: 1px;
}
.border-bottom{
border-bottom-style:solid;
border-bottom-width: 1px;
}
.border-left{
border-left-style:solid;
border-left-width: 1px;
}
.border-none{ border:0 }
.rounded{ border-radius: 3px }
.circle{ border-radius:50% }
.rounded-top{ border-radius: 3px 3px 0 0 }
.rounded-right{ border-radius: 0 3px 3px 0 }
.rounded-bottom{ border-radius: 0 0 3px 3px }
.rounded-left{ border-radius: 3px 0 0 3px }
.not-rounded{ border-radius:0 }
.hide{
position:absolute !important;
height:1px;
width:1px;
overflow:hidden;
clip:rect(1px, 1px, 1px, 1px);
}
@media (max-width: 40em){
.xs-hide{ display:none !important }
}
@media (min-width: 40em) and (max-width: 52em){
.sm-hide{ display:none !important }
}
@media (min-width: 52em) and (max-width: 64em){
.md-hide{ display:none !important }
}
@media (min-width: 64em){
.lg-hide{ display:none !important }
}
.display-none{ display:none !important }

Binary file not shown.

View file

@ -0,0 +1,93 @@
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,23 @@
@font-face{
font-family: 'Source Code Pro';
font-weight: 400;
font-style: normal;
font-stretch: normal;
src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'),
url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'),
url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'),
url('OTF/SourceCodePro-Regular.otf') format('opentype'),
url('TTF/SourceCodePro-Regular.ttf') format('truetype');
}
@font-face{
font-family: 'Source Code Pro';
font-weight: 700;
font-style: normal;
font-stretch: normal;
src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'),
url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'),
url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'),
url('OTF/SourceCodePro-Bold.otf') format('opentype'),
url('TTF/SourceCodePro-Bold.ttf') format('truetype');
}

View file

@ -0,0 +1,123 @@
/*
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
*/
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #333;
background: #f8f8f8;
-webkit-text-size-adjust: none;
}
.hljs-comment,
.diff .hljs-header,
.hljs-javadoc {
color: #998;
font-style: italic;
}
.hljs-keyword,
.css .rule .hljs-keyword,
.hljs-winutils,
.nginx .hljs-title,
.hljs-subst,
.hljs-request,
.hljs-status {
color: #1184CE;
}
.hljs-number,
.hljs-hexcolor,
.ruby .hljs-constant {
color: #ed225d;
}
.hljs-string,
.hljs-tag .hljs-value,
.hljs-phpdoc,
.hljs-dartdoc,
.tex .hljs-formula {
color: #ed225d;
}
.hljs-title,
.hljs-id,
.scss .hljs-preprocessor {
color: #900;
font-weight: bold;
}
.hljs-list .hljs-keyword,
.hljs-subst {
font-weight: normal;
}
.hljs-class .hljs-title,
.hljs-type,
.vhdl .hljs-literal,
.tex .hljs-command {
color: #458;
font-weight: bold;
}
.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rules .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}
.hljs-attribute,
.hljs-variable,
.lisp .hljs-body {
color: #008080;
}
.hljs-regexp {
color: #009926;
}
.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.lisp .hljs-keyword,
.clojure .hljs-keyword,
.scheme .hljs-keyword,
.tex .hljs-special,
.hljs-prompt {
color: #990073;
}
.hljs-built_in {
color: #0086b3;
}
.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold;
}
.hljs-deletion {
background: #fdd;
}
.hljs-addition {
background: #dfd;
}
.diff .hljs-change {
background: #0086b3;
}
.hljs-chunk {
color: #aaa;
}

View file

@ -0,0 +1,108 @@
/* global anchors */
// add anchor links to headers
anchors.options.placement = 'left';
anchors.add('h3');
// Filter UI
var tocElements = document.getElementById('toc')
.getElementsByTagName('li');
document.getElementById('filter-input')
.addEventListener('keyup', function (e) {
var i, element, children;
// enter key
if (e.keyCode === 13) {
// go to the first displayed item in the toc
for (i = 0; i < tocElements.length; i++) {
element = tocElements[i];
if (!element.classList.contains('display-none')) {
location.replace(element.firstChild.href);
return e.preventDefault();
}
}
}
var match = function () {
return true;
};
var value = this.value.toLowerCase();
if (!value.match(/^\s*$/)) {
match = function (element) {
return element.firstChild.innerHTML.toLowerCase().indexOf(value) !== -1;
};
}
for (i = 0; i < tocElements.length; i++) {
element = tocElements[i];
children = Array.from(element.getElementsByTagName('li'));
if (match(element) || children.some(match)) {
element.classList.remove('display-none');
} else {
element.classList.add('display-none');
}
}
});
var toggles = document.getElementsByClassName('toggle-step-sibling');
for (var i = 0; i < toggles.length; i++) {
toggles[i].addEventListener('click', toggleStepSibling);
}
function toggleStepSibling() {
var stepSibling = this.parentNode.parentNode.parentNode.getElementsByClassName('toggle-target')[0];
var klass = 'display-none';
if (stepSibling.classList.contains(klass)) {
stepSibling.classList.remove(klass);
stepSibling.innerHTML = '▾';
} else {
stepSibling.classList.add(klass);
stepSibling.innerHTML = '▸';
}
}
var items = document.getElementsByClassName('toggle-sibling');
for (var j = 0; j < items.length; j++) {
items[j].addEventListener('click', toggleSibling);
}
function toggleSibling() {
var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0];
var icon = this.getElementsByClassName('icon')[0];
var klass = 'display-none';
if (stepSibling.classList.contains(klass)) {
stepSibling.classList.remove(klass);
icon.innerHTML = '▾';
} else {
stepSibling.classList.add(klass);
icon.innerHTML = '▸';
}
}
function showHashTarget(targetId) {
var hashTarget = document.getElementById(targetId);
// new target is hidden
if (hashTarget && hashTarget.offsetHeight === 0 &&
hashTarget.parentNode.parentNode.classList.contains('display-none')) {
hashTarget.parentNode.parentNode.classList.remove('display-none');
}
}
window.addEventListener('hashchange', function() {
showHashTarget(location.hash.substring(1));
});
showHashTarget(location.hash.substring(1));
var toclinks = document.getElementsByClassName('pre-open');
for (var k = 0; k < toclinks.length; k++) {
toclinks[k].addEventListener('mousedown', preOpen, false);
}
function preOpen() {
showHashTarget(this.hash.substring(1));
}

View file

@ -0,0 +1,136 @@
.documentation {
font-family: Helvetica, sans-serif;
color: #666;
line-height: 1.5;
background: #f5f5f5;
}
.black {
color: #666;
}
.bg-white {
background-color: #fff;
}
h4 {
margin: 20px 0 10px 0;
}
.documentation h3 {
color: #000;
}
.border-bottom {
border-color: #ddd;
}
a {
color: #1184CE;
text-decoration: none;
}
.documentation a[href]:hover {
text-decoration: underline;
}
a:hover {
cursor: pointer;
}
.py1-ul li {
padding: 5px 0;
}
.max-height-100 {
max-height: 100%;
}
section:target h3 {
font-weight:700;
}
.documentation td,
.documentation th {
padding: .25rem .25rem;
}
h1:hover .anchorjs-link,
h2:hover .anchorjs-link,
h3:hover .anchorjs-link,
h4:hover .anchorjs-link {
opacity: 1;
}
.fix-3 {
width: 25%;
max-width: 244px;
}
.fix-3 {
width: 25%;
max-width: 244px;
}
@media (min-width: 52em) {
.fix-margin-3 {
margin-left: 25%;
}
}
.pre, pre, code, .code {
font-family: Source Code Pro,Menlo,Consolas,Liberation Mono,monospace;
font-size: 14px;
}
.fill-light {
background: #F9F9F9;
}
.width2 {
width: 1rem;
}
.input {
font-family: inherit;
display: block;
width: 100%;
height: 2rem;
padding: .5rem;
margin-bottom: 1rem;
border: 1px solid #ccc;
font-size: .875rem;
border-radius: 3px;
box-sizing: border-box;
}
table {
border-collapse: collapse;
}
.prose table th,
.prose table td {
text-align: left;
padding:8px;
border:1px solid #ddd;
}
.prose table th:nth-child(1) { border-right: none; }
.prose table th:nth-child(2) { border-left: none; }
.prose table {
border:1px solid #ddd;
}
.prose-big {
font-size: 18px;
line-height: 30px;
}
.quiet {
opacity: 0.7;
}
.minishadow {
box-shadow: 2px 2px 10px #f3f3f3;
}

File diff suppressed because it is too large Load diff

146
documentation/md/API.md Normal file
View file

@ -0,0 +1,146 @@
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
# ePub
Creates a new Book
**Parameters**
- `url` **([string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String) \| [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer))** URL, Path or ArrayBuffer
- `options` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)** to pass to the book
- `options.request` the request function to use
**Examples**
```javascript
ePub("/path/to/book.epub", {})
```
Returns **[Book](#book)** a new Book object
# Book
Creates a new Book
**Parameters**
- `_url` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**
- `options` **[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)**
- `options.requestMethod` **method** a request function to use instead of the default
**Examples**
```javascript
new Book("/path/to/book.epub", {})
```
Returns **[Book](#book)**
## url
## loaded
**Properties**
- `loaded.manifest` **[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)** A child method as property defination
## open
open a url
**Parameters**
- `_url` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)** URL, Path or ArrayBuffer
- `options` **\[[object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)]** to force opening
**Examples**
```javascript
book.open("/path/to/book.epub", { base64: false })
```
Returns **[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)** of when the book has been loaded
## unpack
unpack the contents of the Books packageXml
**Parameters**
- `packageXml` **[document](https://developer.mozilla.org/en-US/docs/Web/JavaScript)** XML Document
## section
Alias for book.spine.get
**Parameters**
- `target` **[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String)**
## renderTo
Sugar to render a book
**Parameters**
- `element`
- `options`
## requestMethod
Switch request methods depending on if book is archived or not
**Parameters**
- `_url`
## unarchive
Unarchive a zipped epub
**Parameters**
- `bookUrl`
- `isBase64`
## isArchivedUrl
Checks if url has a .epub or .zip extension, or is ArrayBuffer (of zip/epub)
**Parameters**
- `bookUrl`
## coverUrl
Get the cover url
## range
Find a DOM Range for a given CFI Range
**Parameters**
- `cfiRange`
# EpubCFI
EPUB CFI spec: <http://www.idpf.org/epub/linking/cfi/epub-cfi.html>
Implements:
- Character Offset: epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)
- Simple Ranges : epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)
Does Not Implement:
- Temporal Offset (~)
- Spatial Offset (@)
- Temporal-Spatial Offset (~ + @)
- Text Location Assertion (\[)
**Parameters**
- `cfiFrom`
- `base`
- `ignoreClass`

View file

@ -3,14 +3,22 @@ var request = require('./request');
var mime = require('../libs/mime/mime'); var mime = require('../libs/mime/mime');
var Path = require('./core').Path; var Path = require('./core').Path;
/**
* Handles Unzipping a requesting files from an Epub Archive
* @class
*/
function Archive() { function Archive() {
this.zip = undefined;
this.checkRequirements(); this.checkRequirements();
this.urlCache = {}; this.urlCache = {};
} }
Archive.prototype.checkRequirements = function(callback){ /**
* Checks to see if JSZip exists in global namspace,
* Requires JSZip if it isn't there
* @private
*/
Archive.prototype.checkRequirements = function(){
try { try {
if (typeof JSZip === 'undefined') { if (typeof JSZip === 'undefined') {
JSZip = require('jszip'); JSZip = require('jszip');
@ -21,10 +29,22 @@ Archive.prototype.checkRequirements = function(callback){
} }
}; };
/**
* Open an archive
* @param {binary} input
* @param {boolean} isBase64 tells JSZip if the input data is base64 encoded
* @return {Promise} zipfile
*/
Archive.prototype.open = function(input, isBase64){ Archive.prototype.open = function(input, isBase64){
return this.zip.loadAsync(input, {"base64": isBase64}); return this.zip.loadAsync(input, {"base64": isBase64});
}; };
/**
* Load and Open an archive
* @param {string} zipUrl
* @param {boolean} isBase64 tells JSZip if the input data is base64 encoded
* @return {Promise} zipfile
*/
Archive.prototype.openUrl = function(zipUrl, isBase64){ Archive.prototype.openUrl = function(zipUrl, isBase64){
return request(zipUrl, "binary") return request(zipUrl, "binary")
.then(function(data){ .then(function(data){
@ -32,6 +52,12 @@ Archive.prototype.openUrl = function(zipUrl, isBase64){
}.bind(this)); }.bind(this));
}; };
/**
* Request
* @param {string} url a url to request from the archive
* @param {[string]} type specify the type of the returned result
* @return {Promise}
*/
Archive.prototype.request = function(url, type){ Archive.prototype.request = function(url, type){
var deferred = new core.defer(); var deferred = new core.defer();
var response; var response;
@ -63,6 +89,13 @@ Archive.prototype.request = function(url, type){
return deferred.promise; return deferred.promise;
}; };
/**
* Handle the response from request
* @private
* @param {any} response
* @param {[string]} type
* @return {any} the parsed result
*/
Archive.prototype.handleResponse = function(response, type){ Archive.prototype.handleResponse = function(response, type){
var r; var r;
@ -87,19 +120,30 @@ Archive.prototype.handleResponse = function(response, type){
return r; return r;
}; };
Archive.prototype.getBlob = function(url, _mimeType){ /**
* Get a Blob from Archive by Url
* @param {string} url
* @param {[string]} mimeType
* @return {Blob}
*/
Archive.prototype.getBlob = function(url, mimeType){
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl); var entry = this.zip.file(decodededUrl);
var mimeType;
if(entry) { if(entry) {
mimeType = _mimeType || mime.lookup(entry.name); mimeType = mimeType || mime.lookup(entry.name);
return entry.async("uint8array").then(function(uint8array) { return entry.async("uint8array").then(function(uint8array) {
return new Blob([uint8array], {type : mimeType}); return new Blob([uint8array], {type : mimeType});
}); });
} }
}; };
/**
* Get Text from Archive by Url
* @param {string} url
* @param {[string]} encoding
* @return {string}
*/
Archive.prototype.getText = function(url, encoding){ Archive.prototype.getText = function(url, encoding){
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl); var entry = this.zip.file(decodededUrl);
@ -111,19 +155,30 @@ Archive.prototype.getText = function(url, encoding){
} }
}; };
Archive.prototype.getBase64 = function(url, _mimeType){ /**
* Get a base64 encoded result from Archive by Url
* @param {string} url
* @param {[string]} mimeType
* @return {string} base64 encoded
*/
Archive.prototype.getBase64 = function(url, mimeType){
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl); var entry = this.zip.file(decodededUrl);
var mimeType;
if(entry) { if(entry) {
mimeType = _mimeType || mime.lookup(entry.name); mimeType = mimeType || mime.lookup(entry.name);
return entry.async("base64").then(function(data) { return entry.async("base64").then(function(data) {
return "data:" + mimeType + ";base64," + data; return "data:" + mimeType + ";base64," + data;
}); });
} }
}; };
/**
* Create a Url from an unarchived item
* @param {string} url
* @param {[object]} options.base64 use base64 encoding or blob url
* @return {Promise} url promise with Url string
*/
Archive.prototype.createUrl = function(url, options){ Archive.prototype.createUrl = function(url, options){
var deferred = new core.defer(); var deferred = new core.defer();
var _URL = window.URL || window.webkitURL || window.mozURL; var _URL = window.URL || window.webkitURL || window.mozURL;
@ -177,6 +232,10 @@ Archive.prototype.createUrl = function(url, options){
return deferred.promise; return deferred.promise;
}; };
/**
* Revoke Temp Url for a achive item
* @param {string} url url of the item in the archive
*/
Archive.prototype.revokeUrl = function(url){ Archive.prototype.revokeUrl = function(url){
var _URL = window.URL || window.webkitURL || window.mozURL; var _URL = window.URL || window.webkitURL || window.mozURL;
var fromCache = this.urlCache[url]; var fromCache = this.urlCache[url];

View file

@ -10,6 +10,7 @@ var Container = require('./container');
var Packaging = require('./packaging'); var Packaging = require('./packaging');
var Navigation = require('./navigation'); var Navigation = require('./navigation');
var Resources = require('./resources'); var Resources = require('./resources');
var PageList = require('./pagelist');
var Rendition = require('./rendition'); var Rendition = require('./rendition');
var Archive = require('./archive'); var Archive = require('./archive');
var request = require('./request'); var request = require('./request');
@ -21,19 +22,24 @@ var CONTAINER_PATH = "META-INF/container.xml";
/** /**
* Creates a new Book * Creates a new Book
* @class * @class
* @param {string} _url * @param {string} url
* @param {object} options * @param {object} options
* @param {method} options.requestMethod a request function to use instead of the default * @param {method} options.requestMethod a request function to use instead of the default
* @param {boolean} [options.requestCredentials=undefined] send the xhr request withCredentials
* @param {object} [options.requestHeaders=undefined] send the xhr request headers
* @param {string} [options.encoding=binary] optional to pass 'binary' or base64' for archived Epubs
* @param {string} [options.replacements=base64] use base64, blobs, or none for replacing assets in archived Epubs
* @returns {Book} * @returns {Book}
* @example new Book("/path/to/book.epub", {}) * @example new Book("/path/to/book.epub", {})
*/ */
function Book(url, options){ function Book(url, options){
this.settings = core.extend(this.settings || {}, { this.settings = core.extend(this.settings || {}, {
requestMethod: this.requestMethod, requestMethod: undefined,
requestCredentials: undefined, requestCredentials: undefined,
encoding: undefined, // optional to pass 'binary' or base64' for archived Epubs requestHeaders: undefined,
base64: true encoding: undefined,
replacements: 'base64'
}); });
core.extend(this.settings, options); core.extend(this.settings, options);
@ -70,6 +76,7 @@ function Book(url, options){
// this.ready = RSVP.hash(this.loaded); // this.ready = RSVP.hash(this.loaded);
/** /**
* @property {promise} ready returns after the book is loaded and parsed * @property {promise} ready returns after the book is loaded and parsed
* @private
*/ */
this.ready = Promise.all([this.loaded.manifest, this.ready = Promise.all([this.loaded.manifest,
this.loaded.spine, this.loaded.spine,
@ -85,6 +92,7 @@ function Book(url, options){
/** /**
* @property {method} request * @property {method} request
* @private
*/ */
this.request = this.settings.requestMethod || request; this.request = this.settings.requestMethod || request;
@ -103,9 +111,27 @@ function Book(url, options){
*/ */
this.navigation = undefined; this.navigation = undefined;
/**
* @property {PageList} pagelist
*/
this.pageList = new PageList();
/**
* @property {Url} url
* @private
*/
this.url = undefined; this.url = undefined;
/**
* @property {Path} path
* @private
*/
this.path = undefined; this.path = undefined;
/**
* @property {boolean} archived
* @private
*/
this.archived = false; this.archived = false;
if(url) { if(url) {
@ -119,7 +145,7 @@ function Book(url, options){
}; };
/** /**
* open a url * Open a epub or url
* @param {string} input URL, Path or ArrayBuffer * @param {string} input URL, Path or ArrayBuffer
* @param {string} [what] to force opening * @param {string} [what] to force opening
* @returns {Promise} of when the book has been loaded * @returns {Promise} of when the book has been loaded
@ -150,6 +176,13 @@ Book.prototype.open = function(input, what){
return opening; return opening;
}; };
/**
* Open an archived epub
* @private
* @param {binary} data
* @param {[string]} encoding
* @return {Promise}
*/
Book.prototype.openEpub = function(data, encoding){ Book.prototype.openEpub = function(data, encoding){
return this.unarchive(data, encoding || this.settings.encoding) return this.unarchive(data, encoding || this.settings.encoding)
.then(function() { .then(function() {
@ -160,6 +193,12 @@ Book.prototype.openEpub = function(data, encoding){
}.bind(this)); }.bind(this));
}; };
/**
* Open the epub container
* @private
* @param {string} url
* @return {string} packagePath
*/
Book.prototype.openContainer = function(url){ Book.prototype.openContainer = function(url){
return this.load(url) return this.load(url)
.then(function(xml) { .then(function(xml) {
@ -168,6 +207,12 @@ Book.prototype.openContainer = function(url){
}.bind(this)); }.bind(this));
}; };
/**
* Open the Open Packaging Format Xml
* @private
* @param {string} url
* @return {Promise}
*/
Book.prototype.openPackaging = function(url){ Book.prototype.openPackaging = function(url){
var packageUrl; var packageUrl;
this.path = new Path(url); this.path = new Path(url);
@ -179,6 +224,11 @@ Book.prototype.openPackaging = function(url){
}.bind(this)); }.bind(this));
}; };
/**
* Load a resource from the Book
* @param {string} path path to the resource to load
* @return {Promise} returns a promise with the requested resource
*/
Book.prototype.load = function (path) { Book.prototype.load = function (path) {
var resolved; var resolved;
if(this.archived) { if(this.archived) {
@ -186,10 +236,16 @@ Book.prototype.load = function (path) {
return this.archive.request(resolved); return this.archive.request(resolved);
} else { } else {
resolved = this.resolve(path); resolved = this.resolve(path);
return this.request(resolved, null, this.requestCredentials, this.requestHeaders); return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders);
} }
}; };
/**
* Resolve a path to it's absolute position in the Book
* @param {string} path
* @param {[boolean]} absolute force resolving the full URL
* @return {string} the resolved path string
*/
Book.prototype.resolve = function (path, absolute) { Book.prototype.resolve = function (path, absolute) {
var resolved = path; var resolved = path;
var isAbsolute = (path.indexOf('://') > -1); var isAbsolute = (path.indexOf('://') > -1);
@ -209,6 +265,12 @@ Book.prototype.resolve = function (path, absolute) {
return resolved; return resolved;
} }
/**
* Determine the type of they input passed to open
* @private
* @param {string} input
* @return {string} binary | directory | epub | opf
*/
Book.prototype.determineType = function(input) { Book.prototype.determineType = function(input) {
var url; var url;
var path; var path;
@ -238,6 +300,7 @@ Book.prototype.determineType = function(input) {
/** /**
* unpack the contents of the Books packageXml * unpack the contents of the Books packageXml
* @private
* @param {document} packageXml XML Document * @param {document} packageXml XML Document
*/ */
Book.prototype.unpack = function(opf){ Book.prototype.unpack = function(opf){
@ -248,7 +311,7 @@ Book.prototype.unpack = function(opf){
this.resources = new Resources(this.package.manifest, { this.resources = new Resources(this.package.manifest, {
archive: this.archive, archive: this.archive,
resolver: this.resolve.bind(this), resolver: this.resolve.bind(this),
base64: this.settings.base64 replacements: this.settings.replacements
}); });
this.loadNavigation(this.package).then(function(toc){ this.loadNavigation(this.package).then(function(toc){
@ -264,6 +327,7 @@ Book.prototype.unpack = function(opf){
this.loading.spine.resolve(this.spine); this.loading.spine.resolve(this.spine);
this.loading.cover.resolve(this.cover); this.loading.cover.resolve(this.cover);
this.loading.resources.resolve(this.resources); this.loading.resources.resolve(this.resources);
this.loading.pageList.resolve(this.pageList);
this.isOpen = true; this.isOpen = true;
@ -279,6 +343,11 @@ Book.prototype.unpack = function(opf){
}; };
/**
* Load Navigation and PageList from package
* @private
* @param {document} opf XML Document
*/
Book.prototype.loadNavigation = function(opf){ Book.prototype.loadNavigation = function(opf){
var navPath = opf.navPath || opf.ncxPath; var navPath = opf.navPath || opf.ncxPath;
@ -289,6 +358,7 @@ Book.prototype.loadNavigation = function(opf){
return this.load(navPath, 'xml') return this.load(navPath, 'xml')
.then(function(xml) { .then(function(xml) {
this.navigation = new Navigation(xml); this.navigation = new Navigation(xml);
this.pageList = new PageList(xml);
}.bind(this)); }.bind(this));
}; };
@ -302,6 +372,9 @@ Book.prototype.section = function(target) {
/** /**
* Sugar to render a book * Sugar to render a book
* @param {element} element element to add the views to
* @param {[object]} options
* @return {Rendition}
*/ */
Book.prototype.renderTo = function(element, options) { Book.prototype.renderTo = function(element, options) {
// var renderMethod = (options && options.method) ? // var renderMethod = (options && options.method) ?
@ -314,31 +387,44 @@ Book.prototype.renderTo = function(element, options) {
return this.rendition; return this.rendition;
}; };
/**
Book.prototype.setRequestCredentials = function(_credentials) { * Set if request should use withCredentials
this.requestCredentials = _credentials; * @param {boolean} credentials
*/
Book.prototype.setRequestCredentials = function(credentials) {
this.settings.requestCredentials = credentials;
}; };
Book.prototype.setRequestHeaders = function(_headers) { /**
this.requestHeaders = _headers; * Set headers request should use
* @param {object} headers
*/
Book.prototype.setRequestHeaders = function(headers) {
this.settings.requestHeaders = headers;
}; };
/** /**
* Unarchive a zipped epub * Unarchive a zipped epub
* @private
* @param {binary} input epub data
* @param {[string]} encoding
* @return {Archive}
*/ */
Book.prototype.unarchive = function(bookUrl, encoding){ Book.prototype.unarchive = function(input, encoding){
this.archive = new Archive(); this.archive = new Archive();
return this.archive.open(bookUrl, encoding); return this.archive.open(input, encoding);
}; };
/** /**
* Get the cover url * Get the cover url
* @return {string} coverUrl
*/ */
Book.prototype.coverUrl = function(){ Book.prototype.coverUrl = function(){
var retrieved = this.loaded.cover. var retrieved = this.loaded.cover.
then(function(url) { then(function(url) {
if(this.archived) { if(this.archived) {
return this.archive.createUrl(this.cover); // return this.archive.createUrl(this.cover);
return this.resources.get(this.cover);
}else{ }else{
return this.cover; return this.cover;
} }
@ -349,6 +435,11 @@ Book.prototype.coverUrl = function(){
return retrieved; return retrieved;
}; };
/**
* load replacement urls
* @private
* @return {Promise} completed loading urls
*/
Book.prototype.replacements = function(){ Book.prototype.replacements = function(){
this.spine.hooks.serialize.register(function(output, section) { this.spine.hooks.serialize.register(function(output, section) {
section.output = this.resources.substitute(output, section.url); section.output = this.resources.substitute(output, section.url);
@ -362,6 +453,8 @@ Book.prototype.replacements = function(){
/** /**
* Find a DOM Range for a given CFI Range * Find a DOM Range for a given CFI Range
* @param {EpubCFI} cfiRange a epub cfi range
* @return {Range}
*/ */
Book.prototype.range = function(cfiRange) { Book.prototype.range = function(cfiRange) {
var cfi = new EpubCFI(cfiRange); var cfi = new EpubCFI(cfiRange);

View file

@ -2,13 +2,21 @@ var path = require('path');
var core = require('./core'); var core = require('./core');
var EpubCFI = require('./epubcfi'); var EpubCFI = require('./epubcfi');
/**
* Handles Parsing and Accessing an Epub Container
* @class
* @param {[document]} containerDocument xml document
*/
function Container(containerDocument) { function Container(containerDocument) {
if (containerDocument) { if (containerDocument) {
this.parse(containerDocument); this.parse(containerDocument);
} }
}; };
/**
* Parse the Container XML
* @param {document} containerDocument
*/
Container.prototype.parse = function(containerDocument){ Container.prototype.parse = function(containerDocument){
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/> //-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
var rootfile, fullpath, folder, encoding; var rootfile, fullpath, folder, encoding;

View file

@ -592,7 +592,7 @@ function defer() {
} }
// Handle IE not supporting namespaced epub:type in querySelector // Handle IE not supporting namespaced epub:type in querySelector
if(!query || query.length === 0) { if(!query || query.length === 0) {
query = core.qsa(html, element); query = this.qsa(html, element);
for (var i = 0; i < query.length; i++) { for (var i = 0; i < query.length; i++) {
if(query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type) { if(query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type) {
return query[i]; return query[i];

View file

@ -7,7 +7,6 @@ var Contents = require('./contents');
* Creates a new Book * Creates a new Book
* @param {string|ArrayBuffer} url URL, Path or ArrayBuffer * @param {string|ArrayBuffer} url URL, Path or ArrayBuffer
* @param {object} options to pass to the book * @param {object} options to pass to the book
* @param options.requestMethod the request function to use
* @returns {Book} a new Book object * @returns {Book} a new Book object
* @example ePub("/path/to/book.epub", {}) * @example ePub("/path/to/book.epub", {})
*/ */

View file

@ -1,17 +1,19 @@
//-- Hooks allow for injecting functions that must all complete in order before finishing /**
// They will execute in parallel but all must finish before continuing * Hooks allow for injecting functions that must all complete in order before finishing
// Functions may return a promise if they are asycn. * They will execute in parallel but all must finish before continuing
* Functions may return a promise if they are asycn.
// this.content = new EPUBJS.Hook(); * @param {any} context scope of this
// this.content.register(function(){}); * @example this.content = new EPUBJS.Hook(this);
// this.content.trigger(args).then(function(){}); */
function Hook(context){ function Hook(context){
this.context = context || this; this.context = context || this;
this.hooks = []; this.hooks = [];
}; };
// Adds a function to be run before a hook completes /**
* Adds a function to be run before a hook completes
* @example this.content.register(function(){...});
*/
Hook.prototype.register = function(){ Hook.prototype.register = function(){
for(var i = 0; i < arguments.length; ++i) { for(var i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === "function") { if (typeof arguments[i] === "function") {
@ -25,7 +27,10 @@ Hook.prototype.register = function(){
} }
}; };
// Triggers a hook to run all functions /**
* Triggers a hook to run all functions
* @example this.content.trigger(args).then(function(){...});
*/
Hook.prototype.trigger = function(){ Hook.prototype.trigger = function(){
var args = arguments; var args = arguments;
var context = this.context; var context = this.context;

View file

@ -1,9 +1,18 @@
var core = require('./core'); var core = require('./core');
/**
* Figures out the CSS to apply for a layout
* @class
* @param {object} settings
* @param {[string=reflowable]} settings.layout
* @param {[string]} settings.spread
* @param {[int=800]} settings.minSpreadWidth
* @param {[boolean=false]} settings.evenSpreads
*/
function Layout(settings){ function Layout(settings){
this.name = settings.layout || "reflowable"; this.name = settings.layout || "reflowable";
this._spread = (settings.spread === "none") ? false : true; this._spread = (settings.spread === "none") ? false : true;
this._minSpreadWidth = settings.spread || 800; this._minSpreadWidth = settings.minSpreadWidth || 800;
this._evenSpreads = settings.evenSpreads || false; this._evenSpreads = settings.evenSpreads || false;
if (settings.flow === "scrolled-continuous" || if (settings.flow === "scrolled-continuous" ||
@ -24,12 +33,20 @@ function Layout(settings){
this.divisor = 1; this.divisor = 1;
}; };
// paginated | scrolled /**
* Switch the flow between paginated and scrolled
* @param {string} flow paginated | scrolled
*/
Layout.prototype.flow = function(flow) { Layout.prototype.flow = function(flow) {
this._flow = (flow === "paginated") ? "paginated" : "scrolled"; this._flow = (flow === "paginated") ? "paginated" : "scrolled";
} }
// true | false /**
* Switch between using spreads or not, and set the
* width at which they switch to single.
* @param {string} spread true | false
* @param {boolean} min integer in pixels
*/
Layout.prototype.spread = function(spread, min) { Layout.prototype.spread = function(spread, min) {
this._spread = (spread === "none") ? false : true; this._spread = (spread === "none") ? false : true;
@ -39,6 +56,12 @@ Layout.prototype.spread = function(spread, min) {
} }
} }
/**
* Calculate the dimensions of the pagination
* @param {number} _width [description]
* @param {number} _height [description]
* @param {number} _gap [description]
*/
Layout.prototype.calculate = function(_width, _height, _gap){ Layout.prototype.calculate = function(_width, _height, _gap){
var divisor = 1; var divisor = 1;
@ -93,6 +116,11 @@ Layout.prototype.calculate = function(_width, _height, _gap){
this.divisor = divisor; this.divisor = divisor;
}; };
/**
* Apply Css to a Document
* @param {Contents} contents
* @return {[Promise]}
*/
Layout.prototype.format = function(contents){ Layout.prototype.format = function(contents){
var formating; var formating;
@ -107,6 +135,12 @@ Layout.prototype.format = function(contents){
return formating; // might be a promise in some View Managers return formating; // might be a promise in some View Managers
}; };
/**
* Count number of pages
* @param {number} totalWidth
* @return {number} spreads
* @return {number} pages
*/
Layout.prototype.count = function(totalWidth) { Layout.prototype.count = function(totalWidth) {
// var totalWidth = contents.scrollWidth(); // var totalWidth = contents.scrollWidth();
var spreads = Math.ceil( totalWidth / this.spreadWidth); var spreads = Math.ceil( totalWidth / this.spreadWidth);

View file

@ -3,6 +3,11 @@ var Queue = require('./queue');
var EpubCFI = require('./epubcfi'); var EpubCFI = require('./epubcfi');
var EventEmitter = require('event-emitter'); var EventEmitter = require('event-emitter');
/**
* Find Locations for a Book
* @param {Spine} spine
* @param {request} request
*/
function Locations(spine, request) { function Locations(spine, request) {
this.spine = spine; this.spine = spine;
this.request = request; this.request = request;
@ -19,7 +24,11 @@ function Locations(spine, request) {
}; };
// Load all of sections in the book /**
* Load all of sections in the book to generate locations
* @param {int} chars how many chars to split on
* @return {object} locations
*/
Locations.prototype.generate = function(chars) { Locations.prototype.generate = function(chars) {
if (chars) { if (chars) {

View file

@ -1,6 +1,10 @@
var core = require('./core'); var core = require('./core');
var path = require('path'); var path = require('path');
/**
* Navigation Parser
* @param {document} xml navigation html / xhtml / ncx
*/
function Navigation(xml){ function Navigation(xml){
this.toc = []; this.toc = [];
this.tocByHref = {}; this.tocByHref = {};
@ -11,6 +15,10 @@ function Navigation(xml){
} }
}; };
/**
* Parse out the navigation items
* @param {document} xml navigation html / xhtml / ncx
*/
Navigation.prototype.parse = function(xml) { Navigation.prototype.parse = function(xml) {
var html = core.qs(xml, "html"); var html = core.qs(xml, "html");
var ncx = core.qs(xml, "ncx"); var ncx = core.qs(xml, "ncx");
@ -24,6 +32,11 @@ Navigation.prototype.parse = function(xml) {
this.unpack(this.toc); this.unpack(this.toc);
}; };
/**
* Unpack navigation items
* @private
* @param {array} toc
*/
Navigation.prototype.unpack = function(toc) { Navigation.prototype.unpack = function(toc) {
var item; var item;
@ -35,7 +48,11 @@ Navigation.prototype.unpack = function(toc) {
}; };
// Get an item from the navigation /**
* Get an item from the navigation
* @param {string} target
* @return {object} navItems
*/
Navigation.prototype.get = function(target) { Navigation.prototype.get = function(target) {
var index; var index;
@ -52,9 +69,14 @@ Navigation.prototype.get = function(target) {
return this.toc[index]; return this.toc[index];
}; };
Navigation.prototype.parseNav = function(navHtml, spineIndexByURL, bookSpine){ /**
* Parse from a Epub > 3.0 Nav
* @private
* @param {document} navHtml
* @return {array} navigation list
*/
Navigation.prototype.parseNav = function(navHtml){
var navElement = core.querySelectorByType(navHtml, "nav", "toc"); var navElement = core.querySelectorByType(navHtml, "nav", "toc");
// var navItems = navElement ? navElement.querySelectorAll("ol li") : [];
var navItems = navElement ? core.qsa(navElement, "li") : []; var navItems = navElement ? core.qsa(navElement, "li") : [];
var length = navItems.length; var length = navItems.length;
var i; var i;
@ -65,7 +87,7 @@ Navigation.prototype.parseNav = function(navHtml, spineIndexByURL, bookSpine){
if(!navItems || length === 0) return list; if(!navItems || length === 0) return list;
for (i = 0; i < length; ++i) { for (i = 0; i < length; ++i) {
item = this.navItem(navItems[i], spineIndexByURL, bookSpine); item = this.navItem(navItems[i]);
toc[item.id] = item; toc[item.id] = item;
if(!item.parent) { if(!item.parent) {
list.push(item); list.push(item);
@ -78,38 +100,25 @@ Navigation.prototype.parseNav = function(navHtml, spineIndexByURL, bookSpine){
return list; return list;
}; };
Navigation.prototype.navItem = function(item, spineIndexByURL, bookSpine){ /**
* Create a navItem
* @private
* @param {element} item
* @return {object} navItem
*/
Navigation.prototype.navItem = function(item){
var id = item.getAttribute('id') || false, var id = item.getAttribute('id') || false,
// content = item.querySelector("a, span"),
content = core.qs(item, "a"), content = core.qs(item, "a"),
src = content.getAttribute('href') || '', src = content.getAttribute('href') || '',
text = content.textContent || "", text = content.textContent || "",
// split = src.split("#"),
// baseUrl = split[0],
// spinePos = spineIndexByURL[baseUrl],
// spineItem = bookSpine[spinePos],
subitems = [], subitems = [],
parentNode = item.parentNode, parentNode = item.parentNode,
parent; parent;
// cfi = spineItem ? spineItem.cfi : '';
if(parentNode && parentNode.nodeName === "navPoint") { if(parentNode && parentNode.nodeName === "navPoint") {
parent = parentNode.getAttribute('id'); parent = parentNode.getAttribute('id');
} }
/*
if(!id) {
if(spinePos) {
spineItem = bookSpine[spinePos];
id = spineItem.id;
cfi = spineItem.cfi;
} else {
id = 'epubjs-autogen-toc-id-' + EPUBJS.core.uuid();
item.setAttribute('id', id);
}
}
*/
return { return {
"id": id, "id": id,
"href": src, "href": src,
@ -119,8 +128,13 @@ Navigation.prototype.navItem = function(item, spineIndexByURL, bookSpine){
}; };
}; };
Navigation.prototype.parseNcx = function(tocXml, spineIndexByURL, bookSpine){ /**
// var navPoints = tocXml.querySelectorAll("navMap navPoint"); * Parse from a Epub > 3.0 NC
* @private
* @param {document} navHtml
* @return {array} navigation list
*/
Navigation.prototype.parseNcx = function(tocXml){
var navPoints = core.qsa(tocXml, "navPoint"); var navPoints = core.qsa(tocXml, "navPoint");
var length = navPoints.length; var length = navPoints.length;
var i; var i;
@ -131,7 +145,7 @@ Navigation.prototype.parseNcx = function(tocXml, spineIndexByURL, bookSpine){
if(!navPoints || length === 0) return list; if(!navPoints || length === 0) return list;
for (i = 0; i < length; ++i) { for (i = 0; i < length; ++i) {
item = this.ncxItem(navPoints[i], spineIndexByURL, bookSpine); item = this.ncxItem(navPoints[i]);
toc[item.id] = item; toc[item.id] = item;
if(!item.parent) { if(!item.parent) {
list.push(item); list.push(item);
@ -144,39 +158,26 @@ Navigation.prototype.parseNcx = function(tocXml, spineIndexByURL, bookSpine){
return list; return list;
}; };
Navigation.prototype.ncxItem = function(item, spineIndexByURL, bookSpine){ /**
* Create a ncxItem
* @private
* @param {element} item
* @return {object} ncxItem
*/
Navigation.prototype.ncxItem = function(item){
var id = item.getAttribute('id') || false, var id = item.getAttribute('id') || false,
// content = item.querySelector("content"),
content = core.qs(item, "content"), content = core.qs(item, "content"),
src = content.getAttribute('src'), src = content.getAttribute('src'),
// navLabel = item.querySelector("navLabel"),
navLabel = core.qs(item, "navLabel"), navLabel = core.qs(item, "navLabel"),
text = navLabel.textContent ? navLabel.textContent : "", text = navLabel.textContent ? navLabel.textContent : "",
// split = src.split("#"),
// baseUrl = split[0],
// spinePos = spineIndexByURL[baseUrl],
// spineItem = bookSpine[spinePos],
subitems = [], subitems = [],
parentNode = item.parentNode, parentNode = item.parentNode,
parent; parent;
// cfi = spineItem ? spineItem.cfi : '';
if(parentNode && parentNode.nodeName === "navPoint") { if(parentNode && parentNode.nodeName === "navPoint") {
parent = parentNode.getAttribute('id'); parent = parentNode.getAttribute('id');
} }
/*
if(!id) {
if(spinePos) {
spineItem = bookSpine[spinePos];
id = spineItem.id;
cfi = spineItem.cfi;
} else {
id = 'epubjs-autogen-toc-id-' + EPUBJS.core.uuid();
item.setAttribute('id', id);
}
}
*/
return { return {
"id": id, "id": id,

View file

@ -2,13 +2,22 @@ var path = require('path');
var core = require('./core'); var core = require('./core');
var EpubCFI = require('./epubcfi'); var EpubCFI = require('./epubcfi');
/**
* Open Packaging Format Parser
* @class
* @param {document} packageDocument OPF XML
*/
function Packaging(packageDocument) { function Packaging(packageDocument) {
if (packageDocument) { if (packageDocument) {
this.parse(packageDocument); this.parse(packageDocument);
} }
}; };
/**
* Parse OPF XML
* @param {document} packageDocument OPF XML
* @return {object} parsed package parts
*/
Packaging.prototype.parse = function(packageDocument){ Packaging.prototype.parse = function(packageDocument){
var metadataNode, manifestNode, spineNode; var metadataNode, manifestNode, spineNode;
@ -60,6 +69,12 @@ Packaging.prototype.parse = function(packageDocument){
}; };
}; };
/**
* Parse Metadata
* @private
* @param {document} xml
* @return {object} metadata
*/
Packaging.prototype.parseMetadata = function(xml){ Packaging.prototype.parseMetadata = function(xml){
var metadata = {}; var metadata = {};
@ -86,6 +101,12 @@ Packaging.prototype.parseMetadata = function(xml){
return metadata; return metadata;
}; };
/**
* Parse Manifest
* @private
* @param {document} manifestXml
* @return {object} manifest
*/
Packaging.prototype.parseManifest = function(manifestXml){ Packaging.prototype.parseManifest = function(manifestXml){
var manifest = {}; var manifest = {};
@ -114,6 +135,12 @@ Packaging.prototype.parseManifest = function(manifestXml){
}; };
/**
* Parse Spine
* @param {document} spineXml
* @param {Packaging.manifest} manifest
* @return {object} spine
*/
Packaging.prototype.parseSpine = function(spineXml, manifest){ Packaging.prototype.parseSpine = function(spineXml, manifest){
var spine = []; var spine = [];
@ -148,6 +175,7 @@ Packaging.prototype.parseSpine = function(spineXml, manifest){
/** /**
* Find TOC NAV * Find TOC NAV
* @private
*/ */
Packaging.prototype.findNavPath = function(manifestNode){ Packaging.prototype.findNavPath = function(manifestNode){
// Find item with property 'nav' // Find item with property 'nav'
@ -160,6 +188,7 @@ Packaging.prototype.findNavPath = function(manifestNode){
/** /**
* Find TOC NCX * Find TOC NCX
* media-type="application/x-dtbncx+xml" href="toc.ncx" * media-type="application/x-dtbncx+xml" href="toc.ncx"
* @private
*/ */
Packaging.prototype.findNcxPath = function(manifestNode, spineNode){ Packaging.prototype.findNcxPath = function(manifestNode, spineNode){
// var node = manifestNode.querySelector("item[media-type='application/x-dtbncx+xml']"); // var node = manifestNode.querySelector("item[media-type='application/x-dtbncx+xml']");
@ -180,8 +209,13 @@ Packaging.prototype.findNcxPath = function(manifestNode, spineNode){
return node ? node.getAttribute('href') : false; return node ? node.getAttribute('href') : false;
}; };
//-- Find Cover: <item properties="cover-image" id="ci" href="cover.svg" media-type="image/svg+xml" /> /**
//-- Fallback for Epub 2.0 * Find the Cover Path
* <item properties="cover-image" id="ci" href="cover.svg" media-type="image/svg+xml" />
* Fallback for Epub 2.0
* @param {document} packageXml
* @return {string} href
*/
Packaging.prototype.findCoverPath = function(packageXml){ Packaging.prototype.findCoverPath = function(packageXml){
var pkg = core.qs(packageXml, "package"); var pkg = core.qs(packageXml, "package");
var epubVersion = pkg.getAttribute('version'); var epubVersion = pkg.getAttribute('version');
@ -205,6 +239,13 @@ Packaging.prototype.findCoverPath = function(packageXml){
} }
}; };
/**
* Get text of a namespaced element
* @private
* @param {document} xml
* @param {string} tag
* @return {string} text
*/
Packaging.prototype.getElementText = function(xml, tag){ Packaging.prototype.getElementText = function(xml, tag){
var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag), var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag),
el; el;
@ -221,6 +262,13 @@ Packaging.prototype.getElementText = function(xml, tag){
}; };
/**
* Get text by property
* @private
* @param {document} xml
* @param {string} property
* @return {string} text
*/
Packaging.prototype.getPropertyText = function(xml, property){ Packaging.prototype.getPropertyText = function(xml, property){
var el = core.qsp(xml, "meta", {"property":property}); var el = core.qsp(xml, "meta", {"property":property});

249
src/pagelist.js Normal file
View file

@ -0,0 +1,249 @@
var EpubCFI = require('./epubcfi');
var core = require('./core');
/**
* Page List Parser
* @param {[document]} xml
*/
function PageList(xml) {
this.pages = [];
this.locations = [];
this.epubcfi = new EpubCFI();
if (xml) {
this.pageList = this.parse(xml);
}
if(this.pageList && this.pageList.length) {
this.process(this.pageList);
}
};
/**
* Parse PageList Xml
* @param {document} xml
*/
PageList.prototype.parse = function(xml) {
var html = core.qs(xml, "html");
// var ncx = core.qs(xml, "ncx");
if(html) {
this.toc = this.parseNav(xml);
} else if(ncx){ // Not supported
// this.toc = this.parseNcx(xml);
return;
}
};
/**
* Parse a Nav PageList
* @private
* @param {document} navHtml
* @return {PageList.item[]} list
*/
PageList.prototype.parseNav = function(navHtml){
var navElement = core.querySelectorByType(navHtml, "nav", "page-list");
var navItems = navElement ? core.qsa(navElement, "li") : [];
var length = navItems.length;
var i;
var toc = {};
var list = [];
var item;
if(!navItems || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.item(navItems[i]);
list.push(item);
}
return list;
};
/**
* Page List Item
* @private
* @param {object} item
* @return {object} pageListItem
*/
PageList.prototype.item = function(item){
var id = item.getAttribute('id') || false,
content = core.qs(item, "a"),
href = content.getAttribute('href') || '',
text = content.textContent || "",
page = parseInt(text),
isCfi = href.indexOf("epubcfi"),
split,
packageUrl,
cfi;
if(isCfi != -1) {
split = href.split("#");
packageUrl = split[0];
cfi = split.length > 1 ? split[1] : false;
return {
"cfi" : cfi,
"href" : href,
"packageUrl" : packageUrl,
"page" : page
};
} else {
return {
"href" : href,
"page" : page
};
}
};
/**
* Process pageList items
* @private
* @param {array} pageList
*/
PageList.prototype.process = function(pageList){
pageList.forEach(function(item){
this.pages.push(item.page);
if (item.cfi) {
this.locations.push(item.cfi);
}
}, this);
this.firstPage = parseInt(this.pages[0]);
this.lastPage = parseInt(this.pages[this.pages.length-1]);
this.totalPages = this.lastPage - this.firstPage;
};
/**
* Replace HREFs with CFI
* TODO: implement getting CFI from Href
*/
PageList.prototype.addCFIs = function() {
this.pageList.forEach(function(pg){
if(!pg.cfi) {
// epubcfi.generateCfiFromHref(pg.href, book).then(function(cfi){
// pg.cfi = cfi;
// pg.packageUrl = book.settings.packageUrl;
// });
}
});
}
/*
EPUBJS.EpubCFI.prototype.generateCfiFromHref = function(href, book) {
var uri = EPUBJS.core.uri(href);
var path = uri.path;
var fragment = uri.fragment;
var spinePos = book.spineIndexByURL[path];
var loaded;
var deferred = new RSVP.defer();
var epubcfi = new EPUBJS.EpubCFI();
var spineItem;
if(typeof spinePos !== "undefined"){
spineItem = book.spine[spinePos];
loaded = book.loadXml(spineItem.url);
loaded.then(function(doc){
var element = doc.getElementById(fragment);
var cfi;
cfi = epubcfi.generateCfiFromElement(element, spineItem.cfiBase);
deferred.resolve(cfi);
});
}
return deferred.promise;
};
*/
/**
* Get a PageList result from a EpubCFI
* @param {string} cfi EpubCFI String
* @return {string} page
*/
PageList.prototype.pageFromCfi = function(cfi){
var pg = -1;
// Check if the pageList has not been set yet
if(this.locations.length === 0) {
return -1;
}
// TODO: check if CFI is valid?
// check if the cfi is in the location list
// var index = this.locations.indexOf(cfi);
var index = core.indexOfSorted(cfi, this.locations, this.epubcfi.compare);
if(index != -1) {
pg = this.pages[index];
} else {
// Otherwise add it to the list of locations
// Insert it in the correct position in the locations page
//index = EPUBJS.core.insert(cfi, this.locations, this.epubcfi.compare);
index = EPUBJS.core.locationOf(cfi, this.locations, this.epubcfi.compare);
// Get the page at the location just before the new one, or return the first
pg = index-1 >= 0 ? this.pages[index-1] : this.pages[0];
if(pg !== undefined) {
// Add the new page in so that the locations and page array match up
//this.pages.splice(index, 0, pg);
} else {
pg = -1;
}
}
return pg;
};
/**
* Get an EpubCFI from a Page List Item
* @param {string} pg
* @return {string} cfi
*/
PageList.prototype.cfiFromPage = function(pg){
var cfi = -1;
// check that pg is an int
if(typeof pg != "number"){
pg = parseInt(pg);
}
// check if the cfi is in the page list
// Pages could be unsorted.
var index = this.pages.indexOf(pg);
if(index != -1) {
cfi = this.locations[index];
}
// TODO: handle pages not in the list
return cfi;
};
/**
* Get a Page from Book percentage
* @param {number} percent
* @return {string} page
*/
PageList.prototype.pageFromPercentage = function(percent){
var pg = Math.round(this.totalPages * percent);
return pg;
};
/**
* Returns a value between 0 - 1 corresponding to the location of a page
* @param {int} pg the page
* @return {number} percentage
*/
PageList.prototype.percentageFromPage = function(pg){
var percentage = (pg - this.firstPage) / this.totalPages;
return Math.round(percentage * 1000) / 1000;
};
/**
* Returns a value between 0 - 1 corresponding to the location of a cfi
* @param {string} cfi EpubCFI String
* @return {number} percentage
*/
PageList.prototype.percentageFromCfi = function(cfi){
var pg = this.pageFromCfi(cfi);
var percentage = this.percentageFromPage(pg);
return percentage;
};
module.exports = PageList;

View file

@ -1,90 +0,0 @@
var path = require('path');
var core = require('./core');
var EpubCFI = require('./epubcfi');
function Parser(){};
/*
Parser.prototype.querySelectorText = function(xml, q){
var el = xml.querySelector(q);
if(el && el.childNodes.length){
return el.childNodes[0].nodeValue;
}
return '';
};
*/
Parser.prototype.querySelectorByType = function(html, element, type){
var query;
if (typeof html.querySelector != "undefined") {
query = html.querySelector(element+'[*|type="'+type+'"]');
}
// Handle IE not supporting namespaced epub:type in querySelector
if(!query || query.length === 0) {
query = core.qsa(html, element);
for (var i = 0; i < query.length; i++) {
if(query[i].getAttributeNS("http://www.idpf.org/2007/ops", "type") === type) {
return query[i];
}
}
} else {
return query;
}
};
Parser.prototype.pageList = function(navHtml, spineIndexByURL, bookSpine){
var navElement = this.querySelectorByType(navHtml, "nav", "page-list");
// var navItems = navElement ? navElement.querySelectorAll("ol li") : [];
var navItems = navElement ? core.qsa(navElement, "li") : [];
var length = navItems.length;
var i;
var toc = {};
var list = [];
var item;
if(!navItems || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.pageListItem(navItems[i], spineIndexByURL, bookSpine);
list.push(item);
}
return list;
};
Parser.prototype.pageListItem = function(item, spineIndexByURL, bookSpine){
var id = item.getAttribute('id') || false,
// content = item.querySelector("a"),
content = core.qs(item, "a"),
href = content.getAttribute('href') || '',
text = content.textContent || "",
page = parseInt(text),
isCfi = href.indexOf("epubcfi"),
split,
packageUrl,
cfi;
if(isCfi != -1) {
split = href.split("#");
packageUrl = split[0];
cfi = split.length > 1 ? split[1] : false;
return {
"cfi" : cfi,
"href" : href,
"packageUrl" : packageUrl,
"page" : page
};
} else {
return {
"href" : href,
"page" : page
};
}
};
module.exports = Parser;

View file

@ -1,14 +1,22 @@
var core = require('./core'); var core = require('./core');
function Queue(_context){ /**
* Queue for handling tasks one at a time
* @class
* @param {scope} context what this will resolve to in the tasks
*/
function Queue(context){
this._q = []; this._q = [];
this.context = _context; this.context = context;
this.tick = core.requestAnimationFrame; this.tick = core.requestAnimationFrame;
this.running = false; this.running = false;
this.paused = false; this.paused = false;
}; };
// Add an item to the queue /**
* Add an item to the queue
* @return {Promise}
*/
Queue.prototype.enqueue = function() { Queue.prototype.enqueue = function() {
var deferred, promise; var deferred, promise;
var queued; var queued;
@ -56,7 +64,10 @@ Queue.prototype.enqueue = function() {
return queued.promise; return queued.promise;
}; };
// Run one item /**
* Run one item
* @return {Promise}
*/
Queue.prototype.dequeue = function(){ Queue.prototype.dequeue = function(){
var inwait, task, result; var inwait, task, result;
@ -101,8 +112,10 @@ Queue.prototype.dump = function(){
} }
}; };
// Run all sequentially, at convince /**
* Run all tasks sequentially, at convince
* @return {Promise}
*/
Queue.prototype.run = function(){ Queue.prototype.run = function(){
if(!this.running){ if(!this.running){
@ -134,7 +147,10 @@ Queue.prototype.run = function(){
return this.defered.promise; return this.defered.promise;
}; };
// Flush all, as quickly as possible /**
* Flush all, as quickly as possible
* @return {Promise}
*/
Queue.prototype.flush = function(){ Queue.prototype.flush = function(){
if(this.running){ if(this.running){
@ -153,21 +169,38 @@ Queue.prototype.flush = function(){
}; };
// Clear all items in wait /**
* Clear all items in wait
*/
Queue.prototype.clear = function(){ Queue.prototype.clear = function(){
this._q = []; this._q = [];
this.running = false; this.running = false;
}; };
/**
* Get the number of tasks in the queue
* @return {int} tasks
*/
Queue.prototype.length = function(){ Queue.prototype.length = function(){
return this._q.length; return this._q.length;
}; };
/**
* Pause a running queue
*/
Queue.prototype.pause = function(){ Queue.prototype.pause = function(){
this.paused = true; this.paused = true;
}; };
// Create a new task from a callback /**
* Create a new task from a callback
* @class
* @private
* @param {function} task
* @param {array} args
* @param {scope} context
* @return {function} task
*/
function Task(task, args, context){ function Task(task, args, context){
return function(){ return function(){

View file

@ -9,6 +9,20 @@ var Layout = require('./layout');
var Mapping = require('./mapping'); var Mapping = require('./mapping');
var Path = require('./core').Path; var Path = require('./core').Path;
/**
* [Rendition description]
* @class
* @param {Book} book
* @param {object} options
* @param {int} options.width
* @param {int} options.height
* @param {string} options.ignoreClass
* @param {string} options.manager
* @param {string} options.view
* @param {string} options.layout
* @param {string} options.spread
* @param {int} options.minSpreadWidth overridden by spread: none (never) / both (always)
*/
function Rendition(book, options) { function Rendition(book, options) {
this.settings = core.extend(this.settings || {}, { this.settings = core.extend(this.settings || {}, {
@ -20,8 +34,7 @@ function Rendition(book, options) {
flow: null, flow: null,
layout: null, layout: null,
spread: null, spread: null,
minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always), minSpreadWidth: 800
useBase64: true
}); });
core.extend(this.settings, options); core.extend(this.settings, options);
@ -34,10 +47,17 @@ function Rendition(book, options) {
this.views = null; this.views = null;
//-- Adds Hook methods to the Rendition prototype /**
* Adds Hook methods to the Rendition prototype
* @property {Hook} hooks
*/
this.hooks = {}; this.hooks = {};
this.hooks.display = new Hook(this); this.hooks.display = new Hook(this);
this.hooks.serialize = new Hook(this); this.hooks.serialize = new Hook(this);
/**
* @property {method} hooks.content
* @type {Hook}
*/
this.hooks.content = new Hook(this); this.hooks.content = new Hook(this);
this.hooks.layout = new Hook(this); this.hooks.layout = new Hook(this);
this.hooks.render = new Hook(this); this.hooks.render = new Hook(this);
@ -55,15 +75,24 @@ function Rendition(book, options) {
this.q.enqueue(this.book.opened); this.q.enqueue(this.book.opened);
// Block the queue until rendering is started // Block the queue until rendering is started
// this.starting = new core.defer(); this.starting = new core.defer();
// this.started = this.starting.promise; this.started = this.starting.promise;
this.q.enqueue(this.start); this.q.enqueue(this.start);
}; };
/**
* Set the manager function
* @param {function} manager
*/
Rendition.prototype.setManager = function(manager) { Rendition.prototype.setManager = function(manager) {
this.manager = manager; this.manager = manager;
}; };
/**
* Require the manager from passed string, or as a function
* @param {string|function} manager [description]
* @return {method}
*/
Rendition.prototype.requireManager = function(manager) { Rendition.prototype.requireManager = function(manager) {
var viewManager; var viewManager;
@ -80,6 +109,11 @@ Rendition.prototype.requireManager = function(manager) {
return viewManager; return viewManager;
}; };
/**
* Require the view from passed string, or as a function
* @param {string|function} view
* @return {view}
*/
Rendition.prototype.requireView = function(view) { Rendition.prototype.requireView = function(view) {
var View; var View;
@ -93,6 +127,10 @@ Rendition.prototype.requireView = function(view) {
return View; return View;
}; };
/**
* Start the rendering
* @return {Promise} rendering has started
*/
Rendition.prototype.start = function(){ Rendition.prototype.start = function(){
if(!this.manager) { if(!this.manager) {
@ -130,11 +168,15 @@ Rendition.prototype.start = function(){
this.emit("started"); this.emit("started");
// Start processing queue // Start processing queue
// this.starting.resolve(); this.starting.resolve();
}; };
// Call to attach the container to an element in the dom /**
// Container must be attached before rendering can begin * Call to attach the container to an element in the dom
* Container must be attached before rendering can begin
* @param {element} element to attach to
* @return {Promise}
*/
Rendition.prototype.attachTo = function(element){ Rendition.prototype.attachTo = function(element){
return this.q.enqueue(function () { return this.q.enqueue(function () {
@ -152,6 +194,14 @@ Rendition.prototype.attachTo = function(element){
}; };
/**
* Display a point in the book
* The request will be added to the rendering Queue,
* so it will wait until book is opened, rendering started
* and all other rendering tasks have finished to be called.
* @param {string} target Url or EpubCFI
* @return {Promise}
*/
Rendition.prototype.display = function(target){ Rendition.prototype.display = function(target){
// if (!this.book.spine.spineItems.length > 0) { // if (!this.book.spine.spineItems.length > 0) {
@ -163,6 +213,12 @@ Rendition.prototype.display = function(target){
}; };
/**
* Tells the manager what to display immediately
* @private
* @param {string} target Url or EpubCFI
* @return {Promise}
*/
Rendition.prototype._display = function(target){ Rendition.prototype._display = function(target){
var isCfiString = this.epubcfi.isCfiString(target); var isCfiString = this.epubcfi.isCfiString(target);
var displaying = new core.defer(); var displaying = new core.defer();
@ -240,13 +296,22 @@ Rendition.prototype.render = function(view, show) {
}; };
*/ */
/**
* Report what has been displayed
* @private
* @param {*} view
*/
Rendition.prototype.afterDisplayed = function(view){ Rendition.prototype.afterDisplayed = function(view){
this.hooks.content.trigger(view, this); this.hooks.content.trigger(view, this);
this.emit("rendered", view.section); this.emit("rendered", view.section);
this.reportLocation(); this.reportLocation();
}; };
Rendition.prototype.onResized = function(size){ /**
* Report resize events and display the last seen location
* @private
*/
Rendition.prototype.onResized = function(){
if(this.location) { if(this.location) {
this.display(this.location.start); this.display(this.location.start);
@ -259,23 +324,42 @@ Rendition.prototype.onResized = function(size){
}; };
/**
* Move the Rendition to a specific offset
* Usually you would be better off calling display()
* @param {object} offset
*/
Rendition.prototype.moveTo = function(offset){ Rendition.prototype.moveTo = function(offset){
this.manager.moveTo(offset); this.manager.moveTo(offset);
}; };
/**
* Go to the next "page" in the rendition
* @return {Promise}
*/
Rendition.prototype.next = function(){ Rendition.prototype.next = function(){
return this.q.enqueue(this.manager.next.bind(this.manager)) return this.q.enqueue(this.manager.next.bind(this.manager))
.then(this.reportLocation.bind(this)); .then(this.reportLocation.bind(this));
}; };
/**
* Go to the previous "page" in the rendition
* @return {Promise}
*/
Rendition.prototype.prev = function(){ Rendition.prototype.prev = function(){
return this.q.enqueue(this.manager.prev.bind(this.manager)) return this.q.enqueue(this.manager.prev.bind(this.manager))
.then(this.reportLocation.bind(this)); .then(this.reportLocation.bind(this));
}; };
//-- http://www.idpf.org/epub/301/spec/epub-publications.html#meta-properties-rendering //-- http://www.idpf.org/epub/301/spec/epub-publications.html#meta-properties-rendering
/**
* Determine the Layout properties from metadata and settings
* @private
* @param {object} metadata
* @return {object} properties
*/
Rendition.prototype.determineLayoutProperties = function(metadata){ Rendition.prototype.determineLayoutProperties = function(metadata){
var settings; var properties;
var layout = this.settings.layout || metadata.layout || "reflowable"; var layout = this.settings.layout || metadata.layout || "reflowable";
var spread = this.settings.spread || metadata.spread || "auto"; var spread = this.settings.spread || metadata.spread || "auto";
var orientation = this.settings.orientation || metadata.orientation || "auto"; var orientation = this.settings.orientation || metadata.orientation || "auto";
@ -287,7 +371,7 @@ Rendition.prototype.determineLayoutProperties = function(metadata){
viewport = "width="+this.settings.width+", height="+this.settings.height+""; viewport = "width="+this.settings.width+", height="+this.settings.height+"";
} }
settings = { properties = {
layout : layout, layout : layout,
spread : spread, spread : spread,
orientation : orientation, orientation : orientation,
@ -296,7 +380,7 @@ Rendition.prototype.determineLayoutProperties = function(metadata){
minSpreadWidth : minSpreadWidth minSpreadWidth : minSpreadWidth
}; };
return settings; return properties;
}; };
// Rendition.prototype.applyLayoutProperties = function(){ // Rendition.prototype.applyLayoutProperties = function(){
@ -307,28 +391,34 @@ Rendition.prototype.determineLayoutProperties = function(metadata){
// this.layout(settings); // this.layout(settings);
// }; // };
// paginated | scrolled /**
// (scrolled-continuous vs scrolled-doc are handled by different view managers) * Adjust the flow of the rendition to paginated or scrolled
Rendition.prototype.flow = function(_flow){ * (scrolled-continuous vs scrolled-doc are handled by different view managers)
var flow; * @param {string} flow
if (_flow === "scrolled-doc" || _flow === "scrolled-continuous") { */
flow = "scrolled"; Rendition.prototype.flow = function(flow){
var _flow;
if (flow === "scrolled-doc" || flow === "scrolled-continuous") {
_flow = "scrolled";
} }
if (_flow === "auto" || _flow === "paginated") { if (flow === "auto" || flow === "paginated") {
flow = "paginated"; _flow = "paginated";
} }
if (this._layout) { if (this._layout) {
this._layout.flow(flow); this._layout.flow(_flow);
} }
if (this.manager) { if (this.manager) {
this.manager.updateFlow(flow); this.manager.updateFlow(_flow);
} }
}; };
// reflowable | pre-paginated /**
* Adjust the layout of the rendition to reflowable or pre-paginated
* @param {object} settings
*/
Rendition.prototype.layout = function(settings){ Rendition.prototype.layout = function(settings){
if (settings) { if (settings) {
this._layout = new Layout(settings); this._layout = new Layout(settings);
@ -344,7 +434,11 @@ Rendition.prototype.layout = function(settings){
return this._layout; return this._layout;
}; };
// none | auto (TODO: implement landscape, portrait, both) /**
* Adjust if the rendition uses spreads
* @param {string} spread none | auto (TODO: implement landscape, portrait, both)
* @param {int} min min width to use spreads at
*/
Rendition.prototype.spread = function(spread, min){ Rendition.prototype.spread = function(spread, min){
this._layout.spread(spread, min); this._layout.spread(spread, min);
@ -354,7 +448,9 @@ Rendition.prototype.spread = function(spread, min){
} }
}; };
/**
* Report the current location
*/
Rendition.prototype.reportLocation = function(){ Rendition.prototype.reportLocation = function(){
return this.q.enqueue(function(){ return this.q.enqueue(function(){
var location = this.manager.currentLocation(); var location = this.manager.currentLocation();
@ -371,7 +467,9 @@ Rendition.prototype.reportLocation = function(){
}.bind(this)); }.bind(this));
}; };
/**
* Remove and Clean Up the Rendition
*/
Rendition.prototype.destroy = function(){ Rendition.prototype.destroy = function(){
// Clear the queue // Clear the queue
this.q.clear(); this.q.clear();
@ -379,6 +477,11 @@ Rendition.prototype.destroy = function(){
this.manager.destroy(); this.manager.destroy();
}; };
/**
* Pass the events from a view
* @private
* @param {View} view
*/
Rendition.prototype.passViewEvents = function(view){ Rendition.prototype.passViewEvents = function(view){
view.contents.listenedEvents.forEach(function(e){ view.contents.listenedEvents.forEach(function(e){
view.on(e, this.triggerViewEvent.bind(this)); view.on(e, this.triggerViewEvent.bind(this));
@ -387,26 +490,46 @@ Rendition.prototype.passViewEvents = function(view){
view.on("selected", this.triggerSelectedEvent.bind(this)); view.on("selected", this.triggerSelectedEvent.bind(this));
}; };
/**
* Emit events passed by a view
* @private
* @param {event} e
*/
Rendition.prototype.triggerViewEvent = function(e){ Rendition.prototype.triggerViewEvent = function(e){
this.emit(e.type, e); this.emit(e.type, e);
}; };
/**
* Emit a selection event's CFI Range passed from a a view
* @private
* @param {EpubCFI} cfirange
*/
Rendition.prototype.triggerSelectedEvent = function(cfirange){ Rendition.prototype.triggerSelectedEvent = function(cfirange){
this.emit("selected", cfirange); this.emit("selected", cfirange);
}; };
Rendition.prototype.range = function(_cfi, ignoreClass){ /**
var cfi = new EpubCFI(_cfi); * Get a Range from a Visible CFI
* @param {string} cfi EpubCfi String
* @param {string} ignoreClass
* @return {range}
*/
Rendition.prototype.range = function(cfi, ignoreClass){
var _cfi = new EpubCFI(cfi);
var found = this.visible().filter(function (view) { var found = this.visible().filter(function (view) {
if(cfi.spinePos === view.index) return true; if(_cfi.spinePos === view.index) return true;
}); });
// Should only every return 1 item // Should only every return 1 item
if (found.length) { if (found.length) {
return found[0].range(cfi, ignoreClass); return found[0].range(_cfi, ignoreClass);
} }
}; };
/**
* Hook to adjust images to fit in columns
* @param {View} view
*/
Rendition.prototype.adjustImages = function(view) { Rendition.prototype.adjustImages = function(view) {
view.addStylesheetRules([ view.addStylesheetRules([

View file

@ -3,9 +3,18 @@ var core = require('./core');
var Path = require('./core').Path; var Path = require('./core').Path;
var path = require('path'); var path = require('path');
/**
* Handle Package Resources
* @class
* @param {Manifest} manifest
* @param {[object]} options
* @param {[string='base64']} options.replacements
* @param {[Archive]} options.archive
* @param {[method]} options.resolver
*/
function Resources(manifest, options) { function Resources(manifest, options) {
this.settings = { this.settings = {
base64: (options && options.base64) || true, replacements: (options && options.replacements) || 'base64',
archive: (options && options.archive), archive: (options && options.archive),
resolver: (options && options.resolver) resolver: (options && options.resolver)
}; };
@ -15,12 +24,16 @@ function Resources(manifest, options) {
return manifest[key]; return manifest[key];
}); });
this.replacementUrls = undefined; this.replacementUrls = [];
this.split(); this.split();
this.urls(); this.splitUrls();
} }
/**
* Split resources by type
* @private
*/
Resources.prototype.split = function(){ Resources.prototype.split = function(){
// HTML // HTML
@ -50,7 +63,11 @@ Resources.prototype.split = function(){
}); });
}; };
Resources.prototype.urls = function(){ /**
* Convert split resources into Urls
* @private
*/
Resources.prototype.splitUrls = function(){
// All Assets Urls // All Assets Urls
this.urls = this.assets. this.urls = this.assets.
@ -74,11 +91,18 @@ Resources.prototype.urls = function(){
Resources.prototype.replacements = function(archive, resolver){ Resources.prototype.replacements = function(archive, resolver){
archive = archive || this.settings.archive; archive = archive || this.settings.archive;
resolver = resolver || this.settings.resolver; resolver = resolver || this.settings.resolver;
if (this.settings.replacements === "none") {
return new Promise(function(resolve, reject) {
resolve(this.urls);
}.bind(this));
}
var replacements = this.urls. var replacements = this.urls.
map(function(url) { map(function(url) {
var absolute = resolver(url); var absolute = resolver(url);
return archive.createUrl(absolute, {"base64": this.settings.base64}); return archive.createUrl(absolute, {"base64": (this.settings.replacements === "base64")});
}.bind(this)) }.bind(this))
return Promise.all(replacements) return Promise.all(replacements)
@ -88,12 +112,19 @@ Resources.prototype.replacements = function(archive, resolver){
}.bind(this)); }.bind(this));
}; };
/**
* Replace URLs in CSS resources
* @private
* @param {[Archive]} archive
* @param {[method]} resolver
* @return {Promise}
*/
Resources.prototype.replaceCss = function(archive, resolver){ Resources.prototype.replaceCss = function(archive, resolver){
var replaced = []; var replaced = [];
archive = archive || this.settings.archive; archive = archive || this.settings.archive;
resolver = resolver || this.settings.resolver; resolver = resolver || this.settings.resolver;
this.cssUrls.forEach(function(href) { this.cssUrls.forEach(function(href) {
var replacment = this.createCssFile(href, archive, resolver) var replacement = this.createCssFile(href, archive, resolver)
.then(function (replacementUrl) { .then(function (replacementUrl) {
// switch the url in the replacementUrls // switch the url in the replacementUrls
var indexInUrls = this.urls.indexOf(href); var indexInUrls = this.urls.indexOf(href);
@ -102,11 +133,19 @@ Resources.prototype.replaceCss = function(archive, resolver){
} }
}.bind(this)); }.bind(this));
replaced.push(replacment); replaced.push(replacement);
}.bind(this)); }.bind(this));
return Promise.all(replaced); return Promise.all(replaced);
}; };
/**
* Create a new CSS file with the replaced URLs
* @private
* @param {string} href the original css file
* @param {[Archive]} archive
* @param {[method]} resolver
* @return {Promise} returns a BlobUrl to the new CSS file or a data url
*/
Resources.prototype.createCssFile = function(href, archive, resolver){ Resources.prototype.createCssFile = function(href, archive, resolver){
var newUrl; var newUrl;
var indexInUrls; var indexInUrls;
@ -136,7 +175,7 @@ Resources.prototype.createCssFile = function(href, archive, resolver){
text = replace.substitute(text, relUrls, this.replacementUrls); text = replace.substitute(text, relUrls, this.replacementUrls);
// Get the new url // Get the new url
if (this.settings.base64) { if (this.settings.replacements === "base64") {
newUrl = core.createBase64Url(text, 'text/css'); newUrl = core.createBase64Url(text, 'text/css');
} else { } else {
newUrl = core.createBlobUrl(text, 'text/css'); newUrl = core.createBlobUrl(text, 'text/css');
@ -147,6 +186,12 @@ Resources.prototype.createCssFile = function(href, archive, resolver){
}; };
/**
* Resolve all resources URLs relative to an absolute URL
* @param {string} absolute to be resolved to
* @param {[resolver]} resolver
* @return {string[]} array with relative Urls
*/
Resources.prototype.relativeTo = function(absolute, resolver){ Resources.prototype.relativeTo = function(absolute, resolver){
resolver = resolver || this.settings.resolver; resolver = resolver || this.settings.resolver;
@ -159,14 +204,24 @@ Resources.prototype.relativeTo = function(absolute, resolver){
}.bind(this)); }.bind(this));
}; };
Resources.prototype.substitute = function(content, url) { /**
var relUrls; * Get a URL for a resource
if (url) { * @param {string} path
relUrls = this.relativeTo(url); * @return {string} url
} else { */
relUrls = this.urls; Resources.prototype.get = function(path) {
var indexInUrls = this.urls.indexOf(path);
if (indexInUrls === -1) {
return;
}
if (this.replacementUrls.length) {
return new Promise(function(resolve, reject) {
resolve(this.replacementUrls[indexInUrls]);
}.bind(this));
} else {
return archive.createUrl(absolute,
{"base64": (this.settings.replacements === "base64")})
}
} }
return replace.substitute(content, relUrls, this.replacementUrls);
};
module.exports = Resources; module.exports = Resources;

View file

@ -3,6 +3,12 @@ var EpubCFI = require('./epubcfi');
var Hook = require('./hook'); var Hook = require('./hook');
var Url = require('./core').Url; var Url = require('./core').Url;
/**
* Represents a Section of the Book
* In most books this is equivelent to a Chapter
* @param {object} item The spine item representing the section
* @param {object} hooks hooks for serialize and content
*/
function Section(item, hooks){ function Section(item, hooks){
this.idref = item.idref; this.idref = item.idref;
this.linear = item.linear; this.linear = item.linear;
@ -25,7 +31,11 @@ function Section(item, hooks){
}; };
/**
* Load the section from its url
* @param {method} _request a request method to use for loading
* @return {document} a promise with the xml document
*/
Section.prototype.load = function(_request){ Section.prototype.load = function(_request){
var request = _request || this.request || require('./request'); var request = _request || this.request || require('./request');
var loading = new core.defer(); var loading = new core.defer();
@ -55,6 +65,11 @@ Section.prototype.load = function(_request){
return loaded; return loaded;
}; };
/**
* Adds a base tag for resolving urls in the section
* @private
* @param {document} _document
*/
Section.prototype.base = function(_document){ Section.prototype.base = function(_document){
var task = new core.defer(); var task = new core.defer();
var base = _document.createElement("base"); // TODO: check if exists var base = _document.createElement("base"); // TODO: check if exists
@ -76,10 +91,11 @@ Section.prototype.base = function(_document){
return task.promise; return task.promise;
}; };
Section.prototype.beforeSectionLoad = function(){ /**
// Stub for a hook - replace me for now * Render the contents of a section
}; * @param {method} _request a request method to use for loading
* @return {string} output a serialized XML Document
*/
Section.prototype.render = function(_request){ Section.prototype.render = function(_request){
var rendering = new core.defer(); var rendering = new core.defer();
var rendered = rendering.promise; var rendered = rendering.promise;
@ -109,15 +125,21 @@ Section.prototype.render = function(_request){
return rendered; return rendered;
}; };
Section.prototype.find = function(_query){ /**
* Find a string in a section
* TODO: need reimplementation from v0.2
* @param {string} query [description]
* @return {[type]} [description]
*/
Section.prototype.find = function(query){
}; };
/* /**
* Reconciles the current chapters layout properies with * Reconciles the current chapters layout properies with
* the global layout properities. * the global layout properities.
* Takes: global layout settings object, chapter properties string * @param {object} global The globa layout settings object, chapter properties string
* Returns: Object with layout properties * @return {object} layoutProperties Object with layout properties
*/ */
Section.prototype.reconcileLayoutSettings = function(global){ Section.prototype.reconcileLayoutSettings = function(global){
//-- Get the global defaults //-- Get the global defaults
@ -143,10 +165,20 @@ Section.prototype.reconcileLayoutSettings = function(global){
return settings; return settings;
}; };
/**
* Get a CFI from a Range in the Section
* @param {range} _range
* @return {string} cfi an EpubCFI string
*/
Section.prototype.cfiFromRange = function(_range) { Section.prototype.cfiFromRange = function(_range) {
return new EpubCFI(_range, this.cfiBase).toString(); return new EpubCFI(_range, this.cfiBase).toString();
}; };
/**
* Get a CFI from an Element in the Section
* @param {element} el
* @return {string} cfi an EpubCFI string
*/
Section.prototype.cfiFromElement = function(el) { Section.prototype.cfiFromElement = function(el) {
return new EpubCFI(el, this.cfiBase).toString(); return new EpubCFI(el, this.cfiBase).toString();
}; };

View file

@ -4,6 +4,9 @@ var Hook = require('./hook');
var Section = require('./section'); var Section = require('./section');
var replacements = require('./replacements'); var replacements = require('./replacements');
/**
* A collection of Spine Items
*/
function Spine(){ function Spine(){
this.spineItems = []; this.spineItems = [];
this.spineByHref = {}; this.spineByHref = {};
@ -22,6 +25,11 @@ function Spine(){
this.loaded = false; this.loaded = false;
}; };
/**
* Unpack items from a opf into spine items
* @param {Package} _package
* @param {method} resolver URL resolver
*/
Spine.prototype.unpack = function(_package, resolver) { Spine.prototype.unpack = function(_package, resolver) {
this.items = _package.spine; this.items = _package.spine;
@ -46,13 +54,8 @@ Spine.prototype.unpack = function(_package, resolver) {
} }
} }
// if(index > 0) {
item.prev = function(){ return this.get(index-1); }.bind(this); item.prev = function(){ return this.get(index-1); }.bind(this);
// }
// if(index+1 < this.items.length) {
item.next = function(){ return this.get(index+1); }.bind(this); item.next = function(){ return this.get(index+1); }.bind(this);
// }
spineItem = new Section(item, this.hooks); spineItem = new Section(item, this.hooks);
@ -64,10 +67,15 @@ Spine.prototype.unpack = function(_package, resolver) {
this.loaded = true; this.loaded = true;
}; };
// book.spine.get(); /**
// book.spine.get(1); * Get an item from the spine
// book.spine.get("chap1.html"); * @param {[string|int]} target
// book.spine.get("#id1234"); * @return {Section} section
* @example spine.get();
* @example spine.get(1);
* @example spine.get("chap1.html");
* @example spine.get("#id1234");
*/
Spine.prototype.get = function(target) { Spine.prototype.get = function(target) {
var index = 0; var index = 0;
@ -87,6 +95,11 @@ Spine.prototype.get = function(target) {
return this.spineItems[index] || null; return this.spineItems[index] || null;
}; };
/**
* Append a Section to the Spine
* @private
* @param {Section} section
*/
Spine.prototype.append = function(section) { Spine.prototype.append = function(section) {
var index = this.spineItems.length; var index = this.spineItems.length;
section.index = index; section.index = index;
@ -99,6 +112,11 @@ Spine.prototype.append = function(section) {
return index; return index;
}; };
/**
* Prepend a Section to the Spine
* @private
* @param {Section} section
*/
Spine.prototype.prepend = function(section) { Spine.prototype.prepend = function(section) {
var index = this.spineItems.unshift(section); var index = this.spineItems.unshift(section);
this.spineByHref[section.href] = 0; this.spineByHref[section.href] = 0;
@ -112,10 +130,15 @@ Spine.prototype.prepend = function(section) {
return 0; return 0;
}; };
Spine.prototype.insert = function(section, index) { // Spine.prototype.insert = function(section, index) {
//
}; // };
/**
* Remove a Section from the Spine
* @private
* @param {Section} section
*/
Spine.prototype.remove = function(section) { Spine.prototype.remove = function(section) {
var index = this.spineItems.indexOf(section); var index = this.spineItems.indexOf(section);
@ -127,6 +150,10 @@ Spine.prototype.remove = function(section) {
} }
}; };
/**
* Loop over the Sections in the Spine
* @return {method} forEach
*/
Spine.prototype.each = function() { Spine.prototype.each = function() {
return this.spineItems.forEach.apply(this.spineItems, arguments); return this.spineItems.forEach.apply(this.spineItems, arguments);
}; };