1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-02 14:49:16 +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 Path = require('./core').Path;
/**
* Handles Unzipping a requesting files from an Epub Archive
* @class
*/
function Archive() {
this.zip = undefined;
this.checkRequirements();
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 {
if (typeof JSZip === 'undefined') {
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){
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){
return request(zipUrl, "binary")
.then(function(data){
@ -32,6 +52,12 @@ Archive.prototype.openUrl = function(zipUrl, isBase64){
}.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){
var deferred = new core.defer();
var response;
@ -63,6 +89,13 @@ Archive.prototype.request = function(url, type){
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){
var r;
@ -87,19 +120,30 @@ Archive.prototype.handleResponse = function(response, type){
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 entry = this.zip.file(decodededUrl);
var mimeType;
if(entry) {
mimeType = _mimeType || mime.lookup(entry.name);
mimeType = mimeType || mime.lookup(entry.name);
return entry.async("uint8array").then(function(uint8array) {
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){
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
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 entry = this.zip.file(decodededUrl);
var mimeType;
if(entry) {
mimeType = _mimeType || mime.lookup(entry.name);
mimeType = mimeType || mime.lookup(entry.name);
return entry.async("base64").then(function(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){
var deferred = new core.defer();
var _URL = window.URL || window.webkitURL || window.mozURL;
@ -177,6 +232,10 @@ Archive.prototype.createUrl = function(url, options){
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){
var _URL = window.URL || window.webkitURL || window.mozURL;
var fromCache = this.urlCache[url];

View file

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

View file

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

View file

@ -592,7 +592,7 @@ function defer() {
}
// Handle IE not supporting namespaced epub:type in querySelector
if(!query || query.length === 0) {
query = core.qsa(html, element);
query = this.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];

View file

@ -7,7 +7,6 @@ var Contents = require('./contents');
* Creates a new Book
* @param {string|ArrayBuffer} url URL, Path or ArrayBuffer
* @param {object} options to pass to the book
* @param options.requestMethod the request function to use
* @returns {Book} a new Book object
* @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
// Functions may return a promise if they are asycn.
// this.content = new EPUBJS.Hook();
// this.content.register(function(){});
// this.content.trigger(args).then(function(){});
/**
* Hooks allow for injecting functions that must all complete in order before finishing
* They will execute in parallel but all must finish before continuing
* Functions may return a promise if they are asycn.
* @param {any} context scope of this
* @example this.content = new EPUBJS.Hook(this);
*/
function Hook(context){
this.context = context || this;
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(){
for(var i = 0; i < arguments.length; ++i) {
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(){
var args = arguments;
var context = this.context;

View file

@ -1,9 +1,18 @@
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){
this.name = settings.layout || "reflowable";
this._spread = (settings.spread === "none") ? false : true;
this._minSpreadWidth = settings.spread || 800;
this._minSpreadWidth = settings.minSpreadWidth || 800;
this._evenSpreads = settings.evenSpreads || false;
if (settings.flow === "scrolled-continuous" ||
@ -24,12 +33,20 @@ function Layout(settings){
this.divisor = 1;
};
// paginated | scrolled
/**
* Switch the flow between paginated and scrolled
* @param {string} flow paginated | scrolled
*/
Layout.prototype.flow = function(flow) {
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) {
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){
var divisor = 1;
@ -93,6 +116,11 @@ Layout.prototype.calculate = function(_width, _height, _gap){
this.divisor = divisor;
};
/**
* Apply Css to a Document
* @param {Contents} contents
* @return {[Promise]}
*/
Layout.prototype.format = function(contents){
var formating;
@ -107,6 +135,12 @@ Layout.prototype.format = function(contents){
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) {
// var totalWidth = contents.scrollWidth();
var spreads = Math.ceil( totalWidth / this.spreadWidth);

View file

@ -3,6 +3,11 @@ var Queue = require('./queue');
var EpubCFI = require('./epubcfi');
var EventEmitter = require('event-emitter');
/**
* Find Locations for a Book
* @param {Spine} spine
* @param {request} request
*/
function Locations(spine, request) {
this.spine = spine;
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) {
if (chars) {

View file

@ -1,6 +1,10 @@
var core = require('./core');
var path = require('path');
/**
* Navigation Parser
* @param {document} xml navigation html / xhtml / ncx
*/
function Navigation(xml){
this.toc = [];
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) {
var html = core.qs(xml, "html");
var ncx = core.qs(xml, "ncx");
@ -24,6 +32,11 @@ Navigation.prototype.parse = function(xml) {
this.unpack(this.toc);
};
/**
* Unpack navigation items
* @private
* @param {array} toc
*/
Navigation.prototype.unpack = function(toc) {
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) {
var index;
@ -52,9 +69,14 @@ Navigation.prototype.get = function(target) {
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 navItems = navElement ? navElement.querySelectorAll("ol li") : [];
var navItems = navElement ? core.qsa(navElement, "li") : [];
var length = navItems.length;
var i;
@ -65,7 +87,7 @@ Navigation.prototype.parseNav = function(navHtml, spineIndexByURL, bookSpine){
if(!navItems || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.navItem(navItems[i], spineIndexByURL, bookSpine);
item = this.navItem(navItems[i]);
toc[item.id] = item;
if(!item.parent) {
list.push(item);
@ -78,38 +100,25 @@ Navigation.prototype.parseNav = function(navHtml, spineIndexByURL, bookSpine){
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,
// content = item.querySelector("a, span"),
content = core.qs(item, "a"),
src = content.getAttribute('href') || '',
text = content.textContent || "",
// split = src.split("#"),
// baseUrl = split[0],
// spinePos = spineIndexByURL[baseUrl],
// spineItem = bookSpine[spinePos],
subitems = [],
parentNode = item.parentNode,
parent;
// cfi = spineItem ? spineItem.cfi : '';
if(parentNode && parentNode.nodeName === "navPoint") {
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 {
"id": id,
"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 length = navPoints.length;
var i;
@ -131,7 +145,7 @@ Navigation.prototype.parseNcx = function(tocXml, spineIndexByURL, bookSpine){
if(!navPoints || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.ncxItem(navPoints[i], spineIndexByURL, bookSpine);
item = this.ncxItem(navPoints[i]);
toc[item.id] = item;
if(!item.parent) {
list.push(item);
@ -144,39 +158,26 @@ Navigation.prototype.parseNcx = function(tocXml, spineIndexByURL, bookSpine){
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,
// content = item.querySelector("content"),
content = core.qs(item, "content"),
src = content.getAttribute('src'),
// navLabel = item.querySelector("navLabel"),
navLabel = core.qs(item, "navLabel"),
text = navLabel.textContent ? navLabel.textContent : "",
// split = src.split("#"),
// baseUrl = split[0],
// spinePos = spineIndexByURL[baseUrl],
// spineItem = bookSpine[spinePos],
subitems = [],
parentNode = item.parentNode,
parent;
// cfi = spineItem ? spineItem.cfi : '';
if(parentNode && parentNode.nodeName === "navPoint") {
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 {
"id": id,

View file

@ -2,13 +2,22 @@ var path = require('path');
var core = require('./core');
var EpubCFI = require('./epubcfi');
/**
* Open Packaging Format Parser
* @class
* @param {document} packageDocument OPF XML
*/
function Packaging(packageDocument) {
if (packageDocument) {
this.parse(packageDocument);
}
};
/**
* Parse OPF XML
* @param {document} packageDocument OPF XML
* @return {object} parsed package parts
*/
Packaging.prototype.parse = function(packageDocument){
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){
var metadata = {};
@ -86,6 +101,12 @@ Packaging.prototype.parseMetadata = function(xml){
return metadata;
};
/**
* Parse Manifest
* @private
* @param {document} manifestXml
* @return {object} manifest
*/
Packaging.prototype.parseManifest = function(manifestXml){
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){
var spine = [];
@ -148,6 +175,7 @@ Packaging.prototype.parseSpine = function(spineXml, manifest){
/**
* Find TOC NAV
* @private
*/
Packaging.prototype.findNavPath = function(manifestNode){
// Find item with property 'nav'
@ -160,6 +188,7 @@ Packaging.prototype.findNavPath = function(manifestNode){
/**
* Find TOC NCX
* media-type="application/x-dtbncx+xml" href="toc.ncx"
* @private
*/
Packaging.prototype.findNcxPath = function(manifestNode, spineNode){
// 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;
};
//-- 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){
var pkg = core.qs(packageXml, "package");
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){
var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag),
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){
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');
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.context = _context;
this.context = context;
this.tick = core.requestAnimationFrame;
this.running = false;
this.paused = false;
};
// Add an item to the queue
/**
* Add an item to the queue
* @return {Promise}
*/
Queue.prototype.enqueue = function() {
var deferred, promise;
var queued;
@ -56,7 +64,10 @@ Queue.prototype.enqueue = function() {
return queued.promise;
};
// Run one item
/**
* Run one item
* @return {Promise}
*/
Queue.prototype.dequeue = function(){
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(){
if(!this.running){
@ -134,7 +147,10 @@ Queue.prototype.run = function(){
return this.defered.promise;
};
// Flush all, as quickly as possible
/**
* Flush all, as quickly as possible
* @return {Promise}
*/
Queue.prototype.flush = function(){
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(){
this._q = [];
this.running = false;
};
/**
* Get the number of tasks in the queue
* @return {int} tasks
*/
Queue.prototype.length = function(){
return this._q.length;
};
/**
* Pause a running queue
*/
Queue.prototype.pause = function(){
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){
return function(){

View file

@ -9,6 +9,20 @@ var Layout = require('./layout');
var Mapping = require('./mapping');
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) {
this.settings = core.extend(this.settings || {}, {
@ -20,8 +34,7 @@ function Rendition(book, options) {
flow: null,
layout: null,
spread: null,
minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always),
useBase64: true
minSpreadWidth: 800
});
core.extend(this.settings, options);
@ -34,10 +47,17 @@ function Rendition(book, options) {
this.views = null;
//-- Adds Hook methods to the Rendition prototype
/**
* Adds Hook methods to the Rendition prototype
* @property {Hook} hooks
*/
this.hooks = {};
this.hooks.display = new Hook(this);
this.hooks.serialize = new Hook(this);
/**
* @property {method} hooks.content
* @type {Hook}
*/
this.hooks.content = new Hook(this);
this.hooks.layout = new Hook(this);
this.hooks.render = new Hook(this);
@ -55,15 +75,24 @@ function Rendition(book, options) {
this.q.enqueue(this.book.opened);
// Block the queue until rendering is started
// this.starting = new core.defer();
// this.started = this.starting.promise;
this.starting = new core.defer();
this.started = this.starting.promise;
this.q.enqueue(this.start);
};
/**
* Set the manager function
* @param {function} manager
*/
Rendition.prototype.setManager = function(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) {
var viewManager;
@ -80,6 +109,11 @@ Rendition.prototype.requireManager = function(manager) {
return viewManager;
};
/**
* Require the view from passed string, or as a function
* @param {string|function} view
* @return {view}
*/
Rendition.prototype.requireView = function(view) {
var View;
@ -93,6 +127,10 @@ Rendition.prototype.requireView = function(view) {
return View;
};
/**
* Start the rendering
* @return {Promise} rendering has started
*/
Rendition.prototype.start = function(){
if(!this.manager) {
@ -130,11 +168,15 @@ Rendition.prototype.start = function(){
this.emit("started");
// 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){
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){
// 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){
var isCfiString = this.epubcfi.isCfiString(target);
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){
this.hooks.content.trigger(view, this);
this.emit("rendered", view.section);
this.reportLocation();
};
Rendition.prototype.onResized = function(size){
/**
* Report resize events and display the last seen location
* @private
*/
Rendition.prototype.onResized = function(){
if(this.location) {
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){
this.manager.moveTo(offset);
};
/**
* Go to the next "page" in the rendition
* @return {Promise}
*/
Rendition.prototype.next = function(){
return this.q.enqueue(this.manager.next.bind(this.manager))
.then(this.reportLocation.bind(this));
};
/**
* Go to the previous "page" in the rendition
* @return {Promise}
*/
Rendition.prototype.prev = function(){
return this.q.enqueue(this.manager.prev.bind(this.manager))
.then(this.reportLocation.bind(this));
};
//-- 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){
var settings;
var properties;
var layout = this.settings.layout || metadata.layout || "reflowable";
var spread = this.settings.spread || metadata.spread || "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+"";
}
settings = {
properties = {
layout : layout,
spread : spread,
orientation : orientation,
@ -296,7 +380,7 @@ Rendition.prototype.determineLayoutProperties = function(metadata){
minSpreadWidth : minSpreadWidth
};
return settings;
return properties;
};
// Rendition.prototype.applyLayoutProperties = function(){
@ -307,28 +391,34 @@ Rendition.prototype.determineLayoutProperties = function(metadata){
// this.layout(settings);
// };
// paginated | scrolled
// (scrolled-continuous vs scrolled-doc are handled by different view managers)
Rendition.prototype.flow = function(_flow){
var flow;
if (_flow === "scrolled-doc" || _flow === "scrolled-continuous") {
flow = "scrolled";
/**
* Adjust the flow of the rendition to paginated or scrolled
* (scrolled-continuous vs scrolled-doc are handled by different view managers)
* @param {string} flow
*/
Rendition.prototype.flow = function(flow){
var _flow;
if (flow === "scrolled-doc" || flow === "scrolled-continuous") {
_flow = "scrolled";
}
if (_flow === "auto" || _flow === "paginated") {
flow = "paginated";
if (flow === "auto" || flow === "paginated") {
_flow = "paginated";
}
if (this._layout) {
this._layout.flow(flow);
this._layout.flow(_flow);
}
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){
if (settings) {
this._layout = new Layout(settings);
@ -344,7 +434,11 @@ Rendition.prototype.layout = function(settings){
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){
this._layout.spread(spread, min);
@ -354,7 +448,9 @@ Rendition.prototype.spread = function(spread, min){
}
};
/**
* Report the current location
*/
Rendition.prototype.reportLocation = function(){
return this.q.enqueue(function(){
var location = this.manager.currentLocation();
@ -371,7 +467,9 @@ Rendition.prototype.reportLocation = function(){
}.bind(this));
};
/**
* Remove and Clean Up the Rendition
*/
Rendition.prototype.destroy = function(){
// Clear the queue
this.q.clear();
@ -379,6 +477,11 @@ Rendition.prototype.destroy = function(){
this.manager.destroy();
};
/**
* Pass the events from a view
* @private
* @param {View} view
*/
Rendition.prototype.passViewEvents = function(view){
view.contents.listenedEvents.forEach(function(e){
view.on(e, this.triggerViewEvent.bind(this));
@ -387,26 +490,46 @@ Rendition.prototype.passViewEvents = function(view){
view.on("selected", this.triggerSelectedEvent.bind(this));
};
/**
* Emit events passed by a view
* @private
* @param {event} e
*/
Rendition.prototype.triggerViewEvent = function(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){
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) {
if(cfi.spinePos === view.index) return true;
if(_cfi.spinePos === view.index) return true;
});
// Should only every return 1 item
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) {
view.addStylesheetRules([

View file

@ -3,9 +3,18 @@ var core = require('./core');
var Path = require('./core').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) {
this.settings = {
base64: (options && options.base64) || true,
replacements: (options && options.replacements) || 'base64',
archive: (options && options.archive),
resolver: (options && options.resolver)
};
@ -15,12 +24,16 @@ function Resources(manifest, options) {
return manifest[key];
});
this.replacementUrls = undefined;
this.replacementUrls = [];
this.split();
this.urls();
this.splitUrls();
}
/**
* Split resources by type
* @private
*/
Resources.prototype.split = function(){
// 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
this.urls = this.assets.
@ -74,11 +91,18 @@ Resources.prototype.urls = function(){
Resources.prototype.replacements = function(archive, resolver){
archive = archive || this.settings.archive;
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.
map(function(url) {
var absolute = resolver(url);
return archive.createUrl(absolute, {"base64": this.settings.base64});
return archive.createUrl(absolute, {"base64": (this.settings.replacements === "base64")});
}.bind(this))
return Promise.all(replacements)
@ -88,12 +112,19 @@ Resources.prototype.replacements = function(archive, resolver){
}.bind(this));
};
/**
* Replace URLs in CSS resources
* @private
* @param {[Archive]} archive
* @param {[method]} resolver
* @return {Promise}
*/
Resources.prototype.replaceCss = function(archive, resolver){
var replaced = [];
archive = archive || this.settings.archive;
resolver = resolver || this.settings.resolver;
this.cssUrls.forEach(function(href) {
var replacment = this.createCssFile(href, archive, resolver)
var replacement = this.createCssFile(href, archive, resolver)
.then(function (replacementUrl) {
// switch the url in the replacementUrls
var indexInUrls = this.urls.indexOf(href);
@ -102,11 +133,19 @@ Resources.prototype.replaceCss = function(archive, resolver){
}
}.bind(this));
replaced.push(replacment);
replaced.push(replacement);
}.bind(this));
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){
var newUrl;
var indexInUrls;
@ -136,7 +175,7 @@ Resources.prototype.createCssFile = function(href, archive, resolver){
text = replace.substitute(text, relUrls, this.replacementUrls);
// Get the new url
if (this.settings.base64) {
if (this.settings.replacements === "base64") {
newUrl = core.createBase64Url(text, 'text/css');
} else {
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){
resolver = resolver || this.settings.resolver;
@ -159,14 +204,24 @@ Resources.prototype.relativeTo = function(absolute, resolver){
}.bind(this));
};
Resources.prototype.substitute = function(content, url) {
var relUrls;
if (url) {
relUrls = this.relativeTo(url);
} else {
relUrls = this.urls;
/**
* Get a URL for a resource
* @param {string} path
* @return {string} url
*/
Resources.prototype.get = function(path) {
var indexInUrls = this.urls.indexOf(path);
if (indexInUrls === -1) {
return;
}
return replace.substitute(content, relUrls, this.replacementUrls);
};
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")})
}
}
module.exports = Resources;

View file

@ -3,6 +3,12 @@ var EpubCFI = require('./epubcfi');
var Hook = require('./hook');
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){
this.idref = item.idref;
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){
var request = _request || this.request || require('./request');
var loading = new core.defer();
@ -55,6 +65,11 @@ Section.prototype.load = function(_request){
return loaded;
};
/**
* Adds a base tag for resolving urls in the section
* @private
* @param {document} _document
*/
Section.prototype.base = function(_document){
var task = new core.defer();
var base = _document.createElement("base"); // TODO: check if exists
@ -76,10 +91,11 @@ Section.prototype.base = function(_document){
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){
var rendering = new core.defer();
var rendered = rendering.promise;
@ -109,15 +125,21 @@ Section.prototype.render = function(_request){
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
* the global layout properities.
* Takes: global layout settings object, chapter properties string
* Returns: Object with layout properties
* @param {object} global The globa layout settings object, chapter properties string
* @return {object} layoutProperties Object with layout properties
*/
Section.prototype.reconcileLayoutSettings = function(global){
//-- Get the global defaults
@ -143,10 +165,20 @@ Section.prototype.reconcileLayoutSettings = function(global){
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) {
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) {
return new EpubCFI(el, this.cfiBase).toString();
};

View file

@ -4,6 +4,9 @@ var Hook = require('./hook');
var Section = require('./section');
var replacements = require('./replacements');
/**
* A collection of Spine Items
*/
function Spine(){
this.spineItems = [];
this.spineByHref = {};
@ -22,6 +25,11 @@ function Spine(){
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) {
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);
// }
// if(index+1 < this.items.length) {
item.next = function(){ return this.get(index+1); }.bind(this);
// }
spineItem = new Section(item, this.hooks);
@ -64,10 +67,15 @@ Spine.prototype.unpack = function(_package, resolver) {
this.loaded = true;
};
// book.spine.get();
// book.spine.get(1);
// book.spine.get("chap1.html");
// book.spine.get("#id1234");
/**
* Get an item from the spine
* @param {[string|int]} target
* @return {Section} section
* @example spine.get();
* @example spine.get(1);
* @example spine.get("chap1.html");
* @example spine.get("#id1234");
*/
Spine.prototype.get = function(target) {
var index = 0;
@ -87,6 +95,11 @@ Spine.prototype.get = function(target) {
return this.spineItems[index] || null;
};
/**
* Append a Section to the Spine
* @private
* @param {Section} section
*/
Spine.prototype.append = function(section) {
var index = this.spineItems.length;
section.index = index;
@ -99,6 +112,11 @@ Spine.prototype.append = function(section) {
return index;
};
/**
* Prepend a Section to the Spine
* @private
* @param {Section} section
*/
Spine.prototype.prepend = function(section) {
var index = this.spineItems.unshift(section);
this.spineByHref[section.href] = 0;
@ -112,10 +130,15 @@ Spine.prototype.prepend = function(section) {
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) {
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() {
return this.spineItems.forEach.apply(this.spineItems, arguments);
};