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

Switch to URI.js for url parsing, basic replacement for Archived urls

This commit is contained in:
fchasen 2015-12-06 22:14:02 -05:00
parent af5583191e
commit 13467128e5
18 changed files with 2677 additions and 207 deletions

2521
dist/epub.js vendored

File diff suppressed because it is too large Load diff

2
dist/epub.js.map vendored

File diff suppressed because one or more lines are too long

7
dist/epub.min.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -16,6 +16,9 @@ var source = require('vinyl-source-stream');
var buffer = require('vinyl-buffer');
var sourcemaps = require('gulp-sourcemaps');
var size = require('gulp-size');
var URI = require('urijs');
// https://github.com/mishoo/UglifyJS2/pull/265
// uglify.AST_Node.warn_function = function() {};
@ -43,6 +46,7 @@ gulp.task('minify', ['bundle'], function(){
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(uglify(uglifyOptions))
.pipe(sourcemaps.write('./'))
.pipe(size({ showFiles: true }))
.pipe(gulp.dest('dist'));
});
@ -59,9 +63,6 @@ gulp.task('serve', ["watch"], function() {
// Default
gulp.task('default', ['lint', 'build']);
// gulp.task('default', function() {
// // place code for your default task here
// });
function bundle(file, watch) {
var opt = {
@ -71,9 +72,21 @@ function bundle(file, watch) {
};
var b = browserify(opt);
// Expose epub module for require
b.require('./src/'+file, {expose: 'epub'});
// Keep JSZip library seperate,
// must be loaded to use Unarchive or `require` will throw an error
b.external('jszip');
// Ignore optional URI libraries
var urijsPath = URI(require.resolve('urijs'));
['./punycode.js', './IPv6.js', './SecondLevelDomains.js'].forEach(function(lib) {
var libPath = URI(lib).absoluteTo(urijsPath).toString();
b.ignore(libPath);
});
// watchify() if watch requested, otherwise run browserify() once
var bundler = watch ? watchify(b) : b;
@ -85,6 +98,7 @@ function bundle(file, watch) {
.pipe(buffer())
.pipe(sourcemaps.init({loadMaps: true}))
.pipe(sourcemaps.write('./'))
.pipe(size({ showFiles: true }))
.pipe(gulp.dest('./dist/'));
}

View file

@ -22,6 +22,7 @@
"gulp-jshint": "^1.8.4",
"gulp-plumber": "^0.6.4",
"gulp-rename": "^1.2.0",
"gulp-size": "^2.0.0",
"gulp-sourcemaps": "^1.6.0",
"gulp-uglify": "^0.3.1",
"gulp-util": "^3.0.0",
@ -37,6 +38,7 @@
},
"dependencies": {
"jszip": "^2.5.0",
"rsvp": "^3.0.18"
"rsvp": "^3.0.18",
"urijs": "^1.17.0"
}
}

View file

@ -1,4 +1,5 @@
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
var Spine = require('./spine');
var Locations = require('./locations');
@ -67,44 +68,43 @@ Book.prototype.open = function(_url){
}
// Reuse parsed url or create a new uri object
if(typeof(_url) === "object") {
uri = _url;
} else {
uri = core.uri(_url);
}
this.url = uri.href;
// if(typeof(_url) === "object") {
// uri = _url;
// } else {
// uri = core.uri(_url);
// }
uri = URI(_url);
this.url = _url;
// Find path to the Container
if(uri.extension === "opf") {
if(uri.suffix() === "opf") {
// Direct link to package, no container
this.packageUrl = uri.href;
this.packageUrl = _url;
this.containerUrl = '';
if(uri.origin) {
this.baseUrl = uri.base;
if(uri.origin()) {
this.baseUrl = uri.origin() + "/" + uri.directory() + "/";
} else if(window){
location = core.uri(window.location.href);
this.baseUrl = core.resolveUrl(location.base, uri.directory);
this.baseUrl = uri.absoluteTo(window.location.href).directory() + "/";
} else {
this.baseUrl = uri.directory;
this.baseUrl = uri.directory() + "/";
}
epubPackage = this.request(this.packageUrl);
} else if(this.isArchived(uri)) {
} else if(this.isArchivedUrl(uri)) {
// Book is archived
this.containerUrl = containerPath;
this.url = '';
this.url = '/';
this.containerUrl = URI(containerPath).absoluteTo(this.url).toString();
epubContainer = this.unarchive(uri.href).
epubContainer = this.unarchive(_url).
then(function() {
return this.request(this.containerUrl);
}.bind(this));
}
// Find the path to the Package from the container
else if (!uri.extension) {
else if (!uri.suffix()) {
this.containerUrl = this.url + containerPath;
@ -117,18 +117,17 @@ Book.prototype.open = function(_url){
return parse.container(containerXml); // Container has path to content
}).
then(function(paths){
var packageUri = core.uri(paths.packagePath);
book.packageUrl = book.url + paths.packagePath;
var packageUri = URI(paths.packagePath);
book.packageUrl = packageUri.absoluteTo(book.url).toString();
book.encoding = paths.encoding;
// Set Url relative to the content
if(packageUri.origin) {
book.baseUrl = packageUri.base;
} else if(window && book.url){
location = core.uri(window.location.href);
book.baseUrl = core.resolveUrl(location.base, book.url + packageUri.directory);
if(packageUri.origin()) {
book.baseUrl = packageUri.origin() + "/" + packageUri.directory() + "/";
} else if(window && !book.isArchivedUrl(uri)){
book.baseUrl = packageUri.absoluteTo(window.location.href).directory() + "/";
} else {
book.baseUrl = packageUri.directory;
book.baseUrl = "/" + packageUri.directory() + "/";
}
return book.request(book.packageUrl);
@ -186,7 +185,7 @@ Book.prototype.unpack = function(packageXml){
// MOVE TO RENDER
// book.globalLayoutProperties = book.parseLayoutProperties(book.package.metadata);
book.cover = book.baseUrl + book.package.coverPath;
book.cover = URI(book.package.coverPath).absoluteTo(book.baseUrl).toString();
};
// Alias for book.spine.get
@ -203,6 +202,7 @@ Book.prototype.renderTo = function(element, options) {
this.rendition = new Renderer(this, options);
this.rendition.attachTo(element);
return this.rendition;
};
@ -230,21 +230,24 @@ Book.prototype.unarchive = function(bookUrl){
};
//-- Checks if url has a .epub or .zip extension, or is ArrayBuffer (of zip/epub)
Book.prototype.isArchived = function(bookUrl){
Book.prototype.isArchivedUrl = function(bookUrl){
var uri;
var extension;
if (bookUrl instanceof ArrayBuffer) {
return true;
}
// Reuse parsed url or create a new uri object
if(typeof(bookUrl) === "object") {
uri = bookUrl;
} else {
uri = core.uri(bookUrl);
}
// if(typeof(bookUrl) === "object") {
// uri = bookUrl;
// } else {
// uri = core.uri(bookUrl);
// }
uri = URI(bookUrl);
extension = uri.suffix();
if(uri.extension && (uri.extension == "epub" || uri.extension == "zip")){
if(extension && (extension == "epub" || extension == "zip")){
return true;
}
@ -253,8 +256,8 @@ Book.prototype.isArchived = function(bookUrl){
//-- Returns the cover
Book.prototype.coverUrl = function(){
var retrieved = this.loaded.cover
.then(function(url) {
var retrieved = this.loaded.cover.
then(function(url) {
if(this.archive) {
return this.archive.createUrl(this.cover);
}else{
@ -266,6 +269,7 @@ Book.prototype.coverUrl = function(){
return retrieved;
};
module.exports = Book;
//-- Enable binding events to book

View file

@ -78,12 +78,12 @@ Continuous.prototype.afterDisplayed = function(currView){
var prevView, nextView;
if(index + 1 === this.views.length && next) {
nextView = new View(next, this.viewSettings);
nextView = this.createView(next);
this.q.enqueue(this.append, nextView);
}
if(index === 0 && prev) {
prevView = new View(prev, this.viewSettings);
prevView = this.createView(prev, this.viewSettings);
this.q.enqueue(this.prepend, prevView);
}

View file

@ -1,3 +1,4 @@
var URI = require('urijs');
var core = require('./core');
function EpubCFI(cfiStr){
@ -359,9 +360,9 @@ EpubCFI.prototype.compare = function(cfiOne, cfiTwo) {
};
EpubCFI.prototype.generateCfiFromHref = function(href, book) {
var uri = core.uri(href);
var path = uri.path;
var fragment = uri.fragment;
var uri = URI(href);
var path = uri.path();
var fragment = uri.fragment();
var spinePos = book.spineIndexByURL[path];
var loaded;
var deferred = new RSVP.defer();

View file

@ -14,8 +14,17 @@ function Hook(context){
};
// Adds a function to be run before a hook completes
Hook.prototype.register = function(func){
this.hooks.push(func);
Hook.prototype.register = function(){
for(var i = 0; i < arguments.length; ++i) {
if (typeof arguments[i] === "function") {
this.hooks.push(arguments[i]);
} else {
// unpack array
for(var j = 0; j < arguments[i].length; ++j) {
this.hooks.push(arguments[i][j]);
}
}
}
};
// Triggers a hook to run all functions
@ -38,4 +47,9 @@ Hook.prototype.trigger = function(){
return RSVP.all(promises);
};
// Adds a function to be run before a hook completes
Hook.prototype.list = function(){
return this.hooks;
};
module.exports = Hook;

View file

@ -1,6 +1,7 @@
var core = require('./core');
var Parser = require('./parser');
var RSVP = require('rsvp');
var URI = require('urijs');
function Navigation(_package, _request){
var navigation = this;
@ -13,7 +14,7 @@ function Navigation(_package, _request){
this.tocById = {};
if(_package.navPath) {
this.navUrl = _package.baseUrl + _package.navPath;
this.navUrl = URI(_package.navPath).absoluteTo(_package.baseUrl).toString();
this.nav = {};
this.nav.load = function(_request){
@ -32,7 +33,7 @@ function Navigation(_package, _request){
}
if(_package.ncxPath) {
this.ncxUrl = _package.baseUrl + _package.ncxPath;
this.ncxUrl = URI(_package.ncxPath).absoluteTo(_package.baseUrl).toString();
this.ncx = {};
this.ncx.load = function(_request){

View file

@ -1,3 +1,4 @@
var URI = require('urijs');
var core = require('./core');
var EpubCFI = require('./epubcfi');
@ -20,7 +21,7 @@ Parser.prototype.container = function(containerXml){
}
fullpath = rootfile.getAttribute('full-path');
folder = core.uri(fullpath).directory;
folder = URI(fullpath).directory();
encoding = containerXml.xmlEncoding;
//-- Now that we have the path we can parse the contents
@ -310,8 +311,8 @@ Parser.prototype.navItem = function(item, spineIndexByURL, bookSpine){
content = item.querySelector("a, span"),
src = content.getAttribute('href') || '',
text = content.textContent || "",
split = src.split("#"),
baseUrl = split[0],
// split = src.split("#"),
// baseUrl = split[0],
// spinePos = spineIndexByURL[baseUrl],
// spineItem = bookSpine[spinePos],
subitems = [],
@ -375,8 +376,8 @@ Parser.prototype.tocItem = function(item, spineIndexByURL, bookSpine){
src = content.getAttribute('src'),
navLabel = item.querySelector("navLabel"),
text = navLabel.textContent ? navLabel.textContent : "",
split = src.split("#"),
baseUrl = split[0],
// split = src.split("#"),
// baseUrl = split[0],
// spinePos = spineIndexByURL[baseUrl],
// spineItem = bookSpine[spinePos],
subitems = [],

View file

@ -1,4 +1,5 @@
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
var replace = require('./replacements');
var Hook = require('./hook');
@ -31,6 +32,7 @@ function Rendition(book, options) {
//-- Adds Hook methods to the Rendition prototype
this.hooks = {};
this.hooks.display = new Hook(this);
this.hooks.serialize = new Hook(this);
this.hooks.content = new Hook(this);
this.hooks.layout = new Hook(this);
this.hooks.render = new Hook(this);
@ -49,6 +51,9 @@ function Rendition(book, options) {
this.q.enqueue(this.parseLayoutProperties);
if(this.book.archive) {
this.replacements();
}
};
/**
@ -214,7 +219,8 @@ Rendition.prototype._display = function(target){
this.views.hide();
// Create a new view
view = new View(section, this.viewSettings);
// view = new View(section, this.viewSettings);
view = this.createView(section);
// This will clear all previous views
displayed = this.fill(view)
@ -421,6 +427,9 @@ Rendition.prototype.onResized = function(e) {
};
Rendition.prototype.createView = function(section) {
// Transfer the existing hooks
section.hooks.serialize.register(this.hooks.serialize.list());
return new View(section, this.viewSettings);
};
@ -624,6 +633,74 @@ Rendition.prototype.triggerViewEvent = function(e){
this.trigger(e.type, e);
};
Rendition.prototype.replacements = function(){
return this.q.enqueue(function () {
var manifest = this.book.package.manifest;
var manifestArray = Object.keys(manifest).
map(function (key){
return manifest[key];
});
// Exclude HTML
var items = manifestArray.
filter(function (item){
if (item.type != "application/xhtml+xml" &&
item.type != "text/html") {
return true;
}
});
// Only CSS
var css = items.
filter(function (item){
if (item.type != "text/css") {
return true;
}
});
var urls = items.
map(function(item) {
// return this.book.baseUrl + item.href;
return item.href;
}.bind(this));
var processing = urls.
map(function(url) {
var absolute = URI(url).absoluteTo(this.book.baseUrl).toString();
// Full url from archive base
return this.book.archive.createUrl(absolute);
}.bind(this));
return RSVP.all(processing).
then(function(replacementUrls) {
this.hooks.serialize.register(function(content, section) {
// resolve file urls
var fileUri = URI(section.url);
// console.log(section.url);
// var fileDirectory = fileUri.directory();
// // // package urls
// var packageUri = URI(this.book.packageUrl);
// var packageDirectory = packageUri.directory();
//
// // Need to compare the directory of the current file
// // with the directory of the package file.
urls = urls.
map(function(href) {
var assetUri = URI(href).absoluteTo(this.book.baseUrl);
var relative = assetUri.relativeTo(fileUri).toString();
return relative;
}.bind(this));
section.output = replace.substitute(content, urls, replacementUrls);
}.bind(this));
}.bind(this)).catch(function(reason){
console.error(reason);
});
}.bind(this));
};
//-- Enable binding events to Renderer
RSVP.EventTarget.mixin(Rendition.prototype);

View file

@ -1,34 +1,61 @@
var URI = require('urijs');
var core = require('./core');
function base(doc, section){
var base;
var head;
if(!doc){
return;
}
head = doc.querySelector("head");
base = head.querySelector("base");
if(!base) {
base = doc.createElement("base");
}
base.setAttribute("href", section.url);
head.insertBefore(base, head.firstChild);
}
function links(view, renderer) {
var links = view.document.querySelectorAll("a[href]");
var replaceLinks = function(link){
var href = link.getAttribute("href");
var uri = new core.uri(href);
var linkUri = URI(href);
var absolute = linkUri.absoluteTo(view.section.url);
var relative = absolute.relativeTo(this.book.baseUrl).toString();
if(uri.protocol){
if(linkUri.protocol()){
link.setAttribute("target", "_blank");
}else{
/*
if(baseDirectory) {
// We must ensure that the file:// protocol is preserved for
// local file links, as in certain contexts (such as under
// Titanium), file links without the file:// protocol will not
// work
if (baseUri.protocol === "file") {
relative = core.resolveUrl(baseUri.base, href);
} else {
relative = core.resolveUrl(baseDirectory, href);
}
} else {
relative = href;
}
*/
// relative = core.resolveUrl(directory, href);
// if(uri.fragment && !base) {
// link.onclick = function(){
// renderer.fragment(href);
// return false;
// };
// } else {
//}
if(href.indexOf("#") === 0) {
if(linkUri.fragment()) {
// do nothing with fragment yet
} else {
link.onclick = function(){
renderer.display(href);
renderer.display(relative);
return false;
};
}
@ -43,6 +70,14 @@ function links(view, renderer) {
};
function substitute(content, urls, replacements) {
urls.forEach(function(url, i){
content = content.replace(new RegExp(url, 'g'), replacements[i]);
});
return content;
}
module.exports = {
'links': links
'base': base,
'links': links,
'substitute': substitute
};

View file

@ -1,4 +1,5 @@
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
function request(url, type, withCredentials, headers) {
@ -36,8 +37,8 @@ function request(url, type, withCredentials, headers) {
// If type isn't set, determine it from the file extension
if(!type) {
uri = core.uri(url);
type = uri.extension;
uri = URI(url);
type = uri.suffix();
}
if(type == 'blob'){

View file

@ -2,8 +2,9 @@ var RSVP = require('rsvp');
var core = require('./core');
var EpubCFI = require('./epubcfi');
var Hook = require('./hook');
var replacements = require('./replacements');
function Section(item){
function Section(item, hooks){
this.idref = item.idref;
this.linear = item.linear;
this.properties = item.properties;
@ -17,10 +18,11 @@ function Section(item){
this.cfiBase = item.cfiBase;
this.hooks = {};
this.hooks.replacements = new Hook(this);
this.hooks.serialize = new Hook(this);
this.hooks.content = new Hook(this);
// Register replacements
this.hooks.replacements.register(this.replacements);
this.hooks.content.register(replacements.base);
};
@ -40,7 +42,7 @@ Section.prototype.load = function(_request){
this.document = xml;
this.contents = xml.documentElement;
return this.hooks.replacements.trigger(this.document);
return this.hooks.content.trigger(this.document, this);
}.bind(this))
.then(function(){
loading.resolve(this.contents);
@ -53,7 +55,7 @@ Section.prototype.load = function(_request){
return loaded;
};
Section.prototype.replacements = function(_document){
Section.prototype.base = function(_document){
var task = new RSVP.defer();
var base = _document.createElement("base"); // TODO: check if exists
var head;
@ -81,12 +83,20 @@ Section.prototype.beforeSectionLoad = function(){
Section.prototype.render = function(_request){
var rendering = new RSVP.defer();
var rendered = rendering.promise;
this.output; // TODO: better way to return this from hooks?
this.load(_request).then(function(contents){
this.load(_request).
then(function(contents){
var serializer = new XMLSerializer();
var output = serializer.serializeToString(contents);
rendering.resolve(output);
})
this.output = serializer.serializeToString(contents);
return this.output;
}.bind(this)).
then(function(){
return this.hooks.serialize.trigger(this.output, this);
}.bind(this)).
then(function(){
rendering.resolve(this.output);
}.bind(this))
.catch(function(error){
rendering.reject(error);
});

View file

@ -30,7 +30,6 @@ Spine.prototype.load = function(_package) {
item.href = manifestItem.href;
item.url = this.baseUrl + item.href;
if(manifestItem.properties.length){
item.properties.push.apply(item.properties, manifestItem.properties);
}

View file

@ -1,4 +1,5 @@
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
var request = require('./request');
var mime = require('../libs/mime/mime');
@ -45,8 +46,8 @@ Unarchive.prototype.request = function(url, type){
// If type isn't set, determine it from the file extension
if(!type) {
uri = core.uri(url);
type = uri.extension;
uri = URI(url);
type = uri.suffix();
}
if(type == 'blob'){
@ -92,7 +93,7 @@ Unarchive.prototype.handleResponse = function(response, type){
};
Unarchive.prototype.getBlob = function(url, _mimeType){
var decodededUrl = window.decodeURIComponent(url);
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl);
var mimeType;
@ -103,7 +104,7 @@ Unarchive.prototype.getBlob = function(url, _mimeType){
};
Unarchive.prototype.getText = function(url, encoding){
var decodededUrl = window.decodeURIComponent(url);
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl);
if(entry) {