1
0
Fork 0
mirror of https://github.com/futurepress/epub.js.git synced 2025-10-05 15:32:55 +02:00
epub.js/dist/epub.js

9799 lines
247 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.ePub = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*
From Zip.js, by Gildas Lormeau
edited down
*/
var table = {
"application" : {
"ecmascript" : [ "es", "ecma" ],
"javascript" : "js",
"ogg" : "ogx",
"pdf" : "pdf",
"postscript" : [ "ps", "ai", "eps", "epsi", "epsf", "eps2", "eps3" ],
"rdf+xml" : "rdf",
"smil" : [ "smi", "smil" ],
"xhtml+xml" : [ "xhtml", "xht" ],
"xml" : [ "xml", "xsl", "xsd", "opf", "ncx" ],
"zip" : "zip",
"x-httpd-eruby" : "rhtml",
"x-latex" : "latex",
"x-maker" : [ "frm", "maker", "frame", "fm", "fb", "book", "fbdoc" ],
"x-object" : "o",
"x-shockwave-flash" : [ "swf", "swfl" ],
"x-silverlight" : "scr",
"epub+zip" : "epub",
"font-tdpfr" : "pfr",
"inkml+xml" : [ "ink", "inkml" ],
"json" : "json",
"jsonml+json" : "jsonml",
"mathml+xml" : "mathml",
"metalink+xml" : "metalink",
"mp4" : "mp4s",
// "oebps-package+xml" : "opf",
"omdoc+xml" : "omdoc",
"oxps" : "oxps",
"vnd.amazon.ebook" : "azw",
"widget" : "wgt",
// "x-dtbncx+xml" : "ncx",
"x-dtbook+xml" : "dtb",
"x-dtbresource+xml" : "res",
"x-font-bdf" : "bdf",
"x-font-ghostscript" : "gsf",
"x-font-linux-psf" : "psf",
"x-font-otf" : "otf",
"x-font-pcf" : "pcf",
"x-font-snf" : "snf",
"x-font-ttf" : [ "ttf", "ttc" ],
"x-font-type1" : [ "pfa", "pfb", "pfm", "afm" ],
"x-font-woff" : "woff",
"x-mobipocket-ebook" : [ "prc", "mobi" ],
"x-mspublisher" : "pub",
"x-nzb" : "nzb",
"x-tgif" : "obj",
"xaml+xml" : "xaml",
"xml-dtd" : "dtd",
"xproc+xml" : "xpl",
"xslt+xml" : "xslt",
"internet-property-stream" : "acx",
"x-compress" : "z",
"x-compressed" : "tgz",
"x-gzip" : "gz",
},
"audio" : {
"flac" : "flac",
"midi" : [ "mid", "midi", "kar", "rmi" ],
"mpeg" : [ "mpga", "mpega", "mp2", "mp3", "m4a", "mp2a", "m2a", "m3a" ],
"mpegurl" : "m3u",
"ogg" : [ "oga", "ogg", "spx" ],
"x-aiff" : [ "aif", "aiff", "aifc" ],
"x-ms-wma" : "wma",
"x-wav" : "wav",
"adpcm" : "adp",
"mp4" : "mp4a",
"webm" : "weba",
"x-aac" : "aac",
"x-caf" : "caf",
"x-matroska" : "mka",
"x-pn-realaudio-plugin" : "rmp",
"xm" : "xm",
"mid" : [ "mid", "rmi" ]
},
"image" : {
"gif" : "gif",
"ief" : "ief",
"jpeg" : [ "jpeg", "jpg", "jpe" ],
"pcx" : "pcx",
"png" : "png",
"svg+xml" : [ "svg", "svgz" ],
"tiff" : [ "tiff", "tif" ],
"x-icon" : "ico",
"bmp" : "bmp",
"webp" : "webp",
"x-pict" : [ "pic", "pct" ],
"x-tga" : "tga",
"cis-cod" : "cod"
},
"text" : {
"cache-manifest" : [ "manifest", "appcache" ],
"css" : "css",
"csv" : "csv",
"html" : [ "html", "htm", "shtml", "stm" ],
"mathml" : "mml",
"plain" : [ "txt", "text", "brf", "conf", "def", "list", "log", "in", "bas" ],
"richtext" : "rtx",
"tab-separated-values" : "tsv",
"x-bibtex" : "bib"
},
"video" : {
"mpeg" : [ "mpeg", "mpg", "mpe", "m1v", "m2v", "mp2", "mpa", "mpv2" ],
"mp4" : [ "mp4", "mp4v", "mpg4" ],
"quicktime" : [ "qt", "mov" ],
"ogg" : "ogv",
"vnd.mpegurl" : [ "mxu", "m4u" ],
"x-flv" : "flv",
"x-la-asf" : [ "lsf", "lsx" ],
"x-mng" : "mng",
"x-ms-asf" : [ "asf", "asx", "asr" ],
"x-ms-wm" : "wm",
"x-ms-wmv" : "wmv",
"x-ms-wmx" : "wmx",
"x-ms-wvx" : "wvx",
"x-msvideo" : "avi",
"x-sgi-movie" : "movie",
"x-matroska" : [ "mpv", "mkv", "mk3d", "mks" ],
"3gpp2" : "3g2",
"h261" : "h261",
"h263" : "h263",
"h264" : "h264",
"jpeg" : "jpgv",
"jpm" : [ "jpm", "jpgm" ],
"mj2" : [ "mj2", "mjp2" ],
"vnd.ms-playready.media.pyv" : "pyv",
"vnd.uvvu.mp4" : [ "uvu", "uvvu" ],
"vnd.vivo" : "viv",
"webm" : "webm",
"x-f4v" : "f4v",
"x-m4v" : "m4v",
"x-ms-vob" : "vob",
"x-smv" : "smv"
}
};
var mimeTypes = (function() {
var type, subtype, val, index, mimeTypes = {};
for (type in table) {
if (table.hasOwnProperty(type)) {
for (subtype in table[type]) {
if (table[type].hasOwnProperty(subtype)) {
val = table[type][subtype];
if (typeof val == "string") {
mimeTypes[val] = type + "/" + subtype;
} else {
for (index = 0; index < val.length; index++) {
mimeTypes[val[index]] = type + "/" + subtype;
}
}
}
}
}
}
return mimeTypes;
})();
var defaultValue = "text/plain";//"application/octet-stream";
function lookup(filename) {
return filename && mimeTypes[filename.split(".").pop().toLowerCase()] || defaultValue;
};
module.exports = {
'lookup': lookup
}
},{}],2:[function(require,module,exports){
},{}],3:[function(require,module,exports){
// shim for using process in browser
var process = module.exports = {};
var queue = [];
var draining = false;
var currentQueue;
var queueIndex = -1;
function cleanUpNextTick() {
draining = false;
if (currentQueue.length) {
queue = currentQueue.concat(queue);
} else {
queueIndex = -1;
}
if (queue.length) {
drainQueue();
}
}
function drainQueue() {
if (draining) {
return;
}
var timeout = setTimeout(cleanUpNextTick);
draining = true;
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
clearTimeout(timeout);
}
process.nextTick = function (fun) {
var args = new Array(arguments.length - 1);
if (arguments.length > 1) {
for (var i = 1; i < arguments.length; i++) {
args[i - 1] = arguments[i];
}
}
queue.push(new Item(fun, args));
if (queue.length === 1 && !draining) {
setTimeout(drainQueue, 0);
}
};
// v8 likes predictible objects
function Item(fun, array) {
this.fun = fun;
this.array = array;
}
Item.prototype.run = function () {
this.fun.apply(null, this.array);
};
process.title = 'browser';
process.browser = true;
process.env = {};
process.argv = [];
process.version = ''; // empty string to avoid regexp issues
process.versions = {};
function noop() {}
process.on = noop;
process.addListener = noop;
process.once = noop;
process.off = noop;
process.removeListener = noop;
process.removeAllListeners = noop;
process.emit = noop;
process.binding = function (name) {
throw new Error('process.binding is not supported');
};
process.cwd = function () { return '/' };
process.chdir = function (dir) {
throw new Error('process.chdir is not supported');
};
process.umask = function() { return 0; };
},{}],4:[function(require,module,exports){
(function (process){
/*!
* @overview RSVP - a tiny implementation of Promises/A+.
* @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors
* @license Licensed under MIT license
* See https://raw.githubusercontent.com/tildeio/rsvp.js/master/LICENSE
* @version 3.0.14
*/
(function() {
"use strict";
function $$rsvp$events$$indexOf(callbacks, callback) {
for (var i=0, l=callbacks.length; i<l; i++) {
if (callbacks[i] === callback) { return i; }
}
return -1;
}
function $$rsvp$events$$callbacksFor(object) {
var callbacks = object._promiseCallbacks;
if (!callbacks) {
callbacks = object._promiseCallbacks = {};
}
return callbacks;
}
var $$rsvp$events$$default = {
/**
`RSVP.EventTarget.mixin` extends an object with EventTarget methods. For
Example:
```javascript
var object = {};
RSVP.EventTarget.mixin(object);
object.on('finished', function(event) {
// handle event
});
object.trigger('finished', { detail: value });
```
`EventTarget.mixin` also works with prototypes:
```javascript
var Person = function() {};
RSVP.EventTarget.mixin(Person.prototype);
var yehuda = new Person();
var tom = new Person();
yehuda.on('poke', function(event) {
console.log('Yehuda says OW');
});
tom.on('poke', function(event) {
console.log('Tom says OW');
});
yehuda.trigger('poke');
tom.trigger('poke');
```
@method mixin
@for RSVP.EventTarget
@private
@param {Object} object object to extend with EventTarget methods
*/
mixin: function(object) {
object.on = this.on;
object.off = this.off;
object.trigger = this.trigger;
object._promiseCallbacks = undefined;
return object;
},
/**
Registers a callback to be executed when `eventName` is triggered
```javascript
object.on('event', function(eventInfo){
// handle the event
});
object.trigger('event');
```
@method on
@for RSVP.EventTarget
@private
@param {String} eventName name of the event to listen for
@param {Function} callback function to be called when the event is triggered.
*/
on: function(eventName, callback) {
var allCallbacks = $$rsvp$events$$callbacksFor(this), callbacks;
callbacks = allCallbacks[eventName];
if (!callbacks) {
callbacks = allCallbacks[eventName] = [];
}
if ($$rsvp$events$$indexOf(callbacks, callback) === -1) {
callbacks.push(callback);
}
},
/**
You can use `off` to stop firing a particular callback for an event:
```javascript
function doStuff() { // do stuff! }
object.on('stuff', doStuff);
object.trigger('stuff'); // doStuff will be called
// Unregister ONLY the doStuff callback
object.off('stuff', doStuff);
object.trigger('stuff'); // doStuff will NOT be called
```
If you don't pass a `callback` argument to `off`, ALL callbacks for the
event will not be executed when the event fires. For example:
```javascript
var callback1 = function(){};
var callback2 = function(){};
object.on('stuff', callback1);
object.on('stuff', callback2);
object.trigger('stuff'); // callback1 and callback2 will be executed.
object.off('stuff');
object.trigger('stuff'); // callback1 and callback2 will not be executed!
```
@method off
@for RSVP.EventTarget
@private
@param {String} eventName event to stop listening to
@param {Function} callback optional argument. If given, only the function
given will be removed from the event's callback queue. If no `callback`
argument is given, all callbacks will be removed from the event's callback
queue.
*/
off: function(eventName, callback) {
var allCallbacks = $$rsvp$events$$callbacksFor(this), callbacks, index;
if (!callback) {
allCallbacks[eventName] = [];
return;
}
callbacks = allCallbacks[eventName];
index = $$rsvp$events$$indexOf(callbacks, callback);
if (index !== -1) { callbacks.splice(index, 1); }
},
/**
Use `trigger` to fire custom events. For example:
```javascript
object.on('foo', function(){
console.log('foo event happened!');
});
object.trigger('foo');
// 'foo event happened!' logged to the console
```
You can also pass a value as a second argument to `trigger` that will be
passed as an argument to all event listeners for the event:
```javascript
object.on('foo', function(value){
console.log(value.name);
});
object.trigger('foo', { name: 'bar' });
// 'bar' logged to the console
```
@method trigger
@for RSVP.EventTarget
@private
@param {String} eventName name of the event to be triggered
@param {Any} options optional value to be passed to any event handlers for
the given `eventName`
*/
trigger: function(eventName, options) {
var allCallbacks = $$rsvp$events$$callbacksFor(this), callbacks, callback;
if (callbacks = allCallbacks[eventName]) {
// Don't cache the callbacks.length since it may grow
for (var i=0; i<callbacks.length; i++) {
callback = callbacks[i];
callback(options);
}
}
}
};
var $$rsvp$config$$config = {
instrument: false
};
$$rsvp$events$$default.mixin($$rsvp$config$$config);
function $$rsvp$config$$configure(name, value) {
if (name === 'onerror') {
// handle for legacy users that expect the actual
// error to be passed to their function added via
// `RSVP.configure('onerror', someFunctionHere);`
$$rsvp$config$$config.on('error', value);
return;
}
if (arguments.length === 2) {
$$rsvp$config$$config[name] = value;
} else {
return $$rsvp$config$$config[name];
}
}
function $$utils$$objectOrFunction(x) {
return typeof x === 'function' || (typeof x === 'object' && x !== null);
}
function $$utils$$isFunction(x) {
return typeof x === 'function';
}
function $$utils$$isMaybeThenable(x) {
return typeof x === 'object' && x !== null;
}
var $$utils$$_isArray;
if (!Array.isArray) {
$$utils$$_isArray = function (x) {
return Object.prototype.toString.call(x) === '[object Array]';
};
} else {
$$utils$$_isArray = Array.isArray;
}
var $$utils$$isArray = $$utils$$_isArray;
var $$utils$$now = Date.now || function() { return new Date().getTime(); };
function $$utils$$F() { }
var $$utils$$o_create = (Object.create || function (o) {
if (arguments.length > 1) {
throw new Error('Second argument not supported');
}
if (typeof o !== 'object') {
throw new TypeError('Argument must be an object');
}
$$utils$$F.prototype = o;
return new $$utils$$F();
});
var $$instrument$$queue = [];
var $$instrument$$default = function instrument(eventName, promise, child) {
if (1 === $$instrument$$queue.push({
name: eventName,
payload: {
guid: promise._guidKey + promise._id,
eventName: eventName,
detail: promise._result,
childGuid: child && promise._guidKey + child._id,
label: promise._label,
timeStamp: $$utils$$now(),
stack: new Error(promise._label).stack
}})) {
setTimeout(function() {
var entry;
for (var i = 0; i < $$instrument$$queue.length; i++) {
entry = $$instrument$$queue[i];
$$rsvp$config$$config.trigger(entry.name, entry.payload);
}
$$instrument$$queue.length = 0;
}, 50);
}
};
function $$$internal$$noop() {}
var $$$internal$$PENDING = void 0;
var $$$internal$$FULFILLED = 1;
var $$$internal$$REJECTED = 2;
var $$$internal$$GET_THEN_ERROR = new $$$internal$$ErrorObject();
function $$$internal$$getThen(promise) {
try {
return promise.then;
} catch(error) {
$$$internal$$GET_THEN_ERROR.error = error;
return $$$internal$$GET_THEN_ERROR;
}
}
function $$$internal$$tryThen(then, value, fulfillmentHandler, rejectionHandler) {
try {
then.call(value, fulfillmentHandler, rejectionHandler);
} catch(e) {
return e;
}
}
function $$$internal$$handleForeignThenable(promise, thenable, then) {
$$rsvp$config$$config.async(function(promise) {
var sealed = false;
var error = $$$internal$$tryThen(then, thenable, function(value) {
if (sealed) { return; }
sealed = true;
if (thenable !== value) {
$$$internal$$resolve(promise, value);
} else {
$$$internal$$fulfill(promise, value);
}
}, function(reason) {
if (sealed) { return; }
sealed = true;
$$$internal$$reject(promise, reason);
}, 'Settle: ' + (promise._label || ' unknown promise'));
if (!sealed && error) {
sealed = true;
$$$internal$$reject(promise, error);
}
}, promise);
}
function $$$internal$$handleOwnThenable(promise, thenable) {
if (thenable._state === $$$internal$$FULFILLED) {
$$$internal$$fulfill(promise, thenable._result);
} else if (promise._state === $$$internal$$REJECTED) {
$$$internal$$reject(promise, thenable._result);
} else {
$$$internal$$subscribe(thenable, undefined, function(value) {
if (thenable !== value) {
$$$internal$$resolve(promise, value);
} else {
$$$internal$$fulfill(promise, value);
}
}, function(reason) {
$$$internal$$reject(promise, reason);
});
}
}
function $$$internal$$handleMaybeThenable(promise, maybeThenable) {
if (maybeThenable.constructor === promise.constructor) {
$$$internal$$handleOwnThenable(promise, maybeThenable);
} else {
var then = $$$internal$$getThen(maybeThenable);
if (then === $$$internal$$GET_THEN_ERROR) {
$$$internal$$reject(promise, $$$internal$$GET_THEN_ERROR.error);
} else if (then === undefined) {
$$$internal$$fulfill(promise, maybeThenable);
} else if ($$utils$$isFunction(then)) {
$$$internal$$handleForeignThenable(promise, maybeThenable, then);
} else {
$$$internal$$fulfill(promise, maybeThenable);
}
}
}
function $$$internal$$resolve(promise, value) {
if (promise === value) {
$$$internal$$fulfill(promise, value);
} else if ($$utils$$objectOrFunction(value)) {
$$$internal$$handleMaybeThenable(promise, value);
} else {
$$$internal$$fulfill(promise, value);
}
}
function $$$internal$$publishRejection(promise) {
if (promise._onerror) {
promise._onerror(promise._result);
}
$$$internal$$publish(promise);
}
function $$$internal$$fulfill(promise, value) {
if (promise._state !== $$$internal$$PENDING) { return; }
promise._result = value;
promise._state = $$$internal$$FULFILLED;
if (promise._subscribers.length === 0) {
if ($$rsvp$config$$config.instrument) {
$$instrument$$default('fulfilled', promise);
}
} else {
$$rsvp$config$$config.async($$$internal$$publish, promise);
}
}
function $$$internal$$reject(promise, reason) {
if (promise._state !== $$$internal$$PENDING) { return; }
promise._state = $$$internal$$REJECTED;
promise._result = reason;
$$rsvp$config$$config.async($$$internal$$publishRejection, promise);
}
function $$$internal$$subscribe(parent, child, onFulfillment, onRejection) {
var subscribers = parent._subscribers;
var length = subscribers.length;
parent._onerror = null;
subscribers[length] = child;
subscribers[length + $$$internal$$FULFILLED] = onFulfillment;
subscribers[length + $$$internal$$REJECTED] = onRejection;
if (length === 0 && parent._state) {
$$rsvp$config$$config.async($$$internal$$publish, parent);
}
}
function $$$internal$$publish(promise) {
var subscribers = promise._subscribers;
var settled = promise._state;
if ($$rsvp$config$$config.instrument) {
$$instrument$$default(settled === $$$internal$$FULFILLED ? 'fulfilled' : 'rejected', promise);
}
if (subscribers.length === 0) { return; }
var child, callback, detail = promise._result;
for (var i = 0; i < subscribers.length; i += 3) {
child = subscribers[i];
callback = subscribers[i + settled];
if (child) {
$$$internal$$invokeCallback(settled, child, callback, detail);
} else {
callback(detail);
}
}
promise._subscribers.length = 0;
}
function $$$internal$$ErrorObject() {
this.error = null;
}
var $$$internal$$TRY_CATCH_ERROR = new $$$internal$$ErrorObject();
function $$$internal$$tryCatch(callback, detail) {
try {
return callback(detail);
} catch(e) {
$$$internal$$TRY_CATCH_ERROR.error = e;
return $$$internal$$TRY_CATCH_ERROR;
}
}
function $$$internal$$invokeCallback(settled, promise, callback, detail) {
var hasCallback = $$utils$$isFunction(callback),
value, error, succeeded, failed;
if (hasCallback) {
value = $$$internal$$tryCatch(callback, detail);
if (value === $$$internal$$TRY_CATCH_ERROR) {
failed = true;
error = value.error;
value = null;
} else {
succeeded = true;
}
if (promise === value) {
$$$internal$$reject(promise, new TypeError('A promises callback cannot return that same promise.'));
return;
}
} else {
value = detail;
succeeded = true;
}
if (promise._state !== $$$internal$$PENDING) {
// noop
} else if (hasCallback && succeeded) {
$$$internal$$resolve(promise, value);
} else if (failed) {
$$$internal$$reject(promise, error);
} else if (settled === $$$internal$$FULFILLED) {
$$$internal$$fulfill(promise, value);
} else if (settled === $$$internal$$REJECTED) {
$$$internal$$reject(promise, value);
}
}
function $$$internal$$initializePromise(promise, resolver) {
try {
resolver(function resolvePromise(value){
$$$internal$$resolve(promise, value);
}, function rejectPromise(reason) {
$$$internal$$reject(promise, reason);
});
} catch(e) {
$$$internal$$reject(promise, e);
}
}
function $$enumerator$$makeSettledResult(state, position, value) {
if (state === $$$internal$$FULFILLED) {
return {
state: 'fulfilled',
value: value
};
} else {
return {
state: 'rejected',
reason: value
};
}
}
function $$enumerator$$Enumerator(Constructor, input, abortOnReject, label) {
this._instanceConstructor = Constructor;
this.promise = new Constructor($$$internal$$noop, label);
this._abortOnReject = abortOnReject;
if (this._validateInput(input)) {
this._input = input;
this.length = input.length;
this._remaining = input.length;
this._init();
if (this.length === 0) {
$$$internal$$fulfill(this.promise, this._result);
} else {
this.length = this.length || 0;
this._enumerate();
if (this._remaining === 0) {
$$$internal$$fulfill(this.promise, this._result);
}
}
} else {
$$$internal$$reject(this.promise, this._validationError());
}
}
$$enumerator$$Enumerator.prototype._validateInput = function(input) {
return $$utils$$isArray(input);
};
$$enumerator$$Enumerator.prototype._validationError = function() {
return new Error('Array Methods must be provided an Array');
};
$$enumerator$$Enumerator.prototype._init = function() {
this._result = new Array(this.length);
};
var $$enumerator$$default = $$enumerator$$Enumerator;
$$enumerator$$Enumerator.prototype._enumerate = function() {
var length = this.length;
var promise = this.promise;
var input = this._input;
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
this._eachEntry(input[i], i);
}
};
$$enumerator$$Enumerator.prototype._eachEntry = function(entry, i) {
var c = this._instanceConstructor;
if ($$utils$$isMaybeThenable(entry)) {
if (entry.constructor === c && entry._state !== $$$internal$$PENDING) {
entry._onerror = null;
this._settledAt(entry._state, i, entry._result);
} else {
this._willSettleAt(c.resolve(entry), i);
}
} else {
this._remaining--;
this._result[i] = this._makeResult($$$internal$$FULFILLED, i, entry);
}
};
$$enumerator$$Enumerator.prototype._settledAt = function(state, i, value) {
var promise = this.promise;
if (promise._state === $$$internal$$PENDING) {
this._remaining--;
if (this._abortOnReject && state === $$$internal$$REJECTED) {
$$$internal$$reject(promise, value);
} else {
this._result[i] = this._makeResult(state, i, value);
}
}
if (this._remaining === 0) {
$$$internal$$fulfill(promise, this._result);
}
};
$$enumerator$$Enumerator.prototype._makeResult = function(state, i, value) {
return value;
};
$$enumerator$$Enumerator.prototype._willSettleAt = function(promise, i) {
var enumerator = this;
$$$internal$$subscribe(promise, undefined, function(value) {
enumerator._settledAt($$$internal$$FULFILLED, i, value);
}, function(reason) {
enumerator._settledAt($$$internal$$REJECTED, i, reason);
});
};
var $$promise$all$$default = function all(entries, label) {
return new $$enumerator$$default(this, entries, true /* abort on reject */, label).promise;
};
var $$promise$race$$default = function race(entries, label) {
/*jshint validthis:true */
var Constructor = this;
var promise = new Constructor($$$internal$$noop, label);
if (!$$utils$$isArray(entries)) {
$$$internal$$reject(promise, new TypeError('You must pass an array to race.'));
return promise;
}
var length = entries.length;
function onFulfillment(value) {
$$$internal$$resolve(promise, value);
}
function onRejection(reason) {
$$$internal$$reject(promise, reason);
}
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
$$$internal$$subscribe(Constructor.resolve(entries[i]), undefined, onFulfillment, onRejection);
}
return promise;
};
var $$promise$resolve$$default = function resolve(object, label) {
/*jshint validthis:true */
var Constructor = this;
if (object && typeof object === 'object' && object.constructor === Constructor) {
return object;
}
var promise = new Constructor($$$internal$$noop, label);
$$$internal$$resolve(promise, object);
return promise;
};
var $$promise$reject$$default = function reject(reason, label) {
/*jshint validthis:true */
var Constructor = this;
var promise = new Constructor($$$internal$$noop, label);
$$$internal$$reject(promise, reason);
return promise;
};
var $$rsvp$promise$$guidKey = 'rsvp_' + $$utils$$now() + '-';
var $$rsvp$promise$$counter = 0;
function $$rsvp$promise$$needsResolver() {
throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
}
function $$rsvp$promise$$needsNew() {
throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
}
var $$rsvp$promise$$default = $$rsvp$promise$$Promise;
/**
Promise objects represent the eventual result of an asynchronous operation. The
primary way of interacting with a promise is through its `then` method, which
registers callbacks to receive either a promises eventual value or the reason
why the promise cannot be fulfilled.
Terminology
-----------
- `promise` is an object or function with a `then` method whose behavior conforms to this specification.
- `thenable` is an object or function that defines a `then` method.
- `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
- `exception` is a value that is thrown using the throw statement.
- `reason` is a value that indicates why a promise was rejected.
- `settled` the final resting state of a promise, fulfilled or rejected.
A promise can be in one of three states: pending, fulfilled, or rejected.
Promises that are fulfilled have a fulfillment value and are in the fulfilled
state. Promises that are rejected have a rejection reason and are in the
rejected state. A fulfillment value is never a thenable.
Promises can also be said to *resolve* a value. If this value is also a
promise, then the original promise's settled state will match the value's
settled state. So a promise that *resolves* a promise that rejects will
itself reject, and a promise that *resolves* a promise that fulfills will
itself fulfill.
Basic Usage:
------------
```js
var promise = new Promise(function(resolve, reject) {
// on success
resolve(value);
// on failure
reject(reason);
});
promise.then(function(value) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Advanced Usage:
---------------
Promises shine when abstracting away asynchronous interactions such as
`XMLHttpRequest`s.
```js
function getJSON(url) {
return new Promise(function(resolve, reject){
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = handler;
xhr.responseType = 'json';
xhr.setRequestHeader('Accept', 'application/json');
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200) {
resolve(this.response);
} else {
reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
}
}
};
});
}
getJSON('/posts.json').then(function(json) {
// on fulfillment
}, function(reason) {
// on rejection
});
```
Unlike callbacks, promises are great composable primitives.
```js
Promise.all([
getJSON('/posts'),
getJSON('/comments')
]).then(function(values){
values[0] // => postsJSON
values[1] // => commentsJSON
return values;
});
```
@class RSVP.Promise
@param {function} resolver
@param {String} label optional string for labeling the promise.
Useful for tooling.
@constructor
*/
function $$rsvp$promise$$Promise(resolver, label) {
this._id = $$rsvp$promise$$counter++;
this._label = label;
this._state = undefined;
this._result = undefined;
this._subscribers = [];
if ($$rsvp$config$$config.instrument) {
$$instrument$$default('created', this);
}
if ($$$internal$$noop !== resolver) {
if (!$$utils$$isFunction(resolver)) {
$$rsvp$promise$$needsResolver();
}
if (!(this instanceof $$rsvp$promise$$Promise)) {
$$rsvp$promise$$needsNew();
}
$$$internal$$initializePromise(this, resolver);
}
}
// deprecated
$$rsvp$promise$$Promise.cast = $$promise$resolve$$default;
$$rsvp$promise$$Promise.all = $$promise$all$$default;
$$rsvp$promise$$Promise.race = $$promise$race$$default;
$$rsvp$promise$$Promise.resolve = $$promise$resolve$$default;
$$rsvp$promise$$Promise.reject = $$promise$reject$$default;
$$rsvp$promise$$Promise.prototype = {
constructor: $$rsvp$promise$$Promise,
_guidKey: $$rsvp$promise$$guidKey,
_onerror: function (reason) {
$$rsvp$config$$config.trigger('error', reason);
},
/**
The primary way of interacting with a promise is through its `then` method,
which registers callbacks to receive either a promise's eventual value or the
reason why the promise cannot be fulfilled.
```js
findUser().then(function(user){
// user is available
}, function(reason){
// user is unavailable, and you are given the reason why
});
```
Chaining
--------
The return value of `then` is itself a promise. This second, 'downstream'
promise is resolved with the return value of the first promise's fulfillment
or rejection handler, or rejected if the handler throws an exception.
```js
findUser().then(function (user) {
return user.name;
}, function (reason) {
return 'default name';
}).then(function (userName) {
// If `findUser` fulfilled, `userName` will be the user's name, otherwise it
// will be `'default name'`
});
findUser().then(function (user) {
throw new Error('Found user, but still unhappy');
}, function (reason) {
throw new Error('`findUser` rejected and we're unhappy');
}).then(function (value) {
// never reached
}, function (reason) {
// if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
// If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
});
```
If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
```js
findUser().then(function (user) {
throw new PedagogicalException('Upstream error');
}).then(function (value) {
// never reached
}).then(function (value) {
// never reached
}, function (reason) {
// The `PedgagocialException` is propagated all the way down to here
});
```
Assimilation
------------
Sometimes the value you want to propagate to a downstream promise can only be
retrieved asynchronously. This can be achieved by returning a promise in the
fulfillment or rejection handler. The downstream promise will then be pending
until the returned promise is settled. This is called *assimilation*.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// The user's comments are now available
});
```
If the assimliated promise rejects, then the downstream promise will also reject.
```js
findUser().then(function (user) {
return findCommentsByAuthor(user);
}).then(function (comments) {
// If `findCommentsByAuthor` fulfills, we'll have the value here
}, function (reason) {
// If `findCommentsByAuthor` rejects, we'll have the reason here
});
```
Simple Example
--------------
Synchronous Example
```javascript
var result;
try {
result = findResult();
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
findResult(function(result, err){
if (err) {
// failure
} else {
// success
}
});
```
Promise Example;
```javascript
findResult().then(function(result){
// success
}, function(reason){
// failure
});
```
Advanced Example
--------------
Synchronous Example
```javascript
var author, books;
try {
author = findAuthor();
books = findBooksByAuthor(author);
// success
} catch(reason) {
// failure
}
```
Errback Example
```js
function foundBooks(books) {
}
function failure(reason) {
}
findAuthor(function(author, err){
if (err) {
failure(err);
// failure
} else {
try {
findBoooksByAuthor(author, function(books, err) {
if (err) {
failure(err);
} else {
try {
foundBooks(books);
} catch(reason) {
failure(reason);
}
}
});
} catch(error) {
failure(err);
}
// success
}
});
```
Promise Example;
```javascript
findAuthor().
then(findBooksByAuthor).
then(function(books){
// found books
}).catch(function(reason){
// something went wrong
});
```
@method then
@param {Function} onFulfilled
@param {Function} onRejected
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
then: function(onFulfillment, onRejection, label) {
var parent = this;
var state = parent._state;
if (state === $$$internal$$FULFILLED && !onFulfillment || state === $$$internal$$REJECTED && !onRejection) {
if ($$rsvp$config$$config.instrument) {
$$instrument$$default('chained', this, this);
}
return this;
}
parent._onerror = null;
var child = new this.constructor($$$internal$$noop, label);
var result = parent._result;
if ($$rsvp$config$$config.instrument) {
$$instrument$$default('chained', parent, child);
}
if (state) {
var callback = arguments[state - 1];
$$rsvp$config$$config.async(function(){
$$$internal$$invokeCallback(state, child, callback, result);
});
} else {
$$$internal$$subscribe(parent, child, onFulfillment, onRejection);
}
return child;
},
/**
`catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
as the catch block of a try/catch statement.
```js
function findAuthor(){
throw new Error('couldn't find that author');
}
// synchronous
try {
findAuthor();
} catch(reason) {
// something went wrong
}
// async with promises
findAuthor().catch(function(reason){
// something went wrong
});
```
@method catch
@param {Function} onRejection
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
'catch': function(onRejection, label) {
return this.then(null, onRejection, label);
},
/**
`finally` will be invoked regardless of the promise's fate just as native
try/catch/finally behaves
Synchronous example:
```js
findAuthor() {
if (Math.random() > 0.5) {
throw new Error();
}
return new Author();
}
try {
return findAuthor(); // succeed or fail
} catch(error) {
return findOtherAuther();
} finally {
// always runs
// doesn't affect the return value
}
```
Asynchronous example:
```js
findAuthor().catch(function(reason){
return findOtherAuther();
}).finally(function(){
// author was either found, or not
});
```
@method finally
@param {Function} callback
@param {String} label optional string for labeling the promise.
Useful for tooling.
@return {Promise}
*/
'finally': function(callback, label) {
var constructor = this.constructor;
return this.then(function(value) {
return constructor.resolve(callback()).then(function(){
return value;
});
}, function(reason) {
return constructor.resolve(callback()).then(function(){
throw reason;
});
}, label);
}
};
function $$rsvp$node$$Result() {
this.value = undefined;
}
var $$rsvp$node$$ERROR = new $$rsvp$node$$Result();
var $$rsvp$node$$GET_THEN_ERROR = new $$rsvp$node$$Result();
function $$rsvp$node$$getThen(obj) {
try {
return obj.then;
} catch(error) {
$$rsvp$node$$ERROR.value= error;
return $$rsvp$node$$ERROR;
}
}
function $$rsvp$node$$tryApply(f, s, a) {
try {
f.apply(s, a);
} catch(error) {
$$rsvp$node$$ERROR.value = error;
return $$rsvp$node$$ERROR;
}
}
function $$rsvp$node$$makeObject(_, argumentNames) {
var obj = {};
var name;
var i;
var length = _.length;
var args = new Array(length);
for (var x = 0; x < length; x++) {
args[x] = _[x];
}
for (i = 0; i < argumentNames.length; i++) {
name = argumentNames[i];
obj[name] = args[i + 1];
}
return obj;
}
function $$rsvp$node$$arrayResult(_) {
var length = _.length;
var args = new Array(length - 1);
for (var i = 1; i < length; i++) {
args[i - 1] = _[i];
}
return args;
}
function $$rsvp$node$$wrapThenable(then, promise) {
return {
then: function(onFulFillment, onRejection) {
return then.call(promise, onFulFillment, onRejection);
}
};
}
var $$rsvp$node$$default = function denodeify(nodeFunc, options) {
var fn = function() {
var self = this;
var l = arguments.length;
var args = new Array(l + 1);
var arg;
var promiseInput = false;
for (var i = 0; i < l; ++i) {
arg = arguments[i];
if (!promiseInput) {
// TODO: clean this up
promiseInput = $$rsvp$node$$needsPromiseInput(arg);
if (promiseInput === $$rsvp$node$$GET_THEN_ERROR) {
var p = new $$rsvp$promise$$default($$$internal$$noop);
$$$internal$$reject(p, $$rsvp$node$$GET_THEN_ERROR.value);
return p;
} else if (promiseInput && promiseInput !== true) {
arg = $$rsvp$node$$wrapThenable(promiseInput, arg);
}
}
args[i] = arg;
}
var promise = new $$rsvp$promise$$default($$$internal$$noop);
args[l] = function(err, val) {
if (err)
$$$internal$$reject(promise, err);
else if (options === undefined)
$$$internal$$resolve(promise, val);
else if (options === true)
$$$internal$$resolve(promise, $$rsvp$node$$arrayResult(arguments));
else if ($$utils$$isArray(options))
$$$internal$$resolve(promise, $$rsvp$node$$makeObject(arguments, options));
else
$$$internal$$resolve(promise, val);
};
if (promiseInput) {
return $$rsvp$node$$handlePromiseInput(promise, args, nodeFunc, self);
} else {
return $$rsvp$node$$handleValueInput(promise, args, nodeFunc, self);
}
};
fn.__proto__ = nodeFunc;
return fn;
};
function $$rsvp$node$$handleValueInput(promise, args, nodeFunc, self) {
var result = $$rsvp$node$$tryApply(nodeFunc, self, args);
if (result === $$rsvp$node$$ERROR) {
$$$internal$$reject(promise, result.value);
}
return promise;
}
function $$rsvp$node$$handlePromiseInput(promise, args, nodeFunc, self){
return $$rsvp$promise$$default.all(args).then(function(args){
var result = $$rsvp$node$$tryApply(nodeFunc, self, args);
if (result === $$rsvp$node$$ERROR) {
$$$internal$$reject(promise, result.value);
}
return promise;
});
}
function $$rsvp$node$$needsPromiseInput(arg) {
if (arg && typeof arg === 'object') {
if (arg.constructor === $$rsvp$promise$$default) {
return true;
} else {
return $$rsvp$node$$getThen(arg);
}
} else {
return false;
}
}
var $$rsvp$all$$default = function all(array, label) {
return $$rsvp$promise$$default.all(array, label);
};
function $$rsvp$all$settled$$AllSettled(Constructor, entries, label) {
this._superConstructor(Constructor, entries, false /* don't abort on reject */, label);
}
$$rsvp$all$settled$$AllSettled.prototype = $$utils$$o_create($$enumerator$$default.prototype);
$$rsvp$all$settled$$AllSettled.prototype._superConstructor = $$enumerator$$default;
$$rsvp$all$settled$$AllSettled.prototype._makeResult = $$enumerator$$makeSettledResult;
$$rsvp$all$settled$$AllSettled.prototype._validationError = function() {
return new Error('allSettled must be called with an array');
};
var $$rsvp$all$settled$$default = function allSettled(entries, label) {
return new $$rsvp$all$settled$$AllSettled($$rsvp$promise$$default, entries, label).promise;
};
var $$rsvp$race$$default = function race(array, label) {
return $$rsvp$promise$$default.race(array, label);
};
function $$promise$hash$$PromiseHash(Constructor, object, label) {
this._superConstructor(Constructor, object, true, label);
}
var $$promise$hash$$default = $$promise$hash$$PromiseHash;
$$promise$hash$$PromiseHash.prototype = $$utils$$o_create($$enumerator$$default.prototype);
$$promise$hash$$PromiseHash.prototype._superConstructor = $$enumerator$$default;
$$promise$hash$$PromiseHash.prototype._init = function() {
this._result = {};
};
$$promise$hash$$PromiseHash.prototype._validateInput = function(input) {
return input && typeof input === 'object';
};
$$promise$hash$$PromiseHash.prototype._validationError = function() {
return new Error('Promise.hash must be called with an object');
};
$$promise$hash$$PromiseHash.prototype._enumerate = function() {
var promise = this.promise;
var input = this._input;
var results = [];
for (var key in input) {
if (promise._state === $$$internal$$PENDING && input.hasOwnProperty(key)) {
results.push({
position: key,
entry: input[key]
});
}
}
var length = results.length;
this._remaining = length;
var result;
for (var i = 0; promise._state === $$$internal$$PENDING && i < length; i++) {
result = results[i];
this._eachEntry(result.entry, result.position);
}
};
var $$rsvp$hash$$default = function hash(object, label) {
return new $$promise$hash$$default($$rsvp$promise$$default, object, label).promise;
};
function $$rsvp$hash$settled$$HashSettled(Constructor, object, label) {
this._superConstructor(Constructor, object, false, label);
}
$$rsvp$hash$settled$$HashSettled.prototype = $$utils$$o_create($$promise$hash$$default.prototype);
$$rsvp$hash$settled$$HashSettled.prototype._superConstructor = $$enumerator$$default;
$$rsvp$hash$settled$$HashSettled.prototype._makeResult = $$enumerator$$makeSettledResult;
$$rsvp$hash$settled$$HashSettled.prototype._validationError = function() {
return new Error('hashSettled must be called with an object');
};
var $$rsvp$hash$settled$$default = function hashSettled(object, label) {
return new $$rsvp$hash$settled$$HashSettled($$rsvp$promise$$default, object, label).promise;
};
var $$rsvp$rethrow$$default = function rethrow(reason) {
setTimeout(function() {
throw reason;
});
throw reason;
};
var $$rsvp$defer$$default = function defer(label) {
var deferred = { };
deferred.promise = new $$rsvp$promise$$default(function(resolve, reject) {
deferred.resolve = resolve;
deferred.reject = reject;
}, label);
return deferred;
};
var $$rsvp$map$$default = function map(promises, mapFn, label) {
return $$rsvp$promise$$default.all(promises, label).then(function(values) {
if (!$$utils$$isFunction(mapFn)) {
throw new TypeError("You must pass a function as map's second argument.");
}
var length = values.length;
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = mapFn(values[i]);
}
return $$rsvp$promise$$default.all(results, label);
});
};
var $$rsvp$resolve$$default = function resolve(value, label) {
return $$rsvp$promise$$default.resolve(value, label);
};
var $$rsvp$reject$$default = function reject(reason, label) {
return $$rsvp$promise$$default.reject(reason, label);
};
var $$rsvp$filter$$default = function filter(promises, filterFn, label) {
return $$rsvp$promise$$default.all(promises, label).then(function(values) {
if (!$$utils$$isFunction(filterFn)) {
throw new TypeError("You must pass a function as filter's second argument.");
}
var length = values.length;
var filtered = new Array(length);
for (var i = 0; i < length; i++) {
filtered[i] = filterFn(values[i]);
}
return $$rsvp$promise$$default.all(filtered, label).then(function(filtered) {
var results = new Array(length);
var newLength = 0;
for (var i = 0; i < length; i++) {
if (filtered[i]) {
results[newLength] = values[i];
newLength++;
}
}
results.length = newLength;
return results;
});
});
};
var $$rsvp$asap$$len = 0;
var $$rsvp$asap$$default = function asap(callback, arg) {
$$rsvp$asap$$queue[$$rsvp$asap$$len] = callback;
$$rsvp$asap$$queue[$$rsvp$asap$$len + 1] = arg;
$$rsvp$asap$$len += 2;
if ($$rsvp$asap$$len === 2) {
// If len is 1, that means that we need to schedule an async flush.
// If additional callbacks are queued before the queue is flushed, they
// will be processed by this flush that we are scheduling.
$$rsvp$asap$$scheduleFlush();
}
};
var $$rsvp$asap$$browserGlobal = (typeof window !== 'undefined') ? window : {};
var $$rsvp$asap$$BrowserMutationObserver = $$rsvp$asap$$browserGlobal.MutationObserver || $$rsvp$asap$$browserGlobal.WebKitMutationObserver;
// test for web worker but not in IE10
var $$rsvp$asap$$isWorker = typeof Uint8ClampedArray !== 'undefined' &&
typeof importScripts !== 'undefined' &&
typeof MessageChannel !== 'undefined';
// node
function $$rsvp$asap$$useNextTick() {
return function() {
process.nextTick($$rsvp$asap$$flush);
};
}
function $$rsvp$asap$$useMutationObserver() {
var iterations = 0;
var observer = new $$rsvp$asap$$BrowserMutationObserver($$rsvp$asap$$flush);
var node = document.createTextNode('');
observer.observe(node, { characterData: true });
return function() {
node.data = (iterations = ++iterations % 2);
};
}
// web worker
function $$rsvp$asap$$useMessageChannel() {
var channel = new MessageChannel();
channel.port1.onmessage = $$rsvp$asap$$flush;
return function () {
channel.port2.postMessage(0);
};
}
function $$rsvp$asap$$useSetTimeout() {
return function() {
setTimeout($$rsvp$asap$$flush, 1);
};
}
var $$rsvp$asap$$queue = new Array(1000);
function $$rsvp$asap$$flush() {
for (var i = 0; i < $$rsvp$asap$$len; i+=2) {
var callback = $$rsvp$asap$$queue[i];
var arg = $$rsvp$asap$$queue[i+1];
callback(arg);
$$rsvp$asap$$queue[i] = undefined;
$$rsvp$asap$$queue[i+1] = undefined;
}
$$rsvp$asap$$len = 0;
}
var $$rsvp$asap$$scheduleFlush;
// Decide what async method to use to triggering processing of queued callbacks:
if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
$$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useNextTick();
} else if ($$rsvp$asap$$BrowserMutationObserver) {
$$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useMutationObserver();
} else if ($$rsvp$asap$$isWorker) {
$$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useMessageChannel();
} else {
$$rsvp$asap$$scheduleFlush = $$rsvp$asap$$useSetTimeout();
}
// default async is asap;
$$rsvp$config$$config.async = $$rsvp$asap$$default;
var $$rsvp$$cast = $$rsvp$resolve$$default;
function $$rsvp$$async(callback, arg) {
$$rsvp$config$$config.async(callback, arg);
}
function $$rsvp$$on() {
$$rsvp$config$$config.on.apply($$rsvp$config$$config, arguments);
}
function $$rsvp$$off() {
$$rsvp$config$$config.off.apply($$rsvp$config$$config, arguments);
}
// Set up instrumentation through `window.__PROMISE_INTRUMENTATION__`
if (typeof window !== 'undefined' && typeof window['__PROMISE_INSTRUMENTATION__'] === 'object') {
var $$rsvp$$callbacks = window['__PROMISE_INSTRUMENTATION__'];
$$rsvp$config$$configure('instrument', true);
for (var $$rsvp$$eventName in $$rsvp$$callbacks) {
if ($$rsvp$$callbacks.hasOwnProperty($$rsvp$$eventName)) {
$$rsvp$$on($$rsvp$$eventName, $$rsvp$$callbacks[$$rsvp$$eventName]);
}
}
}
var rsvp$umd$$RSVP = {
'race': $$rsvp$race$$default,
'Promise': $$rsvp$promise$$default,
'allSettled': $$rsvp$all$settled$$default,
'hash': $$rsvp$hash$$default,
'hashSettled': $$rsvp$hash$settled$$default,
'denodeify': $$rsvp$node$$default,
'on': $$rsvp$$on,
'off': $$rsvp$$off,
'map': $$rsvp$map$$default,
'filter': $$rsvp$filter$$default,
'resolve': $$rsvp$resolve$$default,
'reject': $$rsvp$reject$$default,
'all': $$rsvp$all$$default,
'rethrow': $$rsvp$rethrow$$default,
'defer': $$rsvp$defer$$default,
'EventTarget': $$rsvp$events$$default,
'configure': $$rsvp$config$$configure,
'async': $$rsvp$$async
};
/* global define:true module:true window: true */
if (typeof define === 'function' && define.amd) {
define(function() { return rsvp$umd$$RSVP; });
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = rsvp$umd$$RSVP;
} else if (typeof this !== 'undefined') {
this['RSVP'] = rsvp$umd$$RSVP;
}
}).call(this);
}).call(this,require('_process'))
},{"_process":3}],5:[function(require,module,exports){
/*!
* URI.js - Mutating URLs
*
* Version: 1.17.0
*
* Author: Rodney Rehm
* Web: http://medialize.github.io/URI.js/
*
* Licensed under
* MIT License http://www.opensource.org/licenses/mit-license
* GPL v3 http://opensource.org/licenses/GPL-3.0
*
*/
(function (root, factory) {
'use strict';
// https://github.com/umdjs/umd/blob/master/returnExports.js
if (typeof exports === 'object') {
// Node
module.exports = factory(require('./punycode'), require('./IPv6'), require('./SecondLevelDomains'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['./punycode', './IPv6', './SecondLevelDomains'], factory);
} else {
// Browser globals (root is window)
root.URI = factory(root.punycode, root.IPv6, root.SecondLevelDomains, root);
}
}(this, function (punycode, IPv6, SLD, root) {
'use strict';
/*global location, escape, unescape */
// FIXME: v2.0.0 renamce non-camelCase properties to uppercase
/*jshint camelcase: false */
// save current URI variable, if any
var _URI = root && root.URI;
function URI(url, base) {
var _urlSupplied = arguments.length >= 1;
var _baseSupplied = arguments.length >= 2;
// Allow instantiation without the 'new' keyword
if (!(this instanceof URI)) {
if (_urlSupplied) {
if (_baseSupplied) {
return new URI(url, base);
}
return new URI(url);
}
return new URI();
}
if (url === undefined) {
if (_urlSupplied) {
throw new TypeError('undefined is not a valid argument for URI');
}
if (typeof location !== 'undefined') {
url = location.href + '';
} else {
url = '';
}
}
this.href(url);
// resolve to base according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#constructor
if (base !== undefined) {
return this.absoluteTo(base);
}
return this;
}
URI.version = '1.17.0';
var p = URI.prototype;
var hasOwn = Object.prototype.hasOwnProperty;
function escapeRegEx(string) {
// https://github.com/medialize/URI.js/commit/85ac21783c11f8ccab06106dba9735a31a86924d#commitcomment-821963
return string.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1');
}
function getType(value) {
// IE8 doesn't return [Object Undefined] but [Object Object] for undefined value
if (value === undefined) {
return 'Undefined';
}
return String(Object.prototype.toString.call(value)).slice(8, -1);
}
function isArray(obj) {
return getType(obj) === 'Array';
}
function filterArrayValues(data, value) {
var lookup = {};
var i, length;
if (getType(value) === 'RegExp') {
lookup = null;
} else if (isArray(value)) {
for (i = 0, length = value.length; i < length; i++) {
lookup[value[i]] = true;
}
} else {
lookup[value] = true;
}
for (i = 0, length = data.length; i < length; i++) {
/*jshint laxbreak: true */
var _match = lookup && lookup[data[i]] !== undefined
|| !lookup && value.test(data[i]);
/*jshint laxbreak: false */
if (_match) {
data.splice(i, 1);
length--;
i--;
}
}
return data;
}
function arrayContains(list, value) {
var i, length;
// value may be string, number, array, regexp
if (isArray(value)) {
// Note: this can be optimized to O(n) (instead of current O(m * n))
for (i = 0, length = value.length; i < length; i++) {
if (!arrayContains(list, value[i])) {
return false;
}
}
return true;
}
var _type = getType(value);
for (i = 0, length = list.length; i < length; i++) {
if (_type === 'RegExp') {
if (typeof list[i] === 'string' && list[i].match(value)) {
return true;
}
} else if (list[i] === value) {
return true;
}
}
return false;
}
function arraysEqual(one, two) {
if (!isArray(one) || !isArray(two)) {
return false;
}
// arrays can't be equal if they have different amount of content
if (one.length !== two.length) {
return false;
}
one.sort();
two.sort();
for (var i = 0, l = one.length; i < l; i++) {
if (one[i] !== two[i]) {
return false;
}
}
return true;
}
function trimSlashes(text) {
var trim_expression = /^\/+|\/+$/g;
return text.replace(trim_expression, '');
}
URI._parts = function() {
return {
protocol: null,
username: null,
password: null,
hostname: null,
urn: null,
port: null,
path: null,
query: null,
fragment: null,
// state
duplicateQueryParameters: URI.duplicateQueryParameters,
escapeQuerySpace: URI.escapeQuerySpace
};
};
// state: allow duplicate query parameters (a=1&a=1)
URI.duplicateQueryParameters = false;
// state: replaces + with %20 (space in query strings)
URI.escapeQuerySpace = true;
// static properties
URI.protocol_expression = /^[a-z][a-z0-9.+-]*$/i;
URI.idn_expression = /[^a-z0-9\.-]/i;
URI.punycode_expression = /(xn--)/i;
// well, 333.444.555.666 matches, but it sure ain't no IPv4 - do we care?
URI.ip4_expression = /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
// credits to Rich Brown
// source: http://forums.intermapper.com/viewtopic.php?p=1096#1096
// specification: http://www.ietf.org/rfc/rfc4291.txt
URI.ip6_expression = /^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/;
// expression used is "gruber revised" (@gruber v2) determined to be the
// best solution in a regex-golf we did a couple of ages ago at
// * http://mathiasbynens.be/demo/url-regex
// * http://rodneyrehm.de/t/url-regex.html
URI.find_uri_expression = /\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))/ig;
URI.findUri = {
// valid "scheme://" or "www."
start: /\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,
// everything up to the next whitespace
end: /[\s\r\n]|$/,
// trim trailing punctuation captured by end RegExp
trim: /[`!()\[\]{};:'".,<>?«»“”„‘’]+$/
};
// http://www.iana.org/assignments/uri-schemes.html
// http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers#Well-known_ports
URI.defaultPorts = {
http: '80',
https: '443',
ftp: '21',
gopher: '70',
ws: '80',
wss: '443'
};
// allowed hostname characters according to RFC 3986
// ALPHA DIGIT "-" "." "_" "~" "!" "$" "&" "'" "(" ")" "*" "+" "," ";" "=" %encoded
// I've never seen a (non-IDN) hostname other than: ALPHA DIGIT . -
URI.invalid_hostname_characters = /[^a-zA-Z0-9\.-]/;
// map DOM Elements to their URI attribute
URI.domAttributes = {
'a': 'href',
'blockquote': 'cite',
'link': 'href',
'base': 'href',
'script': 'src',
'form': 'action',
'img': 'src',
'area': 'href',
'iframe': 'src',
'embed': 'src',
'source': 'src',
'track': 'src',
'input': 'src', // but only if type="image"
'audio': 'src',
'video': 'src'
};
URI.getDomAttribute = function(node) {
if (!node || !node.nodeName) {
return undefined;
}
var nodeName = node.nodeName.toLowerCase();
// <input> should only expose src for type="image"
if (nodeName === 'input' && node.type !== 'image') {
return undefined;
}
return URI.domAttributes[nodeName];
};
function escapeForDumbFirefox36(value) {
// https://github.com/medialize/URI.js/issues/91
return escape(value);
}
// encoding / decoding according to RFC3986
function strictEncodeURIComponent(string) {
// see https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/encodeURIComponent
return encodeURIComponent(string)
.replace(/[!'()*]/g, escapeForDumbFirefox36)
.replace(/\*/g, '%2A');
}
URI.encode = strictEncodeURIComponent;
URI.decode = decodeURIComponent;
URI.iso8859 = function() {
URI.encode = escape;
URI.decode = unescape;
};
URI.unicode = function() {
URI.encode = strictEncodeURIComponent;
URI.decode = decodeURIComponent;
};
URI.characters = {
pathname: {
encode: {
// RFC3986 2.1: For consistency, URI producers and normalizers should
// use uppercase hexadecimal digits for all percent-encodings.
expression: /%(24|26|2B|2C|3B|3D|3A|40)/ig,
map: {
// -._~!'()*
'%24': '$',
'%26': '&',
'%2B': '+',
'%2C': ',',
'%3B': ';',
'%3D': '=',
'%3A': ':',
'%40': '@'
}
},
decode: {
expression: /[\/\?#]/g,
map: {
'/': '%2F',
'?': '%3F',
'#': '%23'
}
}
},
reserved: {
encode: {
// RFC3986 2.1: For consistency, URI producers and normalizers should
// use uppercase hexadecimal digits for all percent-encodings.
expression: /%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,
map: {
// gen-delims
'%3A': ':',
'%2F': '/',
'%3F': '?',
'%23': '#',
'%5B': '[',
'%5D': ']',
'%40': '@',
// sub-delims
'%21': '!',
'%24': '$',
'%26': '&',
'%27': '\'',
'%28': '(',
'%29': ')',
'%2A': '*',
'%2B': '+',
'%2C': ',',
'%3B': ';',
'%3D': '='
}
}
},
urnpath: {
// The characters under `encode` are the characters called out by RFC 2141 as being acceptable
// for usage in a URN. RFC2141 also calls out "-", ".", and "_" as acceptable characters, but
// these aren't encoded by encodeURIComponent, so we don't have to call them out here. Also
// note that the colon character is not featured in the encoding map; this is because URI.js
// gives the colons in URNs semantic meaning as the delimiters of path segements, and so it
// should not appear unencoded in a segment itself.
// See also the note above about RFC3986 and capitalalized hex digits.
encode: {
expression: /%(21|24|27|28|29|2A|2B|2C|3B|3D|40)/ig,
map: {
'%21': '!',
'%24': '$',
'%27': '\'',
'%28': '(',
'%29': ')',
'%2A': '*',
'%2B': '+',
'%2C': ',',
'%3B': ';',
'%3D': '=',
'%40': '@'
}
},
// These characters are the characters called out by RFC2141 as "reserved" characters that
// should never appear in a URN, plus the colon character (see note above).
decode: {
expression: /[\/\?#:]/g,
map: {
'/': '%2F',
'?': '%3F',
'#': '%23',
':': '%3A'
}
}
}
};
URI.encodeQuery = function(string, escapeQuerySpace) {
var escaped = URI.encode(string + '');
if (escapeQuerySpace === undefined) {
escapeQuerySpace = URI.escapeQuerySpace;
}
return escapeQuerySpace ? escaped.replace(/%20/g, '+') : escaped;
};
URI.decodeQuery = function(string, escapeQuerySpace) {
string += '';
if (escapeQuerySpace === undefined) {
escapeQuerySpace = URI.escapeQuerySpace;
}
try {
return URI.decode(escapeQuerySpace ? string.replace(/\+/g, '%20') : string);
} catch(e) {
// we're not going to mess with weird encodings,
// give up and return the undecoded original string
// see https://github.com/medialize/URI.js/issues/87
// see https://github.com/medialize/URI.js/issues/92
return string;
}
};
// generate encode/decode path functions
var _parts = {'encode':'encode', 'decode':'decode'};
var _part;
var generateAccessor = function(_group, _part) {
return function(string) {
try {
return URI[_part](string + '').replace(URI.characters[_group][_part].expression, function(c) {
return URI.characters[_group][_part].map[c];
});
} catch (e) {
// we're not going to mess with weird encodings,
// give up and return the undecoded original string
// see https://github.com/medialize/URI.js/issues/87
// see https://github.com/medialize/URI.js/issues/92
return string;
}
};
};
for (_part in _parts) {
URI[_part + 'PathSegment'] = generateAccessor('pathname', _parts[_part]);
URI[_part + 'UrnPathSegment'] = generateAccessor('urnpath', _parts[_part]);
}
var generateSegmentedPathFunction = function(_sep, _codingFuncName, _innerCodingFuncName) {
return function(string) {
// Why pass in names of functions, rather than the function objects themselves? The
// definitions of some functions (but in particular, URI.decode) will occasionally change due
// to URI.js having ISO8859 and Unicode modes. Passing in the name and getting it will ensure
// that the functions we use here are "fresh".
var actualCodingFunc;
if (!_innerCodingFuncName) {
actualCodingFunc = URI[_codingFuncName];
} else {
actualCodingFunc = function(string) {
return URI[_codingFuncName](URI[_innerCodingFuncName](string));
};
}
var segments = (string + '').split(_sep);
for (var i = 0, length = segments.length; i < length; i++) {
segments[i] = actualCodingFunc(segments[i]);
}
return segments.join(_sep);
};
};
// This takes place outside the above loop because we don't want, e.g., encodeUrnPath functions.
URI.decodePath = generateSegmentedPathFunction('/', 'decodePathSegment');
URI.decodeUrnPath = generateSegmentedPathFunction(':', 'decodeUrnPathSegment');
URI.recodePath = generateSegmentedPathFunction('/', 'encodePathSegment', 'decode');
URI.recodeUrnPath = generateSegmentedPathFunction(':', 'encodeUrnPathSegment', 'decode');
URI.encodeReserved = generateAccessor('reserved', 'encode');
URI.parse = function(string, parts) {
var pos;
if (!parts) {
parts = {};
}
// [protocol"://"[username[":"password]"@"]hostname[":"port]"/"?][path]["?"querystring]["#"fragment]
// extract fragment
pos = string.indexOf('#');
if (pos > -1) {
// escaping?
parts.fragment = string.substring(pos + 1) || null;
string = string.substring(0, pos);
}
// extract query
pos = string.indexOf('?');
if (pos > -1) {
// escaping?
parts.query = string.substring(pos + 1) || null;
string = string.substring(0, pos);
}
// extract protocol
if (string.substring(0, 2) === '//') {
// relative-scheme
parts.protocol = null;
string = string.substring(2);
// extract "user:pass@host:port"
string = URI.parseAuthority(string, parts);
} else {
pos = string.indexOf(':');
if (pos > -1) {
parts.protocol = string.substring(0, pos) || null;
if (parts.protocol && !parts.protocol.match(URI.protocol_expression)) {
// : may be within the path
parts.protocol = undefined;
} else if (string.substring(pos + 1, pos + 3) === '//') {
string = string.substring(pos + 3);
// extract "user:pass@host:port"
string = URI.parseAuthority(string, parts);
} else {
string = string.substring(pos + 1);
parts.urn = true;
}
}
}
// what's left must be the path
parts.path = string;
// and we're done
return parts;
};
URI.parseHost = function(string, parts) {
// Copy chrome, IE, opera backslash-handling behavior.
// Back slashes before the query string get converted to forward slashes
// See: https://github.com/joyent/node/blob/386fd24f49b0e9d1a8a076592a404168faeecc34/lib/url.js#L115-L124
// See: https://code.google.com/p/chromium/issues/detail?id=25916
// https://github.com/medialize/URI.js/pull/233
string = string.replace(/\\/g, '/');
// extract host:port
var pos = string.indexOf('/');
var bracketPos;
var t;
if (pos === -1) {
pos = string.length;
}
if (string.charAt(0) === '[') {
// IPv6 host - http://tools.ietf.org/html/draft-ietf-6man-text-addr-representation-04#section-6
// I claim most client software breaks on IPv6 anyways. To simplify things, URI only accepts
// IPv6+port in the format [2001:db8::1]:80 (for the time being)
bracketPos = string.indexOf(']');
parts.hostname = string.substring(1, bracketPos) || null;
parts.port = string.substring(bracketPos + 2, pos) || null;
if (parts.port === '/') {
parts.port = null;
}
} else {
var firstColon = string.indexOf(':');
var firstSlash = string.indexOf('/');
var nextColon = string.indexOf(':', firstColon + 1);
if (nextColon !== -1 && (firstSlash === -1 || nextColon < firstSlash)) {
// IPv6 host contains multiple colons - but no port
// this notation is actually not allowed by RFC 3986, but we're a liberal parser
parts.hostname = string.substring(0, pos) || null;
parts.port = null;
} else {
t = string.substring(0, pos).split(':');
parts.hostname = t[0] || null;
parts.port = t[1] || null;
}
}
if (parts.hostname && string.substring(pos).charAt(0) !== '/') {
pos++;
string = '/' + string;
}
return string.substring(pos) || '/';
};
URI.parseAuthority = function(string, parts) {
string = URI.parseUserinfo(string, parts);
return URI.parseHost(string, parts);
};
URI.parseUserinfo = function(string, parts) {
// extract username:password
var firstSlash = string.indexOf('/');
var pos = string.lastIndexOf('@', firstSlash > -1 ? firstSlash : string.length - 1);
var t;
// authority@ must come before /path
if (pos > -1 && (firstSlash === -1 || pos < firstSlash)) {
t = string.substring(0, pos).split(':');
parts.username = t[0] ? URI.decode(t[0]) : null;
t.shift();
parts.password = t[0] ? URI.decode(t.join(':')) : null;
string = string.substring(pos + 1);
} else {
parts.username = null;
parts.password = null;
}
return string;
};
URI.parseQuery = function(string, escapeQuerySpace) {
if (!string) {
return {};
}
// throw out the funky business - "?"[name"="value"&"]+
string = string.replace(/&+/g, '&').replace(/^\?*&*|&+$/g, '');
if (!string) {
return {};
}
var items = {};
var splits = string.split('&');
var length = splits.length;
var v, name, value;
for (var i = 0; i < length; i++) {
v = splits[i].split('=');
name = URI.decodeQuery(v.shift(), escapeQuerySpace);
// no "=" is null according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#collect-url-parameters
value = v.length ? URI.decodeQuery(v.join('='), escapeQuerySpace) : null;
if (hasOwn.call(items, name)) {
if (typeof items[name] === 'string' || items[name] === null) {
items[name] = [items[name]];
}
items[name].push(value);
} else {
items[name] = value;
}
}
return items;
};
URI.build = function(parts) {
var t = '';
if (parts.protocol) {
t += parts.protocol + ':';
}
if (!parts.urn && (t || parts.hostname)) {
t += '//';
}
t += (URI.buildAuthority(parts) || '');
if (typeof parts.path === 'string') {
if (parts.path.charAt(0) !== '/' && typeof parts.hostname === 'string') {
t += '/';
}
t += parts.path;
}
if (typeof parts.query === 'string' && parts.query) {
t += '?' + parts.query;
}
if (typeof parts.fragment === 'string' && parts.fragment) {
t += '#' + parts.fragment;
}
return t;
};
URI.buildHost = function(parts) {
var t = '';
if (!parts.hostname) {
return '';
} else if (URI.ip6_expression.test(parts.hostname)) {
t += '[' + parts.hostname + ']';
} else {
t += parts.hostname;
}
if (parts.port) {
t += ':' + parts.port;
}
return t;
};
URI.buildAuthority = function(parts) {
return URI.buildUserinfo(parts) + URI.buildHost(parts);
};
URI.buildUserinfo = function(parts) {
var t = '';
if (parts.username) {
t += URI.encode(parts.username);
if (parts.password) {
t += ':' + URI.encode(parts.password);
}
t += '@';
}
return t;
};
URI.buildQuery = function(data, duplicateQueryParameters, escapeQuerySpace) {
// according to http://tools.ietf.org/html/rfc3986 or http://labs.apache.org/webarch/uri/rfc/rfc3986.html
// being »-._~!$&'()*+,;=:@/?« %HEX and alnum are allowed
// the RFC explicitly states ?/foo being a valid use case, no mention of parameter syntax!
// URI.js treats the query string as being application/x-www-form-urlencoded
// see http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type
var t = '';
var unique, key, i, length;
for (key in data) {
if (hasOwn.call(data, key) && key) {
if (isArray(data[key])) {
unique = {};
for (i = 0, length = data[key].length; i < length; i++) {
if (data[key][i] !== undefined && unique[data[key][i] + ''] === undefined) {
t += '&' + URI.buildQueryParameter(key, data[key][i], escapeQuerySpace);
if (duplicateQueryParameters !== true) {
unique[data[key][i] + ''] = true;
}
}
}
} else if (data[key] !== undefined) {
t += '&' + URI.buildQueryParameter(key, data[key], escapeQuerySpace);
}
}
}
return t.substring(1);
};
URI.buildQueryParameter = function(name, value, escapeQuerySpace) {
// http://www.w3.org/TR/REC-html40/interact/forms.html#form-content-type -- application/x-www-form-urlencoded
// don't append "=" for null values, according to http://dvcs.w3.org/hg/url/raw-file/tip/Overview.html#url-parameter-serialization
return URI.encodeQuery(name, escapeQuerySpace) + (value !== null ? '=' + URI.encodeQuery(value, escapeQuerySpace) : '');
};
URI.addQuery = function(data, name, value) {
if (typeof name === 'object') {
for (var key in name) {
if (hasOwn.call(name, key)) {
URI.addQuery(data, key, name[key]);
}
}
} else if (typeof name === 'string') {
if (data[name] === undefined) {
data[name] = value;
return;
} else if (typeof data[name] === 'string') {
data[name] = [data[name]];
}
if (!isArray(value)) {
value = [value];
}
data[name] = (data[name] || []).concat(value);
} else {
throw new TypeError('URI.addQuery() accepts an object, string as the name parameter');
}
};
URI.removeQuery = function(data, name, value) {
var i, length, key;
if (isArray(name)) {
for (i = 0, length = name.length; i < length; i++) {
data[name[i]] = undefined;
}
} else if (getType(name) === 'RegExp') {
for (key in data) {
if (name.test(key)) {
data[key] = undefined;
}
}
} else if (typeof name === 'object') {
for (key in name) {
if (hasOwn.call(name, key)) {
URI.removeQuery(data, key, name[key]);
}
}
} else if (typeof name === 'string') {
if (value !== undefined) {
if (getType(value) === 'RegExp') {
if (!isArray(data[name]) && value.test(data[name])) {
data[name] = undefined;
} else {
data[name] = filterArrayValues(data[name], value);
}
} else if (data[name] === String(value) && (!isArray(value) || value.length === 1)) {
data[name] = undefined;
} else if (isArray(data[name])) {
data[name] = filterArrayValues(data[name], value);
}
} else {
data[name] = undefined;
}
} else {
throw new TypeError('URI.removeQuery() accepts an object, string, RegExp as the first parameter');
}
};
URI.hasQuery = function(data, name, value, withinArray) {
if (typeof name === 'object') {
for (var key in name) {
if (hasOwn.call(name, key)) {
if (!URI.hasQuery(data, key, name[key])) {
return false;
}
}
}
return true;
} else if (typeof name !== 'string') {
throw new TypeError('URI.hasQuery() accepts an object, string as the name parameter');
}
switch (getType(value)) {
case 'Undefined':
// true if exists (but may be empty)
return name in data; // data[name] !== undefined;
case 'Boolean':
// true if exists and non-empty
var _booly = Boolean(isArray(data[name]) ? data[name].length : data[name]);
return value === _booly;
case 'Function':
// allow complex comparison
return !!value(data[name], name, data);
case 'Array':
if (!isArray(data[name])) {
return false;
}
var op = withinArray ? arrayContains : arraysEqual;
return op(data[name], value);
case 'RegExp':
if (!isArray(data[name])) {
return Boolean(data[name] && data[name].match(value));
}
if (!withinArray) {
return false;
}
return arrayContains(data[name], value);
case 'Number':
value = String(value);
/* falls through */
case 'String':
if (!isArray(data[name])) {
return data[name] === value;
}
if (!withinArray) {
return false;
}
return arrayContains(data[name], value);
default:
throw new TypeError('URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter');
}
};
URI.commonPath = function(one, two) {
var length = Math.min(one.length, two.length);
var pos;
// find first non-matching character
for (pos = 0; pos < length; pos++) {
if (one.charAt(pos) !== two.charAt(pos)) {
pos--;
break;
}
}
if (pos < 1) {
return one.charAt(0) === two.charAt(0) && one.charAt(0) === '/' ? '/' : '';
}
// revert to last /
if (one.charAt(pos) !== '/' || two.charAt(pos) !== '/') {
pos = one.substring(0, pos).lastIndexOf('/');
}
return one.substring(0, pos + 1);
};
URI.withinString = function(string, callback, options) {
options || (options = {});
var _start = options.start || URI.findUri.start;
var _end = options.end || URI.findUri.end;
var _trim = options.trim || URI.findUri.trim;
var _attributeOpen = /[a-z0-9-]=["']?$/i;
_start.lastIndex = 0;
while (true) {
var match = _start.exec(string);
if (!match) {
break;
}
var start = match.index;
if (options.ignoreHtml) {
// attribut(e=["']?$)
var attributeOpen = string.slice(Math.max(start - 3, 0), start);
if (attributeOpen && _attributeOpen.test(attributeOpen)) {
continue;
}
}
var end = start + string.slice(start).search(_end);
var slice = string.slice(start, end).replace(_trim, '');
if (options.ignore && options.ignore.test(slice)) {
continue;
}
end = start + slice.length;
var result = callback(slice, start, end, string);
string = string.slice(0, start) + result + string.slice(end);
_start.lastIndex = start + result.length;
}
_start.lastIndex = 0;
return string;
};
URI.ensureValidHostname = function(v) {
// Theoretically URIs allow percent-encoding in Hostnames (according to RFC 3986)
// they are not part of DNS and therefore ignored by URI.js
if (v.match(URI.invalid_hostname_characters)) {
// test punycode
if (!punycode) {
throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-] and Punycode.js is not available');
}
if (punycode.toASCII(v).match(URI.invalid_hostname_characters)) {
throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
}
}
};
// noConflict
URI.noConflict = function(removeAll) {
if (removeAll) {
var unconflicted = {
URI: this.noConflict()
};
if (root.URITemplate && typeof root.URITemplate.noConflict === 'function') {
unconflicted.URITemplate = root.URITemplate.noConflict();
}
if (root.IPv6 && typeof root.IPv6.noConflict === 'function') {
unconflicted.IPv6 = root.IPv6.noConflict();
}
if (root.SecondLevelDomains && typeof root.SecondLevelDomains.noConflict === 'function') {
unconflicted.SecondLevelDomains = root.SecondLevelDomains.noConflict();
}
return unconflicted;
} else if (root.URI === this) {
root.URI = _URI;
}
return this;
};
p.build = function(deferBuild) {
if (deferBuild === true) {
this._deferred_build = true;
} else if (deferBuild === undefined || this._deferred_build) {
this._string = URI.build(this._parts);
this._deferred_build = false;
}
return this;
};
p.clone = function() {
return new URI(this);
};
p.valueOf = p.toString = function() {
return this.build(false)._string;
};
function generateSimpleAccessor(_part){
return function(v, build) {
if (v === undefined) {
return this._parts[_part] || '';
} else {
this._parts[_part] = v || null;
this.build(!build);
return this;
}
};
}
function generatePrefixAccessor(_part, _key){
return function(v, build) {
if (v === undefined) {
return this._parts[_part] || '';
} else {
if (v !== null) {
v = v + '';
if (v.charAt(0) === _key) {
v = v.substring(1);
}
}
this._parts[_part] = v;
this.build(!build);
return this;
}
};
}
p.protocol = generateSimpleAccessor('protocol');
p.username = generateSimpleAccessor('username');
p.password = generateSimpleAccessor('password');
p.hostname = generateSimpleAccessor('hostname');
p.port = generateSimpleAccessor('port');
p.query = generatePrefixAccessor('query', '?');
p.fragment = generatePrefixAccessor('fragment', '#');
p.search = function(v, build) {
var t = this.query(v, build);
return typeof t === 'string' && t.length ? ('?' + t) : t;
};
p.hash = function(v, build) {
var t = this.fragment(v, build);
return typeof t === 'string' && t.length ? ('#' + t) : t;
};
p.pathname = function(v, build) {
if (v === undefined || v === true) {
var res = this._parts.path || (this._parts.hostname ? '/' : '');
return v ? (this._parts.urn ? URI.decodeUrnPath : URI.decodePath)(res) : res;
} else {
if (this._parts.urn) {
this._parts.path = v ? URI.recodeUrnPath(v) : '';
} else {
this._parts.path = v ? URI.recodePath(v) : '/';
}
this.build(!build);
return this;
}
};
p.path = p.pathname;
p.href = function(href, build) {
var key;
if (href === undefined) {
return this.toString();
}
this._string = '';
this._parts = URI._parts();
var _URI = href instanceof URI;
var _object = typeof href === 'object' && (href.hostname || href.path || href.pathname);
if (href.nodeName) {
var attribute = URI.getDomAttribute(href);
href = href[attribute] || '';
_object = false;
}
// window.location is reported to be an object, but it's not the sort
// of object we're looking for:
// * location.protocol ends with a colon
// * location.query != object.search
// * location.hash != object.fragment
// simply serializing the unknown object should do the trick
// (for location, not for everything...)
if (!_URI && _object && href.pathname !== undefined) {
href = href.toString();
}
if (typeof href === 'string' || href instanceof String) {
this._parts = URI.parse(String(href), this._parts);
} else if (_URI || _object) {
var src = _URI ? href._parts : href;
for (key in src) {
if (hasOwn.call(this._parts, key)) {
this._parts[key] = src[key];
}
}
} else {
throw new TypeError('invalid input');
}
this.build(!build);
return this;
};
// identification accessors
p.is = function(what) {
var ip = false;
var ip4 = false;
var ip6 = false;
var name = false;
var sld = false;
var idn = false;
var punycode = false;
var relative = !this._parts.urn;
if (this._parts.hostname) {
relative = false;
ip4 = URI.ip4_expression.test(this._parts.hostname);
ip6 = URI.ip6_expression.test(this._parts.hostname);
ip = ip4 || ip6;
name = !ip;
sld = name && SLD && SLD.has(this._parts.hostname);
idn = name && URI.idn_expression.test(this._parts.hostname);
punycode = name && URI.punycode_expression.test(this._parts.hostname);
}
switch (what.toLowerCase()) {
case 'relative':
return relative;
case 'absolute':
return !relative;
// hostname identification
case 'domain':
case 'name':
return name;
case 'sld':
return sld;
case 'ip':
return ip;
case 'ip4':
case 'ipv4':
case 'inet4':
return ip4;
case 'ip6':
case 'ipv6':
case 'inet6':
return ip6;
case 'idn':
return idn;
case 'url':
return !this._parts.urn;
case 'urn':
return !!this._parts.urn;
case 'punycode':
return punycode;
}
return null;
};
// component specific input validation
var _protocol = p.protocol;
var _port = p.port;
var _hostname = p.hostname;
p.protocol = function(v, build) {
if (v !== undefined) {
if (v) {
// accept trailing ://
v = v.replace(/:(\/\/)?$/, '');
if (!v.match(URI.protocol_expression)) {
throw new TypeError('Protocol "' + v + '" contains characters other than [A-Z0-9.+-] or doesn\'t start with [A-Z]');
}
}
}
return _protocol.call(this, v, build);
};
p.scheme = p.protocol;
p.port = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v !== undefined) {
if (v === 0) {
v = null;
}
if (v) {
v += '';
if (v.charAt(0) === ':') {
v = v.substring(1);
}
if (v.match(/[^0-9]/)) {
throw new TypeError('Port "' + v + '" contains characters other than [0-9]');
}
}
}
return _port.call(this, v, build);
};
p.hostname = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v !== undefined) {
var x = {};
var res = URI.parseHost(v, x);
if (res !== '/') {
throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
}
v = x.hostname;
}
return _hostname.call(this, v, build);
};
// compound accessors
p.origin = function(v, build) {
var parts;
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
var protocol = this.protocol();
var authority = this.authority();
if (!authority) return '';
return (protocol ? protocol + '://' : '') + this.authority();
} else {
var origin = URI(v);
this
.protocol(origin.protocol())
.authority(origin.authority())
.build(!build);
return this;
}
};
p.host = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
return this._parts.hostname ? URI.buildHost(this._parts) : '';
} else {
var res = URI.parseHost(v, this._parts);
if (res !== '/') {
throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
}
this.build(!build);
return this;
}
};
p.authority = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
return this._parts.hostname ? URI.buildAuthority(this._parts) : '';
} else {
var res = URI.parseAuthority(v, this._parts);
if (res !== '/') {
throw new TypeError('Hostname "' + v + '" contains characters other than [A-Z0-9.-]');
}
this.build(!build);
return this;
}
};
p.userinfo = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined) {
if (!this._parts.username) {
return '';
}
var t = URI.buildUserinfo(this._parts);
return t.substring(0, t.length -1);
} else {
if (v[v.length-1] !== '@') {
v += '@';
}
URI.parseUserinfo(v, this._parts);
this.build(!build);
return this;
}
};
p.resource = function(v, build) {
var parts;
if (v === undefined) {
return this.path() + this.search() + this.hash();
}
parts = URI.parse(v);
this._parts.path = parts.path;
this._parts.query = parts.query;
this._parts.fragment = parts.fragment;
this.build(!build);
return this;
};
// fraction accessors
p.subdomain = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
// convenience, return "www" from "www.example.org"
if (v === undefined) {
if (!this._parts.hostname || this.is('IP')) {
return '';
}
// grab domain and add another segment
var end = this._parts.hostname.length - this.domain().length - 1;
return this._parts.hostname.substring(0, end) || '';
} else {
var e = this._parts.hostname.length - this.domain().length;
var sub = this._parts.hostname.substring(0, e);
var replace = new RegExp('^' + escapeRegEx(sub));
if (v && v.charAt(v.length - 1) !== '.') {
v += '.';
}
if (v) {
URI.ensureValidHostname(v);
}
this._parts.hostname = this._parts.hostname.replace(replace, v);
this.build(!build);
return this;
}
};
p.domain = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (typeof v === 'boolean') {
build = v;
v = undefined;
}
// convenience, return "example.org" from "www.example.org"
if (v === undefined) {
if (!this._parts.hostname || this.is('IP')) {
return '';
}
// if hostname consists of 1 or 2 segments, it must be the domain
var t = this._parts.hostname.match(/\./g);
if (t && t.length < 2) {
return this._parts.hostname;
}
// grab tld and add another segment
var end = this._parts.hostname.length - this.tld(build).length - 1;
end = this._parts.hostname.lastIndexOf('.', end -1) + 1;
return this._parts.hostname.substring(end) || '';
} else {
if (!v) {
throw new TypeError('cannot set domain empty');
}
URI.ensureValidHostname(v);
if (!this._parts.hostname || this.is('IP')) {
this._parts.hostname = v;
} else {
var replace = new RegExp(escapeRegEx(this.domain()) + '$');
this._parts.hostname = this._parts.hostname.replace(replace, v);
}
this.build(!build);
return this;
}
};
p.tld = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (typeof v === 'boolean') {
build = v;
v = undefined;
}
// return "org" from "www.example.org"
if (v === undefined) {
if (!this._parts.hostname || this.is('IP')) {
return '';
}
var pos = this._parts.hostname.lastIndexOf('.');
var tld = this._parts.hostname.substring(pos + 1);
if (build !== true && SLD && SLD.list[tld.toLowerCase()]) {
return SLD.get(this._parts.hostname) || tld;
}
return tld;
} else {
var replace;
if (!v) {
throw new TypeError('cannot set TLD empty');
} else if (v.match(/[^a-zA-Z0-9-]/)) {
if (SLD && SLD.is(v)) {
replace = new RegExp(escapeRegEx(this.tld()) + '$');
this._parts.hostname = this._parts.hostname.replace(replace, v);
} else {
throw new TypeError('TLD "' + v + '" contains characters other than [A-Z0-9]');
}
} else if (!this._parts.hostname || this.is('IP')) {
throw new ReferenceError('cannot set TLD on non-domain host');
} else {
replace = new RegExp(escapeRegEx(this.tld()) + '$');
this._parts.hostname = this._parts.hostname.replace(replace, v);
}
this.build(!build);
return this;
}
};
p.directory = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined || v === true) {
if (!this._parts.path && !this._parts.hostname) {
return '';
}
if (this._parts.path === '/') {
return '/';
}
var end = this._parts.path.length - this.filename().length - 1;
var res = this._parts.path.substring(0, end) || (this._parts.hostname ? '/' : '');
return v ? URI.decodePath(res) : res;
} else {
var e = this._parts.path.length - this.filename().length;
var directory = this._parts.path.substring(0, e);
var replace = new RegExp('^' + escapeRegEx(directory));
// fully qualifier directories begin with a slash
if (!this.is('relative')) {
if (!v) {
v = '/';
}
if (v.charAt(0) !== '/') {
v = '/' + v;
}
}
// directories always end with a slash
if (v && v.charAt(v.length - 1) !== '/') {
v += '/';
}
v = URI.recodePath(v);
this._parts.path = this._parts.path.replace(replace, v);
this.build(!build);
return this;
}
};
p.filename = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined || v === true) {
if (!this._parts.path || this._parts.path === '/') {
return '';
}
var pos = this._parts.path.lastIndexOf('/');
var res = this._parts.path.substring(pos+1);
return v ? URI.decodePathSegment(res) : res;
} else {
var mutatedDirectory = false;
if (v.charAt(0) === '/') {
v = v.substring(1);
}
if (v.match(/\.?\//)) {
mutatedDirectory = true;
}
var replace = new RegExp(escapeRegEx(this.filename()) + '$');
v = URI.recodePath(v);
this._parts.path = this._parts.path.replace(replace, v);
if (mutatedDirectory) {
this.normalizePath(build);
} else {
this.build(!build);
}
return this;
}
};
p.suffix = function(v, build) {
if (this._parts.urn) {
return v === undefined ? '' : this;
}
if (v === undefined || v === true) {
if (!this._parts.path || this._parts.path === '/') {
return '';
}
var filename = this.filename();
var pos = filename.lastIndexOf('.');
var s, res;
if (pos === -1) {
return '';
}
// suffix may only contain alnum characters (yup, I made this up.)
s = filename.substring(pos+1);
res = (/^[a-z0-9%]+$/i).test(s) ? s : '';
return v ? URI.decodePathSegment(res) : res;
} else {
if (v.charAt(0) === '.') {
v = v.substring(1);
}
var suffix = this.suffix();
var replace;
if (!suffix) {
if (!v) {
return this;
}
this._parts.path += '.' + URI.recodePath(v);
} else if (!v) {
replace = new RegExp(escapeRegEx('.' + suffix) + '$');
} else {
replace = new RegExp(escapeRegEx(suffix) + '$');
}
if (replace) {
v = URI.recodePath(v);
this._parts.path = this._parts.path.replace(replace, v);
}
this.build(!build);
return this;
}
};
p.segment = function(segment, v, build) {
var separator = this._parts.urn ? ':' : '/';
var path = this.path();
var absolute = path.substring(0, 1) === '/';
var segments = path.split(separator);
if (segment !== undefined && typeof segment !== 'number') {
build = v;
v = segment;
segment = undefined;
}
if (segment !== undefined && typeof segment !== 'number') {
throw new Error('Bad segment "' + segment + '", must be 0-based integer');
}
if (absolute) {
segments.shift();
}
if (segment < 0) {
// allow negative indexes to address from the end
segment = Math.max(segments.length + segment, 0);
}
if (v === undefined) {
/*jshint laxbreak: true */
return segment === undefined
? segments
: segments[segment];
/*jshint laxbreak: false */
} else if (segment === null || segments[segment] === undefined) {
if (isArray(v)) {
segments = [];
// collapse empty elements within array
for (var i=0, l=v.length; i < l; i++) {
if (!v[i].length && (!segments.length || !segments[segments.length -1].length)) {
continue;
}
if (segments.length && !segments[segments.length -1].length) {
segments.pop();
}
segments.push(trimSlashes(v[i]));
}
} else if (v || typeof v === 'string') {
v = trimSlashes(v);
if (segments[segments.length -1] === '') {
// empty trailing elements have to be overwritten
// to prevent results such as /foo//bar
segments[segments.length -1] = v;
} else {
segments.push(v);
}
}
} else {
if (v) {
segments[segment] = trimSlashes(v);
} else {
segments.splice(segment, 1);
}
}
if (absolute) {
segments.unshift('');
}
return this.path(segments.join(separator), build);
};
p.segmentCoded = function(segment, v, build) {
var segments, i, l;
if (typeof segment !== 'number') {
build = v;
v = segment;
segment = undefined;
}
if (v === undefined) {
segments = this.segment(segment, v, build);
if (!isArray(segments)) {
segments = segments !== undefined ? URI.decode(segments) : undefined;
} else {
for (i = 0, l = segments.length; i < l; i++) {
segments[i] = URI.decode(segments[i]);
}
}
return segments;
}
if (!isArray(v)) {
v = (typeof v === 'string' || v instanceof String) ? URI.encode(v) : v;
} else {
for (i = 0, l = v.length; i < l; i++) {
v[i] = URI.encode(v[i]);
}
}
return this.segment(segment, v, build);
};
// mutating query string
var q = p.query;
p.query = function(v, build) {
if (v === true) {
return URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
} else if (typeof v === 'function') {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
var result = v.call(this, data);
this._parts.query = URI.buildQuery(result || data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
this.build(!build);
return this;
} else if (v !== undefined && typeof v !== 'string') {
this._parts.query = URI.buildQuery(v, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
this.build(!build);
return this;
} else {
return q.call(this, v, build);
}
};
p.setQuery = function(name, value, build) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
if (typeof name === 'string' || name instanceof String) {
data[name] = value !== undefined ? value : null;
} else if (typeof name === 'object') {
for (var key in name) {
if (hasOwn.call(name, key)) {
data[key] = name[key];
}
}
} else {
throw new TypeError('URI.addQuery() accepts an object, string as the name parameter');
}
this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
if (typeof name !== 'string') {
build = value;
}
this.build(!build);
return this;
};
p.addQuery = function(name, value, build) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
URI.addQuery(data, name, value === undefined ? null : value);
this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
if (typeof name !== 'string') {
build = value;
}
this.build(!build);
return this;
};
p.removeQuery = function(name, value, build) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
URI.removeQuery(data, name, value);
this._parts.query = URI.buildQuery(data, this._parts.duplicateQueryParameters, this._parts.escapeQuerySpace);
if (typeof name !== 'string') {
build = value;
}
this.build(!build);
return this;
};
p.hasQuery = function(name, value, withinArray) {
var data = URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace);
return URI.hasQuery(data, name, value, withinArray);
};
p.setSearch = p.setQuery;
p.addSearch = p.addQuery;
p.removeSearch = p.removeQuery;
p.hasSearch = p.hasQuery;
// sanitizing URLs
p.normalize = function() {
if (this._parts.urn) {
return this
.normalizeProtocol(false)
.normalizePath(false)
.normalizeQuery(false)
.normalizeFragment(false)
.build();
}
return this
.normalizeProtocol(false)
.normalizeHostname(false)
.normalizePort(false)
.normalizePath(false)
.normalizeQuery(false)
.normalizeFragment(false)
.build();
};
p.normalizeProtocol = function(build) {
if (typeof this._parts.protocol === 'string') {
this._parts.protocol = this._parts.protocol.toLowerCase();
this.build(!build);
}
return this;
};
p.normalizeHostname = function(build) {
if (this._parts.hostname) {
if (this.is('IDN') && punycode) {
this._parts.hostname = punycode.toASCII(this._parts.hostname);
} else if (this.is('IPv6') && IPv6) {
this._parts.hostname = IPv6.best(this._parts.hostname);
}
this._parts.hostname = this._parts.hostname.toLowerCase();
this.build(!build);
}
return this;
};
p.normalizePort = function(build) {
// remove port of it's the protocol's default
if (typeof this._parts.protocol === 'string' && this._parts.port === URI.defaultPorts[this._parts.protocol]) {
this._parts.port = null;
this.build(!build);
}
return this;
};
p.normalizePath = function(build) {
var _path = this._parts.path;
if (!_path) {
return this;
}
if (this._parts.urn) {
this._parts.path = URI.recodeUrnPath(this._parts.path);
this.build(!build);
return this;
}
if (this._parts.path === '/') {
return this;
}
var _was_relative;
var _leadingParents = '';
var _parent, _pos;
// handle relative paths
if (_path.charAt(0) !== '/') {
_was_relative = true;
_path = '/' + _path;
}
// handle relative files (as opposed to directories)
if (_path.slice(-3) === '/..' || _path.slice(-2) === '/.') {
_path += '/';
}
// resolve simples
_path = _path
.replace(/(\/(\.\/)+)|(\/\.$)/g, '/')
.replace(/\/{2,}/g, '/');
// remember leading parents
if (_was_relative) {
_leadingParents = _path.substring(1).match(/^(\.\.\/)+/) || '';
if (_leadingParents) {
_leadingParents = _leadingParents[0];
}
}
// resolve parents
while (true) {
_parent = _path.indexOf('/..');
if (_parent === -1) {
// no more ../ to resolve
break;
} else if (_parent === 0) {
// top level cannot be relative, skip it
_path = _path.substring(3);
continue;
}
_pos = _path.substring(0, _parent).lastIndexOf('/');
if (_pos === -1) {
_pos = _parent;
}
_path = _path.substring(0, _pos) + _path.substring(_parent + 3);
}
// revert to relative
if (_was_relative && this.is('relative')) {
_path = _leadingParents + _path.substring(1);
}
_path = URI.recodePath(_path);
this._parts.path = _path;
this.build(!build);
return this;
};
p.normalizePathname = p.normalizePath;
p.normalizeQuery = function(build) {
if (typeof this._parts.query === 'string') {
if (!this._parts.query.length) {
this._parts.query = null;
} else {
this.query(URI.parseQuery(this._parts.query, this._parts.escapeQuerySpace));
}
this.build(!build);
}
return this;
};
p.normalizeFragment = function(build) {
if (!this._parts.fragment) {
this._parts.fragment = null;
this.build(!build);
}
return this;
};
p.normalizeSearch = p.normalizeQuery;
p.normalizeHash = p.normalizeFragment;
p.iso8859 = function() {
// expect unicode input, iso8859 output
var e = URI.encode;
var d = URI.decode;
URI.encode = escape;
URI.decode = decodeURIComponent;
try {
this.normalize();
} finally {
URI.encode = e;
URI.decode = d;
}
return this;
};
p.unicode = function() {
// expect iso8859 input, unicode output
var e = URI.encode;
var d = URI.decode;
URI.encode = strictEncodeURIComponent;
URI.decode = unescape;
try {
this.normalize();
} finally {
URI.encode = e;
URI.decode = d;
}
return this;
};
p.readable = function() {
var uri = this.clone();
// removing username, password, because they shouldn't be displayed according to RFC 3986
uri.username('').password('').normalize();
var t = '';
if (uri._parts.protocol) {
t += uri._parts.protocol + '://';
}
if (uri._parts.hostname) {
if (uri.is('punycode') && punycode) {
t += punycode.toUnicode(uri._parts.hostname);
if (uri._parts.port) {
t += ':' + uri._parts.port;
}
} else {
t += uri.host();
}
}
if (uri._parts.hostname && uri._parts.path && uri._parts.path.charAt(0) !== '/') {
t += '/';
}
t += uri.path(true);
if (uri._parts.query) {
var q = '';
for (var i = 0, qp = uri._parts.query.split('&'), l = qp.length; i < l; i++) {
var kv = (qp[i] || '').split('=');
q += '&' + URI.decodeQuery(kv[0], this._parts.escapeQuerySpace)
.replace(/&/g, '%26');
if (kv[1] !== undefined) {
q += '=' + URI.decodeQuery(kv[1], this._parts.escapeQuerySpace)
.replace(/&/g, '%26');
}
}
t += '?' + q.substring(1);
}
t += URI.decodeQuery(uri.hash(), true);
return t;
};
// resolving relative and absolute URLs
p.absoluteTo = function(base) {
var resolved = this.clone();
var properties = ['protocol', 'username', 'password', 'hostname', 'port'];
var basedir, i, p;
if (this._parts.urn) {
throw new Error('URNs do not have any generally defined hierarchical components');
}
if (!(base instanceof URI)) {
base = new URI(base);
}
if (!resolved._parts.protocol) {
resolved._parts.protocol = base._parts.protocol;
}
if (this._parts.hostname) {
return resolved;
}
for (i = 0; (p = properties[i]); i++) {
resolved._parts[p] = base._parts[p];
}
if (!resolved._parts.path) {
resolved._parts.path = base._parts.path;
if (!resolved._parts.query) {
resolved._parts.query = base._parts.query;
}
} else if (resolved._parts.path.substring(-2) === '..') {
resolved._parts.path += '/';
}
if (resolved.path().charAt(0) !== '/') {
basedir = base.directory();
basedir = basedir ? basedir : base.path().indexOf('/') === 0 ? '/' : '';
resolved._parts.path = (basedir ? (basedir + '/') : '') + resolved._parts.path;
resolved.normalizePath();
}
resolved.build();
return resolved;
};
p.relativeTo = function(base) {
var relative = this.clone().normalize();
var relativeParts, baseParts, common, relativePath, basePath;
if (relative._parts.urn) {
throw new Error('URNs do not have any generally defined hierarchical components');
}
base = new URI(base).normalize();
relativeParts = relative._parts;
baseParts = base._parts;
relativePath = relative.path();
basePath = base.path();
if (relativePath.charAt(0) !== '/') {
throw new Error('URI is already relative');
}
if (basePath.charAt(0) !== '/') {
throw new Error('Cannot calculate a URI relative to another relative URI');
}
if (relativeParts.protocol === baseParts.protocol) {
relativeParts.protocol = null;
}
if (relativeParts.username !== baseParts.username || relativeParts.password !== baseParts.password) {
return relative.build();
}
if (relativeParts.protocol !== null || relativeParts.username !== null || relativeParts.password !== null) {
return relative.build();
}
if (relativeParts.hostname === baseParts.hostname && relativeParts.port === baseParts.port) {
relativeParts.hostname = null;
relativeParts.port = null;
} else {
return relative.build();
}
if (relativePath === basePath) {
relativeParts.path = '';
return relative.build();
}
// determine common sub path
common = URI.commonPath(relativePath, basePath);
// If the paths have nothing in common, return a relative URL with the absolute path.
if (!common) {
return relative.build();
}
var parents = baseParts.path
.substring(common.length)
.replace(/[^\/]*$/, '')
.replace(/.*?\//g, '../');
relativeParts.path = (parents + relativeParts.path.substring(common.length)) || './';
return relative.build();
};
// comparing URIs
p.equals = function(uri) {
var one = this.clone();
var two = new URI(uri);
var one_map = {};
var two_map = {};
var checked = {};
var one_query, two_query, key;
one.normalize();
two.normalize();
// exact match
if (one.toString() === two.toString()) {
return true;
}
// extract query string
one_query = one.query();
two_query = two.query();
one.query('');
two.query('');
// definitely not equal if not even non-query parts match
if (one.toString() !== two.toString()) {
return false;
}
// query parameters have the same length, even if they're permuted
if (one_query.length !== two_query.length) {
return false;
}
one_map = URI.parseQuery(one_query, this._parts.escapeQuerySpace);
two_map = URI.parseQuery(two_query, this._parts.escapeQuerySpace);
for (key in one_map) {
if (hasOwn.call(one_map, key)) {
if (!isArray(one_map[key])) {
if (one_map[key] !== two_map[key]) {
return false;
}
} else if (!arraysEqual(one_map[key], two_map[key])) {
return false;
}
checked[key] = true;
}
}
for (key in two_map) {
if (hasOwn.call(two_map, key)) {
if (!checked[key]) {
// two contains a parameter not present in one
return false;
}
}
}
return true;
};
// state
p.duplicateQueryParameters = function(v) {
this._parts.duplicateQueryParameters = !!v;
return this;
};
p.escapeQuerySpace = function(v) {
this._parts.escapeQuerySpace = !!v;
return this;
};
return URI;
}));
},{"./IPv6":2,"./SecondLevelDomains":2,"./punycode":2}],6:[function(require,module,exports){
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
var Spine = require('./spine');
var Locations = require('./locations');
var Parser = require('./parser');
var Navigation = require('./navigation');
var Rendition = require('./rendition');
var Continuous = require('./continuous');
var Paginate = require('./paginate');
var Unarchive = require('./unarchive');
var request = require('./request');
function Book(_url, options){
// Promises
this.opening = new RSVP.defer();
this.opened = this.opening.promise;
this.isOpen = false;
this.url = undefined;
this.loading = {
manifest: new RSVP.defer(),
spine: new RSVP.defer(),
metadata: new RSVP.defer(),
cover: new RSVP.defer(),
navigation: new RSVP.defer(),
pageList: new RSVP.defer()
};
this.loaded = {
manifest: this.loading.manifest.promise,
spine: this.loading.spine.promise,
metadata: this.loading.metadata.promise,
cover: this.loading.cover.promise,
navigation: this.loading.navigation.promise,
pageList: this.loading.pageList.promise
};
this.ready = RSVP.hash(this.loaded);
// Queue for methods used before opening
this.isRendered = false;
// this._q = core.queue(this);
this.request = this.requestMethod.bind(this);
this.spine = new Spine(this.request);
this.locations = new Locations(this.spine, this.request);
if(_url) {
this.open(_url);
}
};
Book.prototype.open = function(_url){
var uri;
var parse = new Parser();
var epubPackage;
var epubContainer;
var book = this;
var containerPath = "META-INF/container.xml";
var location;
if(!_url) {
this.opening.resolve(this);
return this.opened;
}
// Reuse parsed url or create a new uri object
// if(typeof(_url) === "object") {
// uri = _url;
// } else {
// uri = core.uri(_url);
// }
uri = URI(_url);
this.url = _url;
// Find path to the Container
if(uri.suffix() === "opf") {
// Direct link to package, no container
this.packageUrl = _url;
this.containerUrl = '';
if(uri.origin()) {
this.baseUrl = uri.origin() + "/" + uri.directory() + "/";
} else if(window){
this.baseUrl = uri.absoluteTo(window.location.href).directory() + "/";
} else {
this.baseUrl = uri.directory() + "/";
}
epubPackage = this.request(this.packageUrl);
} else if(this.isArchivedUrl(uri)) {
// Book is archived
this.url = '/';
this.containerUrl = URI(containerPath).absoluteTo(this.url).toString();
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.suffix()) {
this.containerUrl = this.url + containerPath;
epubContainer = this.request(this.containerUrl);
}
if (epubContainer) {
epubPackage = epubContainer.
then(function(containerXml){
return parse.container(containerXml); // Container has path to content
}).
then(function(paths){
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.origin() + "/" + packageUri.directory() + "/";
} else if(window && !book.isArchivedUrl(uri)){
book.baseUrl = packageUri.absoluteTo(window.location.href).directory() + "/";
} else {
book.baseUrl = "/" + packageUri.directory() + "/";
}
return book.request(book.packageUrl);
}).catch(function(error) {
// handle errors in either of the two requests
console.error("Could not load book at: " + (this.packageUrl || this.containerPath));
book.trigger("book:loadFailed", (this.packageUrl || this.containerPath));
book.opening.reject(error);
});
}
epubPackage.then(function(packageXml) {
// Get package information from epub opf
book.unpack(packageXml);
// Resolve promises
book.loading.manifest.resolve(book.package.manifest);
book.loading.metadata.resolve(book.package.metadata);
book.loading.spine.resolve(book.spine);
book.loading.cover.resolve(book.cover);
book.isOpen = true;
// Clear queue of any waiting book request
// Resolve book opened promise
book.opening.resolve(book);
}).catch(function(error) {
// handle errors in parsing the book
console.error(error.message, error.stack);
book.opening.reject(error);
});
return this.opened;
};
Book.prototype.unpack = function(packageXml){
var book = this,
parse = new Parser();
book.package = parse.packageContents(packageXml); // Extract info from contents
book.package.baseUrl = book.baseUrl; // Provides a url base for resolving paths
this.spine.load(book.package);
book.navigation = new Navigation(book.package, this.request);
book.navigation.load().then(function(toc){
book.toc = toc;
book.loading.navigation.resolve(book.toc);
});
// //-- Set Global Layout setting based on metadata
// MOVE TO RENDER
// book.globalLayoutProperties = book.parseLayoutProperties(book.package.metadata);
book.cover = URI(book.package.coverPath).absoluteTo(book.baseUrl).toString();
};
// Alias for book.spine.get
Book.prototype.section = function(target) {
return this.spine.get(target);
};
// Sugar to render a book
Book.prototype.renderTo = function(element, options) {
var renderMethod = (options && options.method) ?
options.method :
"rendition";
var Renderer = require('./'+renderMethod);
this.rendition = new Renderer(this, options);
this.rendition.attachTo(element);
return this.rendition;
};
Book.prototype.requestMethod = function(_url) {
// Switch request methods
if(this.archive) {
return this.archive.request(_url);
} else {
return request(_url, null, this.requestCredentials, this.requestHeaders);
}
};
Book.prototype.setRequestCredentials = function(_credentials) {
this.requestCredentials = _credentials;
};
Book.prototype.setRequestHeaders = function(_headers) {
this.requestHeaders = _headers;
};
Book.prototype.unarchive = function(bookUrl){
this.archive = new Unarchive();
return this.archive.open(bookUrl);
};
//-- Checks if url has a .epub or .zip extension, or is ArrayBuffer (of zip/epub)
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);
// }
uri = URI(bookUrl);
extension = uri.suffix();
if(extension && (extension == "epub" || extension == "zip")){
return true;
}
return false;
};
//-- Returns the cover
Book.prototype.coverUrl = function(){
var retrieved = this.loaded.cover.
then(function(url) {
if(this.archive) {
return this.archive.createUrl(this.cover);
}else{
return this.cover;
}
}.bind(this));
return retrieved;
};
module.exports = Book;
//-- Enable binding events to book
RSVP.EventTarget.mixin(Book.prototype);
//-- Handle RSVP Errors
RSVP.on('error', function(event) {
console.error(event);
});
RSVP.configure('instrument', true); //-- true | will logging out all RSVP rejections
// RSVP.on('created', listener);
// RSVP.on('chained', listener);
// RSVP.on('fulfilled', listener);
RSVP.on('rejected', function(event){
console.error(event.detail.message, event.detail.stack);
});
},{"./continuous":7,"./core":8,"./locations":12,"./navigation":14,"./paginate":15,"./parser":16,"./rendition":18,"./request":20,"./spine":22,"./unarchive":23,"rsvp":4,"urijs":5}],7:[function(require,module,exports){
var RSVP = require('rsvp');
var core = require('./core');
var Rendition = require('./rendition');
var View = require('./view');
function Continuous(book, options) {
Rendition.apply(this, arguments); // call super constructor.
this.settings = core.extend(this.settings || {}, {
infinite: true,
overflow: "auto",
axis: "vertical",
offset: 500,
offsetDelta: 250
});
core.extend(this.settings, options);
if(this.settings.hidden) {
this.wrapper = this.wrap(this.container);
}
};
// subclass extends superclass
Continuous.prototype = Object.create(Rendition.prototype);
Continuous.prototype.constructor = Continuous;
Continuous.prototype.attachListeners = function(){
// Listen to window for resize event if width or height is set to a percent
if(!core.isNumber(this.settings.width) ||
!core.isNumber(this.settings.height) ) {
window.addEventListener("resize", this.onResized.bind(this), false);
}
if(this.settings.infinite) {
this.start();
}
};
Continuous.prototype.parseTarget = function(target){
if(this.epubcfi.isCfiString(target)) {
cfi = this.epubcfi.parse(target);
spinePos = cfi.spinePos;
section = this.book.spine.get(spinePos);
} else {
section = this.book.spine.get(target);
}
};
Continuous.prototype.moveTo = function(offset){
// var bounds = this.bounds();
// var dist = Math.floor(offset.top / bounds.height) * bounds.height;
return this.check(
offset.left+this.settings.offset,
offset.top+this.settings.offset)
.then(function(){
if(this.settings.axis === "vertical") {
this.scrollBy(0, offset.top);
} else {
this.scrollBy(offset.left, 0);
}
}.bind(this));
};
Continuous.prototype.afterDisplayed = function(currView){
var next = currView.section.next();
var prev = currView.section.prev();
var index = this.views.indexOf(currView);
var prevView, nextView;
if(index + 1 === this.views.length && next) {
nextView = this.createView(next);
this.q.enqueue(this.append, nextView);
}
if(index === 0 && prev) {
prevView = this.createView(prev, this.viewSettings);
this.q.enqueue(this.prepend, prevView);
}
// this.removeShownListeners(currView);
// currView.onShown = this.afterDisplayed.bind(this);
this.trigger("added", currView.section);
};
// Remove Previous Listeners if present
Continuous.prototype.removeShownListeners = function(view){
// view.off("shown", this.afterDisplayed);
// view.off("shown", this.afterDisplayedAbove);
view.onDisplayed = function(){};
};
Continuous.prototype.append = function(view){
// view.on("shown", this.afterDisplayed.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
this.views.append(view);
//this.q.enqueue(this.check);
return this.check();
};
Continuous.prototype.prepend = function(view){
// view.on("shown", this.afterDisplayedAbove.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
view.on("resized", this.counter.bind(this));
this.views.prepend(view);
// this.q.enqueue(this.check);
return this.check();
};
Continuous.prototype.counter = function(bounds){
if(this.settings.axis === "vertical") {
this.scrollBy(0, bounds.heightDelta, true);
} else {
this.scrollBy(bounds.widthDelta, 0, true);
}
};
Continuous.prototype.check = function(_offset){
var checking = new RSVP.defer();
var container = this.bounds();
var promises = [];
var offset = _offset || this.settings.offset;
this.views.each(function(view){
var visible = this.isVisible(view, offset, offset, container);
if(visible) {
if(!view.displayed && !view.rendering) {
// console.log("render",view.section.index)
promises.push(this.render(view));
}
} else {
if(view.displayed) {
// console.log("destroy", view.section.index)
this.q.enqueue(view.destroy.bind(view));
// view.destroy();
// this.q.enqueue(this.trim);
clearTimeout(this.trimTimeout);
this.trimTimeout = setTimeout(function(){
this.q.enqueue(this.trim);
}.bind(this), 250);
}
}
}.bind(this));
if(promises.length){
return RSVP.all(promises)
.then(function(posts) {
// Check to see if anything new is on screen after rendering
this.q.enqueue(this.check);
}.bind(this));
} else {
checking.resolve();
return checking.promise;
}
};
Continuous.prototype.trim = function(){
var task = new RSVP.defer();
var displayed = this.views.displayed();
var first = displayed[0];
var last = displayed[displayed.length-1];
var firstIndex = this.views.indexOf(first);
var lastIndex = this.views.indexOf(last);
var above = this.views.slice(0, firstIndex);
var below = this.views.slice(lastIndex+1);
// Erase all but last above
for (var i = 0; i < above.length-1; i++) {
this.erase(above[i], above);
}
// Erase all except first below
for (var j = 1; j < below.length; j++) {
this.erase(below[j]);
}
task.resolve();
return task.promise;
};
Continuous.prototype.erase = function(view, above){ //Trim
var prevTop;
var prevLeft;
if(this.settings.height) {
prevTop = this.container.scrollTop;
prevLeft = this.container.scrollLeft;
} else {
prevTop = window.scrollY;
prevLeft = window.scrollX;
}
var bounds = view.bounds();
this.views.remove(view);
if(above) {
if(this.settings.axis === "vertical") {
this.scrollTo(0, prevTop - bounds.height, true);
} else {
this.scrollTo(prevLeft - bounds.width, 0, true);
}
}
};
Continuous.prototype.start = function() {
var scroller;
this.tick = core.requestAnimationFrame;
if(this.settings.height) {
this.prevScrollTop = this.container.scrollTop;
this.prevScrollLeft = this.container.scrollLeft;
} else {
this.prevScrollTop = window.scrollY;
this.prevScrollLeft = window.scrollX;
}
this.scrollDeltaVert = 0;
this.scrollDeltaHorz = 0;
if(this.settings.height) {
scroller = this.container;
} else {
scroller = window;
}
window.addEventListener("scroll", function(e){
if(!this.ignore) {
this.scrolled = true;
} else {
this.ignore = false;
}
}.bind(this));
window.addEventListener('unload', function(e){
this.ignore = true;
this.destroy();
}.bind(this));
this.tick.call(window, this.onScroll.bind(this));
this.scrolled = false;
};
Continuous.prototype.onScroll = function(){
if(this.scrolled) {
if(this.settings.height) {
scrollTop = this.container.scrollTop;
scrollLeft = this.container.scrollLeft;
} else {
scrollTop = window.scrollY;
scrollLeft = window.scrollX;
}
if(!this.ignore) {
if((this.scrollDeltaVert === 0 &&
this.scrollDeltaHorz === 0) ||
this.scrollDeltaVert > this.settings.offsetDelta ||
this.scrollDeltaHorz > this.settings.offsetDelta) {
this.q.enqueue(this.check);
this.scrollDeltaVert = 0;
this.scrollDeltaHorz = 0;
this.trigger("scroll", {
top: scrollTop,
left: scrollLeft
});
}
} else {
this.ignore = false;
}
this.scrollDeltaVert += Math.abs(scrollTop-this.prevScrollTop);
this.scrollDeltaHorz += Math.abs(scrollLeft-this.prevScrollLeft);
this.prevScrollTop = scrollTop;
this.prevScrollLeft = scrollLeft;
clearTimeout(this.scrollTimeout);
this.scrollTimeout = setTimeout(function(){
this.scrollDeltaVert = 0;
this.scrollDeltaHorz = 0;
}.bind(this), 150);
this.scrolled = false;
}
this.tick.call(window, this.onScroll.bind(this));
};
Continuous.prototype.resizeView = function(view) {
if(this.settings.axis === "horizontal") {
view.lock("height", this.stage.width, this.stage.height);
} else {
view.lock("width", this.stage.width, this.stage.height);
}
};
Continuous.prototype.currentLocation = function(){
var visible = this.visible();
var startPage, endPage;
var container = this.container.getBoundingClientRect();
if(visible.length === 1) {
return this.map.page(visible[0]);
}
if(visible.length > 1) {
startPage = this.map.page(visible[0]);
endPage = this.map.page(visible[visible.length-1]);
return {
start: startPage.start,
end: endPage.end
};
}
};
/*
Continuous.prototype.current = function(what){
var view, top;
var container = this.container.getBoundingClientRect();
var length = this.views.length - 1;
if(this.settings.axis === "horizontal") {
for (var i = length; i >= 0; i--) {
view = this.views[i];
left = view.position().left;
if(left < container.right) {
if(this._current == view) {
break;
}
this._current = view;
break;
}
}
} else {
for (var i = length; i >= 0; i--) {
view = this.views[i];
top = view.bounds().top;
if(top < container.bottom) {
if(this._current == view) {
break;
}
this._current = view;
break;
}
}
}
return this._current;
};
*/
module.exports = Continuous;
},{"./core":8,"./rendition":18,"./view":24,"rsvp":4}],8:[function(require,module,exports){
var RSVP = require('rsvp');
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
//-- Parse the different parts of a url, returning a object
function uri(url){
var uri = {
protocol : '',
host : '',
path : '',
origin : '',
directory : '',
base : '',
filename : '',
extension : '',
fragment : '',
href : url
},
doubleSlash = url.indexOf('://'),
search = url.indexOf('?'),
fragment = url.indexOf("#"),
withoutProtocol,
dot,
firstSlash;
if(fragment != -1) {
uri.fragment = url.slice(fragment + 1);
url = url.slice(0, fragment);
}
if(search != -1) {
uri.search = url.slice(search + 1);
url = url.slice(0, search);
href = url;
}
if(doubleSlash != -1) {
uri.protocol = url.slice(0, doubleSlash);
withoutProtocol = url.slice(doubleSlash+3);
firstSlash = withoutProtocol.indexOf('/');
if(firstSlash === -1) {
uri.host = uri.path;
uri.path = "";
} else {
uri.host = withoutProtocol.slice(0, firstSlash);
uri.path = withoutProtocol.slice(firstSlash);
}
uri.origin = uri.protocol + "://" + uri.host;
uri.directory = folder(uri.path);
uri.base = uri.origin + uri.directory;
// return origin;
} else {
uri.path = url;
uri.directory = folder(url);
uri.base = uri.directory;
}
//-- Filename
uri.filename = url.replace(uri.base, '');
dot = uri.filename.lastIndexOf('.');
if(dot != -1) {
uri.extension = uri.filename.slice(dot+1);
}
return uri;
};
//-- Parse out the folder, will return everything before the last slash
function folder(url){
var lastSlash = url.lastIndexOf('/');
if(lastSlash == -1) var folder = '';
folder = url.slice(0, lastSlash + 1);
return folder;
};
function isElement(obj) {
return !!(obj && obj.nodeType == 1);
};
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
function uuid() {
var d = new Date().getTime();
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = (d + Math.random()*16)%16 | 0;
d = Math.floor(d/16);
return (c=='x' ? r : (r&0x7|0x8)).toString(16);
});
return uuid;
};
// From Lodash
function values(object) {
var index = -1,
props = Object.keys(object),
length = props.length,
result = Array(length);
while (++index < length) {
result[index] = object[props[index]];
}
return result;
};
function resolveUrl(base, path) {
var url = [],
segments = [],
baseUri = uri(base),
pathUri = uri(path),
baseDirectory = baseUri.directory,
pathDirectory = pathUri.directory,
directories = [],
// folders = base.split("/"),
paths;
// if(uri.host) {
// return path;
// }
if(baseDirectory[0] === "/") {
baseDirectory = baseDirectory.substring(1);
}
if(pathDirectory[pathDirectory.length-1] === "/") {
baseDirectory = baseDirectory.substring(0, baseDirectory.length-1);
}
if(pathDirectory[0] === "/") {
pathDirectory = pathDirectory.substring(1);
}
if(pathDirectory[pathDirectory.length-1] === "/") {
pathDirectory = pathDirectory.substring(0, pathDirectory.length-1);
}
if(baseDirectory) {
directories = baseDirectory.split("/");
}
paths = pathDirectory.split("/");
paths.reverse().forEach(function(part, index){
if(part === ".."){
directories.pop();
} else if(part === directories[directories.length-1]) {
directories.pop();
segments.unshift(part);
} else {
segments.unshift(part);
}
});
url = [baseUri.origin];
if(directories.length) {
url = url.concat(directories);
}
if(segments) {
url = url.concat(segments);
}
url = url.concat(pathUri.filename);
return url.join("/");
};
function documentHeight() {
return Math.max(
document.documentElement.clientHeight,
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight
);
};
function isNumber(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
function prefixed(unprefixed) {
var vendors = ["Webkit", "Moz", "O", "ms" ],
prefixes = ['-Webkit-', '-moz-', '-o-', '-ms-'],
upper = unprefixed[0].toUpperCase() + unprefixed.slice(1),
length = vendors.length;
if (typeof(document.body.style[unprefixed]) != 'undefined') {
return unprefixed;
}
for ( var i=0; i < length; i++ ) {
if (typeof(document.body.style[vendors[i] + upper]) != 'undefined') {
return vendors[i] + upper;
}
}
return unprefixed;
};
function defaults(obj) {
for (var i = 1, length = arguments.length; i < length; i++) {
var source = arguments[i];
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
}
}
return obj;
};
function extend(target) {
var sources = [].slice.call(arguments, 1);
sources.forEach(function (source) {
if(!source) return;
Object.getOwnPropertyNames(source).forEach(function(propName) {
Object.defineProperty(target, propName, Object.getOwnPropertyDescriptor(source, propName));
});
});
return target;
};
// Fast quicksort insert for sorted array -- based on:
// http://stackoverflow.com/questions/1344500/efficient-way-to-insert-a-number-into-a-sorted-array-of-numbers
function insert(item, array, compareFunction) {
var location = locationOf(item, array, compareFunction);
array.splice(location, 0, item);
return location;
};
// Returns where something would fit in
function locationOf(item, array, compareFunction, _start, _end) {
var start = _start || 0;
var end = _end || array.length;
var pivot = parseInt(start + (end - start) / 2);
var compared;
if(!compareFunction){
compareFunction = function(a, b) {
if(a > b) return 1;
if(a < b) return -1;
if(a = b) return 0;
};
}
if(end-start <= 0) {
return pivot;
}
compared = compareFunction(array[pivot], item);
if(end-start === 1) {
return compared > 0 ? pivot : pivot + 1;
}
if(compared === 0) {
return pivot;
}
if(compared === -1) {
return locationOf(item, array, compareFunction, pivot, end);
} else{
return locationOf(item, array, compareFunction, start, pivot);
}
};
// Returns -1 of mpt found
function indexOfSorted(item, array, compareFunction, _start, _end) {
var start = _start || 0;
var end = _end || array.length;
var pivot = parseInt(start + (end - start) / 2);
var compared;
if(!compareFunction){
compareFunction = function(a, b) {
if(a > b) return 1;
if(a < b) return -1;
if(a = b) return 0;
};
}
if(end-start <= 0) {
return -1; // Not found
}
compared = compareFunction(array[pivot], item);
if(end-start === 1) {
return compared === 0 ? pivot : -1;
}
if(compared === 0) {
return pivot; // Found
}
if(compared === -1) {
return indexOfSorted(item, array, compareFunction, pivot, end);
} else{
return indexOfSorted(item, array, compareFunction, start, pivot);
}
};
function bounds(el) {
var style = window.getComputedStyle(el);
var widthProps = ["width", "paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"];
var heightProps = ["height", "paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"];
var width = 0;
var height = 0;
widthProps.forEach(function(prop){
width += parseFloat(style[prop]) || 0;
});
heightProps.forEach(function(prop){
height += parseFloat(style[prop]) || 0;
});
return {
height: height,
width: width
};
};
function borders(el) {
var style = window.getComputedStyle(el);
var widthProps = ["paddingRight", "paddingLeft", "marginRight", "marginLeft", "borderRightWidth", "borderLeftWidth"];
var heightProps = ["paddingTop", "paddingBottom", "marginTop", "marginBottom", "borderTopWidth", "borderBottomWidth"];
var width = 0;
var height = 0;
widthProps.forEach(function(prop){
width += parseFloat(style[prop]) || 0;
});
heightProps.forEach(function(prop){
height += parseFloat(style[prop]) || 0;
});
return {
height: height,
width: width
};
};
function windowBounds() {
var width = window.innerWidth;
var height = window.innerHeight;
return {
top: 0,
left: 0,
right: width,
bottom: height,
width: width,
height: height
};
};
//https://stackoverflow.com/questions/13482352/xquery-looking-for-text-with-single-quote/13483496#13483496
function cleanStringForXpath(str) {
var parts = str.match(/[^'"]+|['"]/g);
parts = parts.map(function(part){
if (part === "'") {
return '\"\'\"'; // output "'"
}
if (part === '"') {
return "\'\"\'"; // output '"'
}
return "\'" + part + "\'";
});
return "concat(\'\'," + parts.join(",") + ")";
};
function indexOfTextNode(textNode){
var parent = textNode.parentNode;
var children = parent.childNodes;
var sib;
var index = -1;
for (var i = 0; i < children.length; i++) {
sib = children[i];
if(sib.nodeType === Node.TEXT_NODE){
index++;
}
if(sib == textNode) break;
}
return index;
};
function isXml(ext) {
return ['xml', 'opf', 'ncx'].indexOf(ext) > -1;
}
module.exports = {
'uri': uri,
'folder': folder,
'isElement': isElement,
'uuid': uuid,
'values': values,
'resolveUrl': resolveUrl,
'indexOfSorted': indexOfSorted,
'documentHeight': documentHeight,
'isNumber': isNumber,
'prefixed': prefixed,
'defaults': defaults,
'extend': extend,
'insert': insert,
'locationOf': locationOf,
'indexOfSorted': indexOfSorted,
'requestAnimationFrame': requestAnimationFrame,
'bounds': bounds,
'borders': borders,
'windowBounds': windowBounds,
'cleanStringForXpath': cleanStringForXpath,
'indexOfTextNode': indexOfTextNode,
'isXml': isXml
};
},{"rsvp":4}],9:[function(require,module,exports){
var URI = require('urijs');
var core = require('./core');
function EpubCFI(cfiStr){
if(cfiStr) return this.parse(cfiStr);
};
EpubCFI.prototype.generateChapterComponent = function(_spineNodeIndex, _pos, id) {
var pos = parseInt(_pos),
spineNodeIndex = _spineNodeIndex + 1,
cfi = '/'+spineNodeIndex+'/';
cfi += (pos + 1) * 2;
if(id) cfi += "[" + id + "]";
//cfi += "!";
return cfi;
};
EpubCFI.prototype.generatePathComponent = function(steps) {
var parts = [];
steps.forEach(function(part){
var segment = '';
segment += (part.index + 1) * 2;
if(part.id) {
segment += "[" + part.id + "]";
}
parts.push(segment);
});
return parts.join('/');
};
EpubCFI.prototype.generateCfiFromElement = function(element, chapter) {
var steps = this.pathTo(element);
var path = this.generatePathComponent(steps);
if(!path.length) {
// Start of Chapter
return "epubcfi(" + chapter + "!/4/)";
} else {
// First Text Node
return "epubcfi(" + chapter + "!" + path + "/1:0)";
}
};
EpubCFI.prototype.pathTo = function(node) {
var stack = [],
children;
while(node && node.parentNode !== null && node.parentNode.nodeType != 9) {
children = node.parentNode.children;
stack.unshift({
'id' : node.id,
// 'classList' : node.classList,
'tagName' : node.tagName,
'index' : children ? Array.prototype.indexOf.call(children, node) : 0
});
node = node.parentNode;
}
return stack;
};
EpubCFI.prototype.getChapterComponent = function(cfiStr) {
var splitStr = cfiStr.split("!");
return splitStr[0];
};
EpubCFI.prototype.getPathComponent = function(cfiStr) {
var splitStr = cfiStr.split("!");
var pathComponent = splitStr[1] ? splitStr[1].split(":") : '';
return pathComponent[0];
};
EpubCFI.prototype.getCharecterOffsetComponent = function(cfiStr) {
var splitStr = cfiStr.split(":");
return splitStr[1] || '';
};
EpubCFI.prototype.parse = function(cfiStr) {
var cfi = {},
chapSegment,
chapterComponent,
pathComponent,
charecterOffsetComponent,
assertion,
chapId,
path,
end,
endInt,
text,
parseStep = function(part){
var type, index, has_brackets, id;
type = "element";
index = parseInt(part) / 2 - 1;
has_brackets = part.match(/\[(.*)\]/);
if(has_brackets && has_brackets[1]){
id = has_brackets[1];
}
return {
"type" : type,
'index' : index,
'id' : id || false
};
};
if(typeof cfiStr !== "string") {
return {spinePos: -1};
}
cfi.str = cfiStr;
if(cfiStr.indexOf("epubcfi(") === 0 && cfiStr[cfiStr.length-1] === ")") {
// Remove intial epubcfi( and ending )
cfiStr = cfiStr.slice(8, cfiStr.length-1);
}
chapterComponent = this.getChapterComponent(cfiStr);
pathComponent = this.getPathComponent(cfiStr) || '';
charecterOffsetComponent = this.getCharecterOffsetComponent(cfiStr);
// Make sure this is a valid cfi or return
if(!chapterComponent) {
return {spinePos: -1};
}
// Chapter segment is always the second one
chapSegment = chapterComponent.split("/")[2] || '';
if(!chapSegment) return {spinePos:-1};
cfi.spinePos = (parseInt(chapSegment) / 2 - 1 ) || 0;
chapId = chapSegment.match(/\[(.*)\]/);
cfi.spineId = chapId ? chapId[1] : false;
if(pathComponent.indexOf(',') != -1) {
// Handle ranges -- not supported yet
console.warn("CFI Ranges are not supported");
}
path = pathComponent.split('/');
end = path.pop();
cfi.steps = [];
path.forEach(function(part){
var step;
if(part) {
step = parseStep(part);
cfi.steps.push(step);
}
});
//-- Check if END is a text node or element
endInt = parseInt(end);
if(!isNaN(endInt)) {
if(endInt % 2 === 0) { // Even = is an element
cfi.steps.push(parseStep(end));
} else {
cfi.steps.push({
"type" : "text",
'index' : (endInt - 1 ) / 2
});
}
}
assertion = charecterOffsetComponent.match(/\[(.*)\]/);
if(assertion && assertion[1]){
cfi.characterOffset = parseInt(charecterOffsetComponent.split('[')[0]);
// We arent handling these assertions yet
cfi.textLocationAssertion = assertion[1];
} else {
cfi.characterOffset = parseInt(charecterOffsetComponent);
}
return cfi;
};
EpubCFI.prototype.addMarker = function(cfi, _doc, _marker) {
var doc = _doc || document;
var marker = _marker || this.createMarker(doc);
var parent;
var lastStep;
var text;
var split;
if(typeof cfi === 'string') {
cfi = this.parse(cfi);
}
// Get the terminal step
lastStep = cfi.steps[cfi.steps.length-1];
// check spinePos
if(cfi.spinePos === -1) {
// Not a valid CFI
return false;
}
// Find the CFI elements parent
parent = this.findParent(cfi, doc);
if(!parent) {
// CFI didn't return an element
// Maybe it isnt in the current chapter?
return false;
}
if(lastStep && lastStep.type === "text") {
text = parent.childNodes[lastStep.index];
if(cfi.characterOffset){
split = text.splitText(cfi.characterOffset);
marker.classList.add("EPUBJS-CFI-SPLIT");
parent.insertBefore(marker, split);
} else {
parent.insertBefore(marker, text);
}
} else {
parent.insertBefore(marker, parent.firstChild);
}
return marker;
};
EpubCFI.prototype.createMarker = function(_doc) {
var doc = _doc || document;
var element = doc.createElement('span');
element.id = "EPUBJS-CFI-MARKER:"+ core.uuid();
element.classList.add("EPUBJS-CFI-MARKER");
return element;
};
EpubCFI.prototype.removeMarker = function(marker, _doc) {
var doc = _doc || document;
// var id = marker.id;
// Cleanup textnodes if they were split
if(marker.classList.contains("EPUBJS-CFI-SPLIT")){
nextSib = marker.nextSibling;
prevSib = marker.previousSibling;
if(nextSib &&
prevSib &&
nextSib.nodeType === 3 &&
prevSib.nodeType === 3){
prevSib.textContent += nextSib.textContent;
marker.parentNode.removeChild(nextSib);
}
marker.parentNode.removeChild(marker);
} else if(marker.classList.contains("EPUBJS-CFI-MARKER")) {
// Remove only elements added as markers
marker.parentNode.removeChild(marker);
}
};
EpubCFI.prototype.findParent = function(cfi, _doc) {
var doc = _doc || document,
element = doc.getElementsByTagName('html')[0],
children = Array.prototype.slice.call(element.children),
num, index, part, sections,
text, textBegin, textEnd;
if(typeof cfi === 'string') {
cfi = this.parse(cfi);
}
sections = cfi.steps.slice(0); // Clone steps array
if(!sections.length) {
return doc.getElementsByTagName('body')[0];
}
while(sections && sections.length > 0) {
part = sections.shift();
// Find textNodes Parent
if(part.type === "text") {
text = element.childNodes[part.index];
element = text.parentNode || element;
// Find element by id if present
} else if(part.id){
element = doc.getElementById(part.id);
// Find element in parent
}else{
element = children[part.index];
}
// Element can't be found
if(typeof element === "undefined") {
console.error("No Element For", part, cfi.str);
return false;
}
// Get current element children and continue through steps
children = Array.prototype.slice.call(element.children);
}
return element;
};
EpubCFI.prototype.compare = function(cfiOne, cfiTwo) {
if(typeof cfiOne === 'string') {
cfiOne = new EpubCFI(cfiOne);
}
if(typeof cfiTwo === 'string') {
cfiTwo = new EpubCFI(cfiTwo);
}
// Compare Spine Positions
if(cfiOne.spinePos > cfiTwo.spinePos) {
return 1;
}
if(cfiOne.spinePos < cfiTwo.spinePos) {
return -1;
}
// Compare Each Step in the First item
for (var i = 0; i < cfiOne.steps.length; i++) {
if(!cfiTwo.steps[i]) {
return 1;
}
if(cfiOne.steps[i].index > cfiTwo.steps[i].index) {
return 1;
}
if(cfiOne.steps[i].index < cfiTwo.steps[i].index) {
return -1;
}
// Otherwise continue checking
}
// All steps in First present in Second
if(cfiOne.steps.length < cfiTwo.steps.length) {
return -1;
}
// Compare the charecter offset of the text node
if(cfiOne.characterOffset > cfiTwo.characterOffset) {
return 1;
}
if(cfiOne.characterOffset < cfiTwo.characterOffset) {
return -1;
}
// CFI's are equal
return 0;
};
EpubCFI.prototype.generateCfiFromHref = function(href, book) {
var uri = 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 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;
};
EpubCFI.prototype.generateCfiFromTextNode = function(anchor, offset, base) {
var parent = anchor.parentNode;
var steps = this.pathTo(parent);
var path = this.generatePathComponent(steps);
var index = 1 + (2 * Array.prototype.indexOf.call(parent.childNodes, anchor));
return "epubcfi(" + base + "!" + path + "/"+index+":"+(offset || 0)+")";
};
EpubCFI.prototype.generateCfiFromRangeAnchor = function(range, base) {
var anchor = range.anchorNode;
var offset = range.anchorOffset;
return this.generateCfiFromTextNode(anchor, offset, base);
};
EpubCFI.prototype.generateCfiFromRange = function(range, base) {
var start, startElement, startSteps, startPath, startOffset, startIndex;
var end, endElement, endSteps, endPath, endOffset, endIndex;
start = range.startContainer;
if(start.nodeType === 3) { // text node
startElement = start.parentNode;
//startIndex = 1 + (2 * Array.prototype.indexOf.call(startElement.childNodes, start));
startIndex = 1 + (2 * core.indexOfTextNode(start));
startSteps = this.pathTo(startElement);
} else if(range.collapsed) {
return this.generateCfiFromElement(start, base); // single element
} else {
startSteps = this.pathTo(start);
}
startPath = this.generatePathComponent(startSteps);
startOffset = range.startOffset;
if(!range.collapsed) {
end = range.endContainer;
if(end.nodeType === 3) { // text node
endElement = end.parentNode;
// endIndex = 1 + (2 * Array.prototype.indexOf.call(endElement.childNodes, end));
endIndex = 1 + (2 * core.indexOfTextNode(end));
endSteps = this.pathTo(endElement);
} else {
endSteps = this.pathTo(end);
}
endPath = this.generatePathComponent(endSteps);
endOffset = range.endOffset;
// Remove steps present in startPath
endPath = endPath.replace(startPath, '');
if (endPath.length) {
endPath = endPath + "/";
}
return "epubcfi(" + base + "!" + startPath + "/" + startIndex + ":" + startOffset + "," + endPath + endIndex + ":" + endOffset + ")";
} else {
return "epubcfi(" + base + "!" + startPath + "/"+ startIndex +":"+ startOffset +")";
}
};
EpubCFI.prototype.generateXpathFromSteps = function(steps) {
var xpath = [".", "*"];
steps.forEach(function(step){
var position = step.index + 1;
if(step.id){
xpath.push("*[position()=" + position + " and @id='" + step.id + "']");
} else if(step.type === "text") {
xpath.push("text()[" + position + "]");
} else {
xpath.push("*[" + position + "]");
}
});
return xpath.join("/");
};
EpubCFI.prototype.generateQueryFromSteps = function(steps) {
var query = ["html"];
steps.forEach(function(step){
var position = step.index + 1;
if(step.id){
query.push("#" + step.id);
} else if(step.type === "text") {
// unsupported in querySelector
// query.push("text()[" + position + "]");
} else {
query.push("*:nth-child(" + position + ")");
}
});
return query.join(">");
};
EpubCFI.prototype.generateRangeFromCfi = function(cfi, _doc) {
var doc = _doc || document;
var range = doc.createRange();
var lastStep;
var xpath;
var startContainer;
var textLength;
var query;
var startContainerParent;
if(typeof cfi === 'string') {
cfi = this.parse(cfi);
}
// check spinePos
if(cfi.spinePos === -1) {
// Not a valid CFI
return false;
}
// Get the terminal step
lastStep = cfi.steps[cfi.steps.length-1];
if(typeof document.evaluate != 'undefined') {
xpath = this.generateXpathFromSteps(cfi.steps);
startContainer = doc.evaluate(xpath, doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
} else {
// Get the query string
query = this.generateQueryFromSteps(cfi.steps);
// Find the containing element
startContainerParent = doc.querySelector(query);
// Find the text node within that element
if(startContainerParent && lastStep.type == "text") {
startContainer = startContainerParent.childNodes[lastStep.index];
}
}
if(!startContainer) {
return null;
}
if(startContainer && cfi.characterOffset >= 0) {
textLength = startContainer.length;
if(cfi.characterOffset < textLength) {
range.setStart(startContainer, cfi.characterOffset);
range.setEnd(startContainer, textLength );
} else {
console.debug("offset greater than length:", cfi.characterOffset, textLength);
range.setStart(startContainer, textLength - 1 );
range.setEnd(startContainer, textLength );
}
} else if(startContainer) {
range.selectNode(startContainer);
}
// doc.defaultView.getSelection().addRange(range);
return range;
};
EpubCFI.prototype.isCfiString = function(target) {
if(typeof target === "string" &&
target.indexOf("epubcfi(") === 0) {
return true;
}
return false;
};
module.exports = EpubCFI;
},{"./core":8,"urijs":5}],10:[function(require,module,exports){
var RSVP = require('rsvp');
//-- 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(){});
function Hook(context){
this.context = context || this;
this.hooks = [];
};
// Adds a function to be run before a hook completes
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
Hook.prototype.trigger = function(){
var args = arguments;
var context = this.context;
var promises = [];
this.hooks.forEach(function(task, i) {
var executing = task.apply(context, args);
if(executing && typeof executing["then"] === "function") {
// Task is a function that returns a promise
promises.push(executing);
}
// Otherwise Task resolves immediately, continue
});
return RSVP.all(promises);
};
// Adds a function to be run before a hook completes
Hook.prototype.list = function(){
return this.hooks;
};
module.exports = Hook;
},{"rsvp":4}],11:[function(require,module,exports){
var core = require('./core');
function Reflowable(){
};
Reflowable.prototype.calculate = function(_width, _height, _gap, _devisor){
var divisor = _devisor || 1;
//-- Check the width and create even width columns
var fullWidth = Math.floor(_width);
var width = (fullWidth % 2 === 0) ? fullWidth : fullWidth - 1;
var section = Math.floor(width / 8);
var gap = (_gap >= 0) ? _gap : ((section % 2 === 0) ? section : section - 1);
var colWidth;
var spreadWidth;
var delta;
//-- Double Page
if(divisor > 1) {
colWidth = Math.floor((width - gap) / divisor);
} else {
colWidth = width;
}
spreadWidth = colWidth * divisor;
delta = (colWidth + gap) * divisor;
this.columnAxis = core.prefixed('columnAxis');
this.columnGap = core.prefixed('columnGap');
this.columnWidth = core.prefixed('columnWidth');
this.columnFill = core.prefixed('columnFill');
this.width = width;
this.height = _height;
this.spread = spreadWidth;
this.delta = delta;
this.column = colWidth;
this.gap = gap;
this.divisor = divisor;
};
Reflowable.prototype.format = function(view){
var $doc = view.document.documentElement;
var $body = view.document.body;//view.document.querySelector("body");
$doc.style.overflow = "hidden";
// Must be set to the new calculated width or the columns will be off
// $body.style.width = this.width + "px";
$doc.style.width = this.width + "px";
//-- Adjust height
$body.style.height = this.height + "px";
//-- Add columns
$body.style[this.columnAxis] = "horizontal";
$body.style[this.columnFill] = "auto";
$body.style[this.columnGap] = this.gap+"px";
$body.style[this.columnWidth] = this.column+"px";
// Add extra padding for the gap between this and the next view
view.iframe.style.marginRight = this.gap+"px";
};
Reflowable.prototype.count = function(view) {
var totalWidth = view.root().scrollWidth;
var spreads = Math.ceil(totalWidth / this.spread);
return {
spreads : spreads,
pages : spreads * this.divisor
};
};
function Fixed(_width, _height){
};
Fixed.prototype.calculate = function(_width, _height){
};
Fixed.prototype.format = function(view){
var width, height;
var $doc = view.document.documentElement;
var $viewport = documentElement.querySelector("[name=viewport");
/**
* check for the viewport size
* <meta name="viewport" content="width=1024,height=697" />
*/
if($viewport && $viewport.hasAttribute("content")) {
content = $viewport.getAttribute("content");
contents = content.split(',');
if(contents[0]){
width = contents[0].replace("width=", '');
}
if(contents[1]){
height = contents[1].replace("height=", '');
}
}
//-- Adjust width and height
// $doc.style.width = width + "px" || "auto";
// $doc.style.height = height + "px" || "auto";
view.resize(width, height);
//-- Scroll
$doc.style.overflow = "auto";
};
Fixed.prototype.count = function(){
return {
spreads : 1,
pages : 1
};
};
function Scroll(){
};
Scroll.prototype.calculate = function(_width, _height){
this.spread = _width;
this.column = _width;
this.gap = 0;
};
Scroll.prototype.format = function(view){
var $doc = view.document.documentElement;
$doc.style.width = "auto";
$doc.style.height = "auto";
};
Scroll.prototype.count = function(){
return {
spreads : 1,
pages : 1
};
};
module.exports = {
'Reflowable': Reflowable,
'Fixed': Fixed,
'Scroll': Scroll
};
},{"./core":8}],12:[function(require,module,exports){
var core = require('./core');
var Queue = require('./queue');
var EpubCFI = require('./epubcfi');
var RSVP = require('rsvp');
function Locations(spine, request) {
this.spine = spine;
this.request = request;
this.q = new Queue(this);
this.epubcfi = new EpubCFI();
this._locations = [];
this.total = 0;
this.break = 150;
this._current = 0;
};
// Load all of sections in the book
Locations.prototype.generate = function(chars) {
if (chars) {
this.break = chars;
}
this.q.pause();
this.spine.each(function(section) {
this.q.enqueue(this.process, section);
}.bind(this));
return this.q.run().then(function() {
this.total = this._locations.length-1;
if (this._currentCfi) {
this.currentLocation = this._currentCfi;
}
return this._locations;
// console.log(this.precentage(this.book.rendition.location.start), this.precentage(this.book.rendition.location.end));
}.bind(this));
};
Locations.prototype.process = function(section) {
return section.load(this.request)
.then(function(contents) {
var range;
var doc = contents.ownerDocument;
var counter = 0;
this.sprint(contents, function(node) {
var len = node.length;
var dist;
var pos = 0;
// Start range
if (counter == 0) {
range = doc.createRange();
range.setStart(node, 0);
}
dist = this.break - counter;
// Node is smaller than a break
if(dist > len){
counter += len;
pos = len;
}
while (pos < len) {
counter = this.break;
pos += this.break;
// Gone over
if(pos >= len){
// Continue counter for next node
counter = len - (pos - this.break);
// At End
} else {
// End the previous range
range.setEnd(node, pos);
cfi = section.cfiFromRange(range);
this._locations.push(cfi);
counter = 0;
// Start new range
pos += 1;
range = doc.createRange();
range.setStart(node, pos);
}
}
}.bind(this));
// Close remaining
if (range) {
range.setEnd(prev, prev.length);
cfi = section.cfiFromRange(range);
this._locations.push(cfi)
counter = 0;
}
}.bind(this));
};
Locations.prototype.sprint = function(root, func) {
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, null, false);
while ((node = treeWalker.nextNode())) {
func(node);
}
};
Locations.prototype.locationFromCfi = function(cfi){
// Check if the location has not been set yet
if(this._locations.length === 0) {
return -1;
}
return core.locationOf(cfi, this._locations, this.epubcfi.compare);
};
Locations.prototype.precentageFromCfi = function(cfi) {
// Find closest cfi
var loc = this.locationFromCfi(cfi);
// Get percentage in total
return this.precentageFromLocation(loc);
};
Locations.prototype.percentageFromLocation = function(loc) {
if (!loc || !this.total) {
return 0;
}
return (loc / this.total);
};
Locations.prototype.cfiFromLocation = function(loc){
var cfi = -1;
// check that pg is an int
if(typeof loc != "number"){
loc = parseInt(pg);
}
if(loc >= 0 && loc < this._locations.length) {
cfi = this._locations[loc];
}
return cfi;
};
Locations.prototype.cfiFromPercentage = function(value){
var percentage = (value > 1) ? value / 100 : value; // Normalize value to 0-1
var loc = Math.ceil(this.total * percentage);
return this.cfiFromLocation(loc);
};
Locations.prototype.load = function(locations){
this._locations = JSON.parse(locations);
this.total = this._locations.length-1;
return this._locations;
};
Locations.prototype.save = function(json){
return JSON.stringify(this._locations);
};
Locations.prototype.getCurrent = function(json){
return this._current;
};
Locations.prototype.setCurrent = function(curr){
var loc;
if(typeof curr == "string"){
this._currentCfi = curr;
} else if (typeof curr == "number") {
this._current = curr;
} else {
return;
}
if(this._locations.length === 0) {
return;
}
if(typeof curr == "string"){
loc = this.locationFromCfi(curr);
this._current = loc;
} else {
loc = curr;
}
this.trigger("changed", {
percentage: this.precentageFromLocation(loc)
});
};
Object.defineProperty(Locations.prototype, 'currentLocation', {
get: function () {
return this._current;
},
set: function (curr) {
this.setCurrent(curr);
}
});
RSVP.EventTarget.mixin(Locations.prototype);
module.exports = Locations;
},{"./core":8,"./epubcfi":9,"./queue":17,"rsvp":4}],13:[function(require,module,exports){
function Map(layout){
this.layout = layout;
};
Map.prototype.section = function(view) {
var ranges = this.findRanges(view);
var map = this.rangeListToCfiList(view, ranges);
return map;
};
Map.prototype.page = function(view, start, end) {
var root = view.document.body;
return this.rangePairToCfiPair(view.section, {
start: this.findStart(root, start, end),
end: this.findEnd(root, start, end)
});
};
Map.prototype.walk = function(root, func) {
//var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_TEXT, null, false);
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
acceptNode: function (node) {
if ( node.data.trim().length > 0 ) {
return NodeFilter.FILTER_ACCEPT;
} else {
return NodeFilter.FILTER_REJECT;
}
}
}, false);
var node;
var result;
while ((node = treeWalker.nextNode())) {
result = func(node);
if(result) break;
}
return result;
};
Map.prototype.findRanges = function(view){
var columns = [];
var count = this.layout.count(view);
var column = this.layout.column;
var gap = this.layout.gap;
var start, end;
for (var i = 0; i < count.pages; i++) {
start = (column + gap) * i;
end = (column * (i+1)) + (gap * i);
columns.push({
start: this.findStart(view.document.body, start, end),
end: this.findEnd(view.document.body, start, end)
});
}
return columns;
};
Map.prototype.findStart = function(root, start, end){
var stack = [root];
var $el;
var found;
var $prev = root;
while (stack.length) {
$el = stack.shift();
found = this.walk($el, function(node){
var left, right;
var elPos;
var elRange;
if(node.nodeType == Node.TEXT_NODE){
elRange = document.createRange();
elRange.selectNodeContents(node);
elPos = elRange.getBoundingClientRect();
} else {
elPos = node.getBoundingClientRect();
}
left = elPos.left;
right = elPos.right;
if( left >= start && left <= end ) {
return node;
} else if (right > start) {
return node;
} else {
$prev = node;
stack.push(node);
}
});
if(found) {
return this.findTextStartRange(found, start, end);
}
}
// Return last element
return this.findTextStartRange($prev, start, end);
};
Map.prototype.findEnd = function(root, start, end){
var stack = [root];
var $el;
var $prev = root;
var found;
while (stack.length) {
$el = stack.shift();
found = this.walk($el, function(node){
var left, right;
var elPos;
var elRange;
if(node.nodeType == Node.TEXT_NODE){
elRange = document.createRange();
elRange.selectNodeContents(node);
elPos = elRange.getBoundingClientRect();
} else {
elPos = node.getBoundingClientRect();
}
left = elPos.left;
right = elPos.right;
if(left > end && $prev) {
return $prev;
} else if(right > end) {
return node;
} else {
$prev = node;
stack.push(node);
}
});
if(found){
return this.findTextEndRange(found, start, end);
}
}
// end of chapter
return this.findTextEndRange($prev, start, end);
};
Map.prototype.findTextStartRange = function(node, start, end){
var ranges = this.splitTextNodeIntoRanges(node);
var prev;
var range;
var pos;
for (var i = 0; i < ranges.length; i++) {
range = ranges[i];
pos = range.getBoundingClientRect();
if( pos.left >= start ) {
return range;
}
prev = range;
}
return ranges[0];
};
Map.prototype.findTextEndRange = function(node, start, end){
var ranges = this.splitTextNodeIntoRanges(node);
var prev;
var range;
var pos;
for (var i = 0; i < ranges.length; i++) {
range = ranges[i];
pos = range.getBoundingClientRect();
if(pos.left > end && prev) {
return prev;
} else if(pos.right > end) {
return range;
}
prev = range;
}
// Ends before limit
return ranges[ranges.length-1];
};
Map.prototype.splitTextNodeIntoRanges = function(node, _splitter){
var ranges = [];
var textContent = node.textContent || "";
var text = textContent.trim();
var range;
var rect;
var list;
var doc = node.ownerDocument;
var splitter = _splitter || " ";
pos = text.indexOf(splitter);
if(pos === -1 || node.nodeType != Node.TEXT_NODE) {
range = doc.createRange();
range.selectNodeContents(node);
return [range];
}
range = doc.createRange();
range.setStart(node, 0);
range.setEnd(node, pos);
ranges.push(range);
range = false;
while ( pos != -1 ) {
pos = text.indexOf(splitter, pos + 1);
if(pos > 0) {
if(range) {
range.setEnd(node, pos);
ranges.push(range);
}
range = doc.createRange();
range.setStart(node, pos+1);
}
}
if(range) {
range.setEnd(node, text.length);
ranges.push(range);
}
return ranges;
};
Map.prototype.rangePairToCfiPair = function(section, rangePair){
var startRange = rangePair.start;
var endRange = rangePair.end;
startRange.collapse(true);
endRange.collapse(true);
startCfi = section.cfiFromRange(startRange);
endCfi = section.cfiFromRange(endRange);
return {
start: startCfi,
end: endCfi
};
};
Map.prototype.rangeListToCfiList = function(view, columns){
var map = [];
var rangePair, cifPair;
for (var i = 0; i < columns.length; i++) {
cifPair = this.rangePairToCfiPair(view.section, columns[i]);
map.push(cifPair);
}
return map;
};
module.exports = Map;
},{}],14:[function(require,module,exports){
var core = require('./core');
var Parser = require('./parser');
var RSVP = require('rsvp');
var URI = require('urijs');
function Navigation(_package, _request){
var navigation = this;
var parse = new Parser();
var request = _request || require('./request');
this.package = _package;
this.toc = [];
this.tocByHref = {};
this.tocById = {};
if(_package.navPath) {
this.navUrl = URI(_package.navPath).absoluteTo(_package.baseUrl).toString();
this.nav = {};
this.nav.load = function(_request){
var loading = new RSVP.defer();
var loaded = loading.promise;
request(navigation.navUrl, 'xml').then(function(xml){
navigation.toc = parse.nav(xml);
navigation.loaded(navigation.toc);
loading.resolve(navigation.toc);
});
return loaded;
};
}
if(_package.ncxPath) {
this.ncxUrl = URI(_package.ncxPath).absoluteTo(_package.baseUrl).toString();
this.ncx = {};
this.ncx.load = function(_request){
var loading = new RSVP.defer();
var loaded = loading.promise;
request(navigation.ncxUrl, 'xml').then(function(xml){
navigation.toc = parse.ncx(xml);
navigation.loaded(navigation.toc);
loading.resolve(navigation.toc);
});
return loaded;
};
}
};
// Load the navigation
Navigation.prototype.load = function(_request) {
var request = _request || require('./request');
var loading, loaded;
if(this.nav) {
loading = this.nav.load();
} else if(this.ncx) {
loading = this.ncx.load();
} else {
loaded = new RSVP.defer();
loaded.resolve([]);
loading = loaded.promise;
}
return loading;
};
Navigation.prototype.loaded = function(toc) {
var item;
for (var i = 0; i < toc.length; i++) {
item = toc[i];
this.tocByHref[item.href] = i;
this.tocById[item.id] = i;
}
};
// Get an item from the navigation
Navigation.prototype.get = function(target) {
var index;
if(!target) {
return this.toc;
}
if(target.indexOf("#") === 0) {
index = this.tocById[target.substring(1)];
} else if(target in this.tocByHref){
index = this.tocByHref[target];
}
return this.toc[index];
};
module.exports = Navigation;
},{"./core":8,"./parser":16,"./request":20,"rsvp":4,"urijs":5}],15:[function(require,module,exports){
var RSVP = require('rsvp');
var core = require('./core');
var Continuous = require('./continuous');
var Map = require('./map');
var Layout = require('./layout');
function Paginate(book, options) {
Continuous.apply(this, arguments);
this.settings = core.extend(this.settings || {}, {
width: 600,
height: 400,
axis: "horizontal",
forceSingle: false,
minSpreadWidth: 800, //-- overridden by spread: none (never) / both (always)
gap: "auto", //-- "auto" or int
overflow: "hidden",
infinite: false
});
core.extend(this.settings, options);
this.isForcedSingle = this.settings.forceSingle;
this.viewSettings = {
axis: this.settings.axis
};
this.start();
};
Paginate.prototype = Object.create(Continuous.prototype);
Paginate.prototype.constructor = Paginate;
Paginate.prototype.determineSpreads = function(cutoff){
if(this.isForcedSingle || !cutoff || this.bounds().width < cutoff) {
return 1; //-- Single Page
}else{
return 2; //-- Double Page
}
};
Paginate.prototype.forceSingle = function(bool){
if(bool === false) {
this.isForcedSingle = false;
// this.spreads = false;
} else {
this.isForcedSingle = true;
// this.spreads = this.determineSpreads(this.minSpreadWidth);
}
this.applyLayoutMethod();
};
/**
* Uses the settings to determine which Layout Method is needed
* Triggers events based on the method choosen
* Takes: Layout settings object
* Returns: String of appropriate for EPUBJS.Layout function
*/
// Paginate.prototype.determineLayout = function(settings){
// // Default is layout: reflowable & spread: auto
// var spreads = this.determineSpreads(this.settings.minSpreadWidth);
// console.log("spreads", spreads, this.settings.minSpreadWidth)
// var layoutMethod = spreads ? "ReflowableSpreads" : "Reflowable";
// var scroll = false;
//
// if(settings.layout === "pre-paginated") {
// layoutMethod = "Fixed";
// scroll = true;
// spreads = false;
// }
//
// if(settings.layout === "reflowable" && settings.spread === "none") {
// layoutMethod = "Reflowable";
// scroll = false;
// spreads = false;
// }
//
// if(settings.layout === "reflowable" && settings.spread === "both") {
// layoutMethod = "ReflowableSpreads";
// scroll = false;
// spreads = true;
// }
//
// this.spreads = spreads;
//
// return layoutMethod;
// };
Paginate.prototype.start = function(){
// On display
// this.layoutSettings = this.reconcileLayoutSettings(globalLayout, chapter.properties);
// this.layoutMethod = this.determineLayout(this.layoutSettings);
// this.layout = new EPUBJS.Layout[this.layoutMethod]();
//this.hooks.display.register(this.registerLayoutMethod.bind(this));
// this.hooks.display.register(this.reportLocation);
this.on('displayed', this.reportLocation.bind(this));
// this.hooks.content.register(this.adjustImages.bind(this));
this.currentPage = 0;
window.addEventListener('unload', function(e){
this.ignore = true;
this.destroy();
}.bind(this));
};
// EPUBJS.Rendition.prototype.createView = function(section) {
// var view = new EPUBJS.View(section, this.viewSettings);
// return view;
// };
Paginate.prototype.applyLayoutMethod = function() {
//var task = new RSVP.defer();
// this.spreads = this.determineSpreads(this.settings.minSpreadWidth);
this.layout = new Layout.Reflowable();
this.updateLayout();
// Set the look ahead offset for what is visible
this.map = new Map(this.layout);
// this.hooks.layout.register(this.layout.format.bind(this));
//task.resolve();
//return task.promise;
// return layout;
};
Paginate.prototype.updateLayout = function() {
this.spreads = this.determineSpreads(this.settings.minSpreadWidth);
this.layout.calculate(
this.stage.width,
this.stage.height,
this.settings.gap,
this.spreads
);
this.settings.offset = this.layout.delta;
};
Paginate.prototype.moveTo = function(offset){
var dist = Math.floor(offset.left / this.layout.delta) * this.layout.delta;
return this.check(0, dist+this.settings.offset).then(function(){
this.scrollBy(dist, 0);
}.bind(this));
};
Paginate.prototype.page = function(pg){
// this.currentPage = pg;
// this.renderer.infinite.scrollTo(this.currentPage * this.formated.pageWidth, 0);
//-- Return false if page is greater than the total
// return false;
};
Paginate.prototype.next = function(){
return this.q.enqueue(function(){
// console.log(this.container.scrollWidth, this.container.scrollLeft + this.container.offsetWidth + this.layout.delta)
if(this.container.scrollLeft +
this.container.offsetWidth +
this.layout.delta < this.container.scrollWidth) {
this.scrollBy(this.layout.delta, 0);
} else {
this.scrollTo(this.container.scrollWidth - this.layout.delta, 0);
}
this.reportLocation();
return this.check();
});
// return this.page(this.currentPage + 1);
};
Paginate.prototype.prev = function(){
return this.q.enqueue(function(){
this.scrollBy(-this.layout.delta, 0);
this.reportLocation();
return this.check();
});
// return this.page(this.currentPage - 1);
};
// Paginate.prototype.reportLocation = function(){
// return this.q.enqueue(function(){
// this.location = this.currentLocation();
// this.trigger("locationChanged", this.location);
// }.bind(this));
// };
Paginate.prototype.currentLocation = function(){
var visible = this.visible();
var startA, startB, endA, endB;
var pageLeft, pageRight;
var container = this.container.getBoundingClientRect();
if(visible.length === 1) {
startA = container.left - visible[0].position().left;
endA = startA + this.layout.spread;
return this.map.page(visible[0], startA, endA);
}
if(visible.length > 1) {
// Left Col
startA = container.left - visible[0].position().left;
endA = startA + this.layout.column;
// Right Col
startB = container.left + this.layout.spread - visible[visible.length-1].position().left;
endB = startB + this.layout.column;
pageLeft = this.map.page(visible[0], startA, endA);
pageRight = this.map.page(visible[visible.length-1], startB, endB);
return {
start: pageLeft.start,
end: pageRight.end
};
}
};
Paginate.prototype.resize = function(width, height){
// Clear the queue
this.q.clear();
this.stageSize(width, height);
this.updateLayout();
if(this.location) {
this.display(this.location.start);
}
this.trigger("resized", {
width: this.stage.width,
height: this.stage.height
});
};
Paginate.prototype.onResized = function(e) {
this.views.clear();
clearTimeout(this.resizeTimeout);
this.resizeTimeout = setTimeout(function(){
this.resize();
}.bind(this), 150);
};
Paginate.prototype.adjustImages = function(view) {
view.addStylesheetRules([
["img",
["max-width", (this.layout.spread) + "px"],
["max-height", (this.layout.height) + "px"]
]
]);
return new RSVP.Promise(function(resolve, reject){
// Wait to apply
setTimeout(function() {
resolve();
}, 1);
});
};
// Paginate.prototype.display = function(what){
// return this.display(what);
// };
module.exports = Paginate;
},{"./continuous":7,"./core":8,"./layout":11,"./map":13,"rsvp":4}],16:[function(require,module,exports){
var URI = require('urijs');
var core = require('./core');
var EpubCFI = require('./epubcfi');
function Parser(){};
Parser.prototype.container = function(containerXml){
//-- <rootfile full-path="OPS/package.opf" media-type="application/oebps-package+xml"/>
var rootfile, fullpath, folder, encoding;
if(!containerXml) {
console.error("Container File Not Found");
return;
}
rootfile = containerXml.querySelector("rootfile");
if(!rootfile) {
console.error("No RootFile Found");
return;
}
fullpath = rootfile.getAttribute('full-path');
folder = URI(fullpath).directory();
encoding = containerXml.xmlEncoding;
//-- Now that we have the path we can parse the contents
return {
'packagePath' : fullpath,
'basePath' : folder,
'encoding' : encoding
};
};
Parser.prototype.identifier = function(packageXml){
var metadataNode;
if(!packageXml) {
console.error("Package File Not Found");
return;
}
metadataNode = packageXml.querySelector("metadata");
if(!metadataNode) {
console.error("No Metadata Found");
return;
}
return this.getElementText(metadataNode, "identifier");
};
Parser.prototype.packageContents = function(packageXml){
var parse = this;
var metadataNode, manifestNode, spineNode;
var manifest, navPath, ncxPath, coverPath;
var spineNodeIndex;
var spine;
var spineIndexByURL;
var metadata;
if(!packageXml) {
console.error("Package File Not Found");
return;
}
metadataNode = packageXml.querySelector("metadata");
if(!metadataNode) {
console.error("No Metadata Found");
return;
}
manifestNode = packageXml.querySelector("manifest");
if(!manifestNode) {
console.error("No Manifest Found");
return;
}
spineNode = packageXml.querySelector("spine");
if(!spineNode) {
console.error("No Spine Found");
return;
}
manifest = parse.manifest(manifestNode);
navPath = parse.findNavPath(manifestNode);
ncxPath = parse.findNcxPath(manifestNode, spineNode);
coverPath = parse.findCoverPath(packageXml);
spineNodeIndex = Array.prototype.indexOf.call(spineNode.parentNode.childNodes, spineNode);
spine = parse.spine(spineNode, manifest);
metadata = parse.metadata(metadataNode);
metadata.direction = spineNode.getAttribute("page-progression-direction");
return {
'metadata' : metadata,
'spine' : spine,
'manifest' : manifest,
'navPath' : navPath,
'ncxPath' : ncxPath,
'coverPath': coverPath,
'spineNodeIndex' : spineNodeIndex
};
};
//-- Find TOC NAV
Parser.prototype.findNavPath = function(manifestNode){
// Find item with property 'nav'
// Should catch nav irregardless of order
var node = manifestNode.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']");
return node ? node.getAttribute('href') : false;
};
//-- Find TOC NCX: media-type="application/x-dtbncx+xml" href="toc.ncx"
Parser.prototype.findNcxPath = function(manifestNode, spineNode){
var node = manifestNode.querySelector("item[media-type='application/x-dtbncx+xml']");
var tocId;
// If we can't find the toc by media-type then try to look for id of the item in the spine attributes as
// according to http://www.idpf.org/epub/20/spec/OPF_2.0.1_draft.htm#Section2.4.1.2,
// "The item that describes the NCX must be referenced by the spine toc attribute."
if (!node) {
tocId = spineNode.getAttribute("toc");
if(tocId) {
node = manifestNode.querySelector("item[id='" + tocId + "']");
}
}
return node ? node.getAttribute('href') : false;
};
//-- Expanded to match Readium web components
Parser.prototype.metadata = function(xml){
var metadata = {},
p = this;
metadata.title = p.getElementText(xml, 'title');
metadata.creator = p.getElementText(xml, 'creator');
metadata.description = p.getElementText(xml, 'description');
metadata.pubdate = p.getElementText(xml, 'date');
metadata.publisher = p.getElementText(xml, 'publisher');
metadata.identifier = p.getElementText(xml, "identifier");
metadata.language = p.getElementText(xml, "language");
metadata.rights = p.getElementText(xml, "rights");
metadata.modified_date = p.querySelectorText(xml, "meta[property='dcterms:modified']");
metadata.layout = p.querySelectorText(xml, "meta[property='rendition:layout']");
metadata.orientation = p.querySelectorText(xml, "meta[property='rendition:orientation']");
metadata.spread = p.querySelectorText(xml, "meta[property='rendition:spread']");
// metadata.page_prog_dir = packageXml.querySelector("spine").getAttribute("page-progression-direction");
return metadata;
};
//-- Find Cover: <item properties="cover-image" id="ci" href="cover.svg" media-type="image/svg+xml" />
//-- Fallback for Epub 2.0
Parser.prototype.findCoverPath = function(packageXml){
var epubVersion = packageXml.querySelector('package').getAttribute('version');
if (epubVersion === '2.0') {
var metaCover = packageXml.querySelector('meta[name="cover"]');
if (metaCover) {
var coverId = metaCover.getAttribute('content');
var cover = packageXml.querySelector("item[id='" + coverId + "']");
return cover ? cover.getAttribute('href') : false;
}
else {
return false;
}
}
else {
var node = packageXml.querySelector("item[properties='cover-image']");
return node ? node.getAttribute('href') : false;
}
};
Parser.prototype.getElementText = function(xml, tag){
var found = xml.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/", tag),
el;
if(!found || found.length === 0) return '';
el = found[0];
if(el.childNodes.length){
return el.childNodes[0].nodeValue;
}
return '';
};
Parser.prototype.querySelectorText = function(xml, q){
var el = xml.querySelector(q);
if(el && el.childNodes.length){
return el.childNodes[0].nodeValue;
}
return '';
};
Parser.prototype.manifest = function(manifestXml){
var manifest = {};
//-- Turn items into an array
var selected = manifestXml.querySelectorAll("item"),
items = Array.prototype.slice.call(selected);
//-- Create an object with the id as key
items.forEach(function(item){
var id = item.getAttribute('id'),
href = item.getAttribute('href') || '',
type = item.getAttribute('media-type') || '',
properties = item.getAttribute('properties') || '';
manifest[id] = {
'href' : href,
// 'url' : href,
'type' : type,
'properties' : properties.length ? properties.split(' ') : []
};
});
return manifest;
};
Parser.prototype.spine = function(spineXml, manifest){
var spine = [];
var selected = spineXml.getElementsByTagName("itemref"),
items = Array.prototype.slice.call(selected);
var epubcfi = new EpubCFI();
//-- Add to array to mantain ordering and cross reference with manifest
items.forEach(function(item, index){
var idref = item.getAttribute('idref');
// var cfiBase = epubcfi.generateChapterComponent(spineNodeIndex, index, Id);
var props = item.getAttribute('properties') || '';
var propArray = props.length ? props.split(' ') : [];
// var manifestProps = manifest[Id].properties;
// var manifestPropArray = manifestProps.length ? manifestProps.split(' ') : [];
var itemref = {
'idref' : idref,
'linear' : item.getAttribute('linear') || '',
'properties' : propArray,
// 'href' : manifest[Id].href,
// 'url' : manifest[Id].url,
'index' : index
// 'cfiBase' : cfiBase
};
spine.push(itemref);
});
return spine;
};
Parser.prototype.querySelectorByType = function(html, element, type){
var query = html.querySelector(element+'[*|type="'+type+'"]');
// Handle IE not supporting namespaced epub:type in querySelector
if(query === null || query.length === 0) {
query = html.querySelectorAll(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.nav = function(navHtml, spineIndexByURL, bookSpine){
var navElement = this.querySelectorByType(navHtml, "nav", "toc");
var navItems = navElement ? navElement.querySelectorAll("ol li") : [];
var length = navItems.length;
var i;
var toc = {};
var list = [];
var item, parent;
if(!navItems || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.navItem(navItems[i], spineIndexByURL, bookSpine);
toc[item.id] = item;
if(!item.parent) {
list.push(item);
} else {
parent = toc[item.parent];
parent.subitems.push(item);
}
}
return list;
};
Parser.prototype.navItem = function(item, spineIndexByURL, bookSpine){
var id = item.getAttribute('id') || false,
content = item.querySelector("a, span"),
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,
"label": text,
"subitems" : subitems,
"parent" : parent
};
};
Parser.prototype.toc = function(tocXml, spineIndexByURL, bookSpine){
var navPoints = tocXml.querySelectorAll("navMap navPoint");
var length = navPoints.length;
var i;
var toc = {};
var list = [];
var item, parent;
if(!navPoints || length === 0) return list;
for (i = 0; i < length; ++i) {
item = this.tocItem(navPoints[i], spineIndexByURL, bookSpine);
toc[item.id] = item;
if(!item.parent) {
list.push(item);
} else {
parent = toc[item.parent];
parent.subitems.push(item);
}
}
return list;
};
Parser.prototype.tocItem = function(item, spineIndexByURL, bookSpine){
var id = item.getAttribute('id') || false,
content = item.querySelector("content"),
src = content.getAttribute('src'),
navLabel = item.querySelector("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,
"href": src,
"label": text,
"subitems" : subitems,
"parent" : parent
};
};
Parser.prototype.pageList = function(navHtml, spineIndexByURL, bookSpine){
var navElement = this.querySelectorByType(navHtml, "nav", "page-list");
var navItems = navElement ? navElement.querySelectorAll("ol 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"),
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;
},{"./core":8,"./epubcfi":9,"urijs":5}],17:[function(require,module,exports){
var RSVP = require('rsvp');
var core = require('./core');
function Queue(_context){
this._q = [];
this.context = _context;
this.tick = core.requestAnimationFrame;
this.running = false;
this.paused = false;
};
// Add an item to the queue
Queue.prototype.enqueue = function() {
var deferred, promise;
var queued;
var task = [].shift.call(arguments);
var args = arguments;
// Handle single args without context
// if(args && !Array.isArray(args)) {
// args = [args];
// }
if(!task) {
return console.error("No Task Provided");
}
if(typeof task === "function"){
deferred = new RSVP.defer();
promise = deferred.promise;
queued = {
"task" : task,
"args" : args,
//"context" : context,
"deferred" : deferred,
"promise" : promise
};
} else {
// Task is a promise
queued = {
"promise" : task
};
}
this._q.push(queued);
// Wait to start queue flush
if (this.paused == false && !this.running) {
// setTimeout(this.flush.bind(this), 0);
// this.tick.call(window, this.run.bind(this));
this.run();
}
return queued.promise;
};
// Run one item
Queue.prototype.dequeue = function(){
var inwait, task, result;
if(this._q.length) {
inwait = this._q.shift();
task = inwait.task;
if(task){
// console.log(task)
result = task.apply(this.context, inwait.args);
if(result && typeof result["then"] === "function") {
// Task is a function that returns a promise
return result.then(function(){
inwait.deferred.resolve.apply(this.context, arguments);
}.bind(this));
} else {
// Task resolves immediately
inwait.deferred.resolve.apply(this.context, result);
return inwait.promise;
}
} else if(inwait.promise) {
// Task is a promise
return inwait.promise;
}
} else {
inwait = new RSVP.defer();
inwait.deferred.resolve();
return inwait.promise;
}
};
// Run All Immediately
Queue.prototype.dump = function(){
while(this._q.length) {
this.dequeue();
}
};
// Run all sequentially, at convince
Queue.prototype.run = function(){
if(!this.running){
this.running = true;
this.defered = new RSVP.defer();
}
this.tick.call(window, function() {
if(this._q.length) {
this.dequeue()
.then(function(){
this.run();
}.bind(this));
} else {
this.defered.resolve();
this.running = undefined;
}
}.bind(this));
// Unpause
if(this.paused == true) {
this.paused = false;
}
return this.defered.promise;
};
// Flush all, as quickly as possible
Queue.prototype.flush = function(){
if(this.running){
return this.running;
}
if(this._q.length) {
this.running = this.dequeue()
.then(function(){
this.running = undefined;
return this.flush();
}.bind(this));
return this.running;
}
};
// Clear all items in wait
Queue.prototype.clear = function(){
this._q = [];
this.running = false;
};
Queue.prototype.length = function(){
return this._q.length;
};
Queue.prototype.pause = function(){
this.paused = true;
};
// Create a new task from a callback
function Task(task, args, context){
return function(){
var toApply = arguments || [];
return new RSVP.Promise(function(resolve, reject) {
var callback = function(value){
resolve(value);
};
// Add the callback to the arguments list
toApply.push(callback);
// Apply all arguments to the functions
task.apply(this, toApply);
}.bind(this));
};
};
module.exports = Queue;
},{"./core":8,"rsvp":4}],18:[function(require,module,exports){
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
var replace = require('./replacements');
var Hook = require('./hook');
var EpubCFI = require('./epubcfi');
var Queue = require('./queue');
var View = require('./view');
var Views = require('./views');
var Layout = require('./layout');
var Map = require('./map');
function Rendition(book, options) {
this.settings = core.extend(this.settings || {}, {
infinite: true,
hidden: false,
width: false,
height: null,
layoutOveride : null, // Default: { spread: 'reflowable', layout: 'auto', orientation: 'auto'},
axis: "vertical"
});
core.extend(this.settings, options);
this.viewSettings = {};
this.book = book;
this.views = null;
//-- 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);
this.hooks.show = new Hook(this);
this.hooks.content.register(replace.links.bind(this));
this.hooks.content.register(this.passViewEvents.bind(this));
// this.hooks.display.register(this.afterDisplay.bind(this));
this.epubcfi = new EpubCFI();
this.q = new Queue(this);
this.q.enqueue(this.book.opened);
this.q.enqueue(this.parseLayoutProperties);
if(this.book.archive) {
this.replacements();
}
};
/**
* Creates an element to render to.
* Resizes to passed width and height or to the elements size
*/
Rendition.prototype.initialize = function(_options){
var options = _options || {};
var height = options.height;// !== false ? options.height : "100%";
var width = options.width;// !== false ? options.width : "100%";
var hidden = options.hidden || false;
var container;
var wrapper;
if(options.height && core.isNumber(options.height)) {
height = options.height + "px";
}
if(options.width && core.isNumber(options.width)) {
width = options.width + "px";
}
// Create new container element
container = document.createElement("div");
container.id = "epubjs-container:" + core.uuid();
container.classList.add("epub-container");
// Style Element
container.style.fontSize = "0";
container.style.wordSpacing = "0";
container.style.lineHeight = "0";
container.style.verticalAlign = "top";
if(this.settings.axis === "horizontal") {
container.style.whiteSpace = "nowrap";
}
if(width){
container.style.width = width;
}
if(height){
container.style.height = height;
}
container.style.overflow = this.settings.overflow;
return container;
};
Rendition.wrap = function(container) {
var wrapper = document.createElement("div");
wrapper.style.visibility = "hidden";
wrapper.style.overflow = "hidden";
wrapper.style.width = "0";
wrapper.style.height = "0";
wrapper.appendChild(container);
return wrapper;
};
// Call to attach the container to an element in the dom
// Container must be attached before rendering can begin
Rendition.prototype.attachTo = function(_element){
var bounds;
this.container = this.initialize({
"width" : this.settings.width,
"height" : this.settings.height
});
if(core.isElement(_element)) {
this.element = _element;
} else if (typeof _element === "string") {
this.element = document.getElementById(_element);
}
if(!this.element){
console.error("Not an Element");
return;
}
if(this.settings.hidden) {
this.wrapper = this.wrap(this.container);
this.element.appendChild(this.wrapper);
} else {
this.element.appendChild(this.container);
}
this.views = new Views(this.container);
// Attach Listeners
this.attachListeners();
// Calculate Stage Size
this.stageSize();
// Add Layout method
this.applyLayoutMethod();
// Trigger Attached
this.trigger("attached");
// Start processing queue
// this.q.run();
};
Rendition.prototype.attachListeners = function(){
// Listen to window for resize event if width or height is set to 100%
if(!core.isNumber(this.settings.width) ||
!core.isNumber(this.settings.height) ) {
window.addEventListener("resize", this.onResized.bind(this), false);
}
};
Rendition.prototype.bounds = function() {
return this.container.getBoundingClientRect();
};
Rendition.prototype.display = function(target){
return this.q.enqueue(this._display, target);
};
Rendition.prototype._display = function(target){
var displaying = new RSVP.defer();
var displayed = displaying.promise;
var section;
var view;
var offset;
var fragment;
var cfi = this.epubcfi.isCfiString(target);
var visible;
section = this.book.spine.get(target);
if(!section){
displaying.reject(new Error("No Section Found"));
return displayed;
}
// Check to make sure the section we want isn't already shown
visible = this.views.find(section);
if(visible) {
offset = view.locationOf(target);
displayed = this.moveTo(offset)
.then(function(){
return this.check();
});
} else {
// Hide all current views
this.views.hide();
// Create a new view
// view = new View(section, this.viewSettings);
view = this.createView(section);
// This will clear all previous views
displayed = this.fill(view)
.then(function(){
// Parse the target fragment
if(typeof target === "string" &&
target.indexOf("#") > -1) {
fragment = target.substring(target.indexOf("#")+1);
}
// Move to correct place within the section, if needed
if(cfi || fragment) {
offset = view.locationOf(target);
return this.moveTo(offset);
}
if(typeof this.check === 'function') {
return this.check();
}
}.bind(this))
.then(function(){
return this.hooks.display.trigger(view);
}.bind(this))
.then(function(){
this.views.show();
}.bind(this));
}
displayed.then(function(){
this.trigger("displayed", section);
}.bind(this));
return displayed;
};
// Takes a cfi, fragment or page?
Rendition.prototype.moveTo = function(offset){
this.scrollBy(offset.left, offset.top);
};
Rendition.prototype.render = function(view, show) {
view.create();
view.onLayout = this.layout.format.bind(this.layout);
// Fit to size of the container, apply padding
this.resizeView(view);
// Render Chain
return view.render(this.book.request)
.then(function(){
return this.hooks.content.trigger(view, this);
}.bind(this))
.then(function(){
return this.hooks.layout.trigger(view, this);
}.bind(this))
.then(function(){
return view.display();
}.bind(this))
.then(function(){
return this.hooks.render.trigger(view, this);
}.bind(this))
.then(function(){
if(show !== false && this.views.hidden === false) {
this.q.enqueue(function(view){
view.show();
}, view);
}
// this.map = new Map(view, this.layout);
this.hooks.show.trigger(view, this);
this.trigger("rendered", view.section);
}.bind(this))
.catch(function(e){
this.trigger("loaderror", e);
}.bind(this));
};
Rendition.prototype.afterDisplayed = function(view){
this.trigger("added", view.section);
};
Rendition.prototype.fill = function(view){
this.views.clear();
this.views.append(view);
// view.on("shown", this.afterDisplayed.bind(this));
view.onDisplayed = this.afterDisplayed.bind(this);
return this.render(view);
};
Rendition.prototype.resizeView = function(view) {
if(this.globalLayoutProperties.layout === "pre-paginated") {
view.lock("both", this.stage.width, this.stage.height);
} else {
view.lock("width", this.stage.width, this.stage.height);
}
};
Rendition.prototype.stageSize = function(_width, _height){
var bounds;
var width = _width || this.settings.width;
var height = _height || this.settings.height;
// If width or height are set to false, inherit them from containing element
if(width === false) {
bounds = this.element.getBoundingClientRect();
if(bounds.width) {
width = bounds.width;
this.container.style.width = bounds.width + "px";
}
}
if(height === false) {
bounds = bounds || this.element.getBoundingClientRect();
if(bounds.height) {
height = bounds.height;
this.container.style.height = bounds.height + "px";
}
}
if(width && !core.isNumber(width)) {
bounds = this.container.getBoundingClientRect();
width = bounds.width;
//height = bounds.height;
}
if(height && !core.isNumber(height)) {
bounds = bounds || this.container.getBoundingClientRect();
//width = bounds.width;
height = bounds.height;
}
this.containerStyles = window.getComputedStyle(this.container);
this.containerPadding = {
left: parseFloat(this.containerStyles["padding-left"]) || 0,
right: parseFloat(this.containerStyles["padding-right"]) || 0,
top: parseFloat(this.containerStyles["padding-top"]) || 0,
bottom: parseFloat(this.containerStyles["padding-bottom"]) || 0
};
this.stage = {
width: width -
this.containerPadding.left -
this.containerPadding.right,
height: height -
this.containerPadding.top -
this.containerPadding.bottom
};
return this.stage;
};
Rendition.prototype.applyLayoutMethod = function() {
this.layout = new Layout.Scroll();
this.updateLayout();
this.map = new Map(this.layout);
};
Rendition.prototype.updateLayout = function() {
this.layout.calculate(this.stage.width, this.stage.height);
};
Rendition.prototype.resize = function(width, height){
this.stageSize(width, height);
this.updateLayout();
this.views.each(this.resizeView.bind(this));
this.trigger("resized", {
width: this.stage.width,
height: this.stage.height
});
};
Rendition.prototype.onResized = function(e) {
this.resize();
};
Rendition.prototype.createView = function(section) {
// Transfer the existing hooks
section.hooks.serialize.register(this.hooks.serialize.list());
return new View(section, this.viewSettings);
};
Rendition.prototype.next = function(){
return this.q.enqueue(function(){
var next;
var view;
if(!this.views.length) return;
next = this.views.last().section.next();
if(next) {
view = this.createView(next);
return this.fill(view);
}
});
};
Rendition.prototype.prev = function(){
return this.q.enqueue(function(){
var prev;
var view;
if(!this.views.length) return;
prev = this.views.first().section.prev();
if(prev) {
view = this.createView(prev);
return this.fill(view);
}
});
};
//-- http://www.idpf.org/epub/fxl/
Rendition.prototype.parseLayoutProperties = function(_metadata){
var metadata = _metadata || this.book.package.metadata;
var layout = (this.layoutOveride && this.layoutOveride.layout) || metadata.layout || "reflowable";
var spread = (this.layoutOveride && this.layoutOveride.spread) || metadata.spread || "auto";
var orientation = (this.layoutOveride && this.layoutOveride.orientation) || metadata.orientation || "auto";
this.globalLayoutProperties = {
layout : layout,
spread : spread,
orientation : orientation
};
return this.globalLayoutProperties;
};
Rendition.prototype.current = function(){
var visible = this.visible();
if(visible.length){
// Current is the last visible view
return visible[visible.length-1];
}
return null;
};
Rendition.prototype.isVisible = function(view, offsetPrev, offsetNext, _container){
var position = view.position();
var container = _container || this.container.getBoundingClientRect();
if(this.settings.axis === "horizontal" &&
position.right > container.left - offsetPrev &&
position.left < container.right + offsetNext) {
return true;
} else if(this.settings.axis === "vertical" &&
position.bottom > container.top - offsetPrev &&
position.top < container.bottom + offsetNext) {
return true;
}
return false;
};
Rendition.prototype.visible = function(){
var container = this.bounds();
var displayedViews = this.views.displayed();
var visible = [];
var isVisible;
var view;
for (var i = 0; i < displayedViews.length; i++) {
view = displayedViews[i];
isVisible = this.isVisible(view, 0, 0, container);
if(isVisible === true) {
visible.push(view);
}
}
return visible;
};
Rendition.prototype.bounds = function(func) {
var bounds;
if(!this.settings.height) {
bounds = core.windowBounds();
} else {
bounds = this.container.getBoundingClientRect();
}
return bounds;
};
Rendition.prototype.destroy = function(){
// Clear the queue
this.q.clear();
this.views.clear();
clearTimeout(this.trimTimeout);
if(this.settings.hidden) {
this.element.removeChild(this.wrapper);
} else {
this.element.removeChild(this.container);
}
};
Rendition.prototype.reportLocation = function(){
return this.q.enqueue(function(){
this.location = this.currentLocation();
this.trigger("locationChanged", this.location);
}.bind(this));
};
Rendition.prototype.currentLocation = function(){
var view;
var start, end;
if(this.views.length) {
view = this.views.first();
// start = container.left - view.position().left;
// end = start + this.layout.spread;
return this.map.page(view);
}
};
Rendition.prototype.scrollBy = function(x, y, silent){
if(silent) {
this.ignore = true;
}
if(this.settings.height) {
if(x) this.container.scrollLeft += x;
if(y) this.container.scrollTop += y;
} else {
window.scrollBy(x,y);
}
// console.log("scrollBy", x, y);
this.scrolled = true;
};
Rendition.prototype.scrollTo = function(x, y, silent){
if(silent) {
this.ignore = true;
}
if(this.settings.height) {
this.container.scrollLeft = x;
this.container.scrollTop = y;
} else {
window.scrollTo(x,y);
}
// console.log("scrollTo", x, y);
this.scrolled = true;
// if(this.container.scrollLeft != x){
// setTimeout(function() {
// this.scrollTo(x, y, silent);
// }.bind(this), 10);
// return;
// };
};
Rendition.prototype.passViewEvents = function(view){
view.listenedEvents.forEach(function(e){
view.on(e, this.triggerViewEvent.bind(this));
}.bind(this));
};
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);
module.exports = Rendition;
},{"./core":8,"./epubcfi":9,"./hook":10,"./layout":11,"./map":13,"./queue":17,"./replacements":19,"./view":24,"./views":25,"rsvp":4,"urijs":5}],19:[function(require,module,exports){
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 linkUri = URI(href);
var absolute = linkUri.absoluteTo(view.section.url);
var relative = absolute.relativeTo(this.book.baseUrl).toString();
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;
}
*/
if(linkUri.fragment()) {
// do nothing with fragment yet
} else {
link.onclick = function(){
renderer.display(relative);
return false;
};
}
}
};
for (var i = 0; i < links.length; i++) {
replaceLinks(links[i]);
}
};
function substitute(content, urls, replacements) {
urls.forEach(function(url, i){
content = content.replace(new RegExp(url, 'g'), replacements[i]);
});
return content;
}
module.exports = {
'base': base,
'links': links,
'substitute': substitute
};
},{"./core":8,"urijs":5}],20:[function(require,module,exports){
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
function request(url, type, withCredentials, headers) {
var supportsURL = window.URL;
var BLOB_RESPONSE = supportsURL ? "blob" : "arraybuffer";
var uri;
var deferred = new RSVP.defer();
var xhr = new XMLHttpRequest();
//-- Check from PDF.js:
// https://github.com/mozilla/pdf.js/blob/master/web/compatibility.js
var xhrPrototype = XMLHttpRequest.prototype;
var header;
if (!('overrideMimeType' in xhrPrototype)) {
// IE10 might have response, but not overrideMimeType
Object.defineProperty(xhrPrototype, 'overrideMimeType', {
value: function xmlHttpRequestOverrideMimeType(mimeType) {}
});
}
if(withCredentials) {
xhr.withCredentials = true;
}
xhr.open("GET", url, true);
for(header in headers) {
xhr.setRequestHeader(header, headers[header]);
}
xhr.onreadystatechange = handler;
// If type isn't set, determine it from the file extension
if(!type) {
uri = URI(url);
type = uri.suffix();
}
if(type == 'blob'){
xhr.responseType = BLOB_RESPONSE;
}
if(type == "json") {
xhr.setRequestHeader("Accept", "application/json");
}
if(core.isXml(type)) {
xhr.responseType = "document";
xhr.overrideMimeType('text/xml'); // for OPF parsing
}
if(type == 'xhtml') {
xhr.responseType = "document";
}
if(type == 'html' || type == 'htm') {
xhr.responseType = "document";
}
if(type == "binary") {
xhr.responseType = "arraybuffer";
}
xhr.send();
function handler() {
if (this.readyState === this.DONE) {
if (this.status === 200 || this.responseXML ) { //-- Firefox is reporting 0 for blob urls
var r;
if((this.responseType == '' || this.responseType == 'document')
&& this.responseXML){
r = this.responseXML;
} else
if(core.isXml(type)){
// If this.responseXML wasn't set, try to parse using a DOMParser from text
r = new DOMParser().parseFromString(this.response, "text/xml");
}else
if(type == 'xhtml'){
r = new DOMParser().parseFromString(this.response, "application/xhtml+xml");
}else
if(type == 'html' || type == 'htm'){
r = new DOMParser().parseFromString(this.response, "text/html");
}else
if(type == 'json'){
r = JSON.parse(this.response);
}else
if(type == 'blob'){
if(supportsURL) {
r = this.response;
} else {
//-- Safari doesn't support responseType blob, so create a blob from arraybuffer
r = new Blob([this.response]);
}
}else{
r = this.response;
}
deferred.resolve(r);
} else {
deferred.reject({
status: this.status,
message : this.response,
stack : new Error().stack
});
}
}
}
return deferred.promise;
};
module.exports = request;
},{"./core":8,"rsvp":4,"urijs":5}],21:[function(require,module,exports){
var RSVP = require('rsvp');
var core = require('./core');
var EpubCFI = require('./epubcfi');
var Hook = require('./hook');
var replacements = require('./replacements');
function Section(item, hooks){
this.idref = item.idref;
this.linear = item.linear;
this.properties = item.properties;
this.index = item.index;
this.href = item.href;
this.url = item.url;
this.next = item.next;
this.prev = item.prev;
this.epubcfi = new EpubCFI();
this.cfiBase = item.cfiBase;
this.hooks = {};
this.hooks.serialize = new Hook(this);
this.hooks.content = new Hook(this);
// Register replacements
this.hooks.content.register(replacements.base);
};
Section.prototype.load = function(_request){
var request = _request || this.request || require('./request');
var loading = new RSVP.defer();
var loaded = loading.promise;
if(this.contents) {
loading.resolve(this.contents);
} else {
request(this.url)
.then(function(xml){
var base;
var directory = core.folder(this.url);
this.document = xml;
this.contents = xml.documentElement;
return this.hooks.content.trigger(this.document, this);
}.bind(this))
.then(function(){
loading.resolve(this.contents);
}.bind(this))
.catch(function(error){
loading.reject(error);
});
}
return loaded;
};
Section.prototype.base = function(_document){
var task = new RSVP.defer();
var base = _document.createElement("base"); // TODO: check if exists
var head;
base.setAttribute("href", this.url);
if(_document) {
head = _document.querySelector("head");
}
if(head) {
head.insertBefore(base, head.firstChild);
task.resolve();
} else {
task.reject(new Error("No head to insert into"));
}
return task.promise;
};
Section.prototype.beforeSectionLoad = function(){
// Stub for a hook - replace me for now
};
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){
var serializer = new XMLSerializer();
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);
});
return rendered;
};
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
*/
Section.prototype.reconcileLayoutSettings = function(global){
//-- Get the global defaults
var settings = {
layout : global.layout,
spread : global.spread,
orientation : global.orientation
};
//-- Get the chapter's display type
this.properties.forEach(function(prop){
var rendition = prop.replace("rendition:", '');
var split = rendition.indexOf("-");
var property, value;
if(split != -1){
property = rendition.slice(0, split);
value = rendition.slice(split+1);
settings[property] = value;
}
});
return settings;
};
Section.prototype.cfiFromRange = function(_range) {
return this.epubcfi.generateCfiFromRange(_range, this.cfiBase);
};
Section.prototype.cfiFromElement = function(el) {
return this.epubcfi.generateCfiFromElement(el, this.cfiBase);
};
module.exports = Section;
},{"./core":8,"./epubcfi":9,"./hook":10,"./replacements":19,"./request":20,"rsvp":4}],22:[function(require,module,exports){
var RSVP = require('rsvp');
var core = require('./core');
var EpubCFI = require('./epubcfi');
var Section = require('./section');
function Spine(_request){
this.request = _request;
this.spineItems = [];
this.spineByHref = {};
this.spineById = {};
};
Spine.prototype.load = function(_package) {
this.items = _package.spine;
this.manifest = _package.manifest;
this.spineNodeIndex = _package.spineNodeIndex;
this.baseUrl = _package.baseUrl || '';
this.length = this.items.length;
this.epubcfi = new EpubCFI();
this.items.forEach(function(item, index){
var href, url;
var manifestItem = this.manifest[item.idref];
var spineItem;
item.cfiBase = this.epubcfi.generateChapterComponent(this.spineNodeIndex, item.index, item.idref);
if(manifestItem) {
item.href = manifestItem.href;
item.url = this.baseUrl + item.href;
if(manifestItem.properties.length){
item.properties.push.apply(item.properties, manifestItem.properties);
}
}
// 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.append(spineItem);
}.bind(this));
};
// book.spine.get();
// book.spine.get(1);
// book.spine.get("chap1.html");
// book.spine.get("#id1234");
Spine.prototype.get = function(target) {
var index = 0;
if(this.epubcfi.isCfiString(target)) {
cfi = this.epubcfi.parse(target);
index = cfi.spinePos;
} else if(target && (typeof target === "number" || isNaN(target) === false)){
index = target;
} else if(target && target.indexOf("#") === 0) {
index = this.spineById[target.substring(1)];
} else if(target) {
// Remove fragments
target = target.split("#")[0];
index = this.spineByHref[target];
}
return this.spineItems[index] || null;
};
Spine.prototype.append = function(section) {
var index = this.spineItems.length;
section.index = index;
this.spineItems.push(section);
this.spineByHref[section.href] = index;
this.spineById[section.idref] = index;
return index;
};
Spine.prototype.prepend = function(section) {
var index = this.spineItems.unshift(section);
this.spineByHref[section.href] = 0;
this.spineById[section.idref] = 0;
// Re-index
this.spineItems.forEach(function(item, index){
item.index = index;
});
return 0;
};
Spine.prototype.insert = function(section, index) {
};
Spine.prototype.remove = function(section) {
var index = this.spineItems.indexOf(section);
if(index > -1) {
delete this.spineByHref[section.href];
delete this.spineById[section.idref];
return this.spineItems.splice(index, 1);
}
};
Spine.prototype.each = function() {
return this.spineItems.forEach.apply(this.spineItems, arguments);
};
module.exports = Spine;
},{"./core":8,"./epubcfi":9,"./section":21,"rsvp":4}],23:[function(require,module,exports){
var RSVP = require('rsvp');
var URI = require('urijs');
var core = require('./core');
var request = require('./request');
var mime = require('../libs/mime/mime');
function Unarchive() {
this.checkRequirements();
this.urlCache = {};
}
Unarchive.prototype.checkRequirements = function(callback){
try {
if (typeof JSZip !== 'undefined') {
this.zip = new JSZip();
} else {
JSZip = require('jszip');
this.zip = new JSZip();
}
} catch (e) {
console.error("JSZip lib not loaded");
}
};
Unarchive.prototype.open = function(zipUrl){
if (zipUrl instanceof ArrayBuffer) {
return new RSVP.Promise(function(resolve, reject) {
this.zip = new JSZip(zipUrl);
resolve(this.zip);
});
} else {
return request(zipUrl, "binary")
.then(function(data){
this.zip = new JSZip(data);
return this.zip;
}.bind(this));
}
};
Unarchive.prototype.request = function(url, type){
var deferred = new RSVP.defer();
var response;
var r;
// If type isn't set, determine it from the file extension
if(!type) {
uri = URI(url);
type = uri.suffix();
}
if(type == 'blob'){
response = this.getBlob(url);
} else {
response = this.getText(url);
}
if (response) {
r = this.handleResponse(response, type);
deferred.resolve(r);
} else {
deferred.reject({
message : "File not found in the epub: " + url,
stack : new Error().stack
});
}
return deferred.promise;
};
Unarchive.prototype.handleResponse = function(response, type){
var r;
if(type == "json") {
r = JSON.parse(response);
}
else
if(core.isXml(type)) {
r = new DOMParser().parseFromString(response, "text/xml");
}
else
if(type == 'xhtml') {
r = new DOMParser().parseFromString(response, "application/xhtml+xml");
}
else
if(type == 'html' || type == 'htm') {
r = new DOMParser().parseFromString(response, "text/html");
} else {
r = response;
}
return r;
};
Unarchive.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);
return new Blob([entry.asUint8Array()], {type : mimeType});
}
};
Unarchive.prototype.getText = function(url, encoding){
var decodededUrl = window.decodeURIComponent(url.substr(1)); // Remove first slash
var entry = this.zip.file(decodededUrl);
if(entry) {
return entry.asText();
}
};
Unarchive.prototype.createUrl = function(url, mime){
var deferred = new RSVP.defer();
var _URL = window.URL || window.webkitURL || window.mozURL;
var tempUrl;
var blob;
if(url in this.urlCache) {
deferred.resolve(this.urlCache[url]);
return deferred.promise;
}
blob = this.getBlob(url);
if (blob) {
tempUrl = _URL.createObjectURL(blob);
deferred.resolve(tempUrl);
this.urlCache[url] = tempUrl;
} else {
deferred.reject({
message : "File not found in the epub: " + url,
stack : new Error().stack
});
}
return deferred.promise;
};
Unarchive.prototype.revokeUrl = function(url){
var _URL = window.URL || window.webkitURL || window.mozURL;
var fromCache = this.urlCache[url];
if(fromCache) _URL.revokeObjectURL(fromCache);
};
module.exports = Unarchive;
},{"../libs/mime/mime":1,"./core":8,"./request":20,"jszip":"jszip","rsvp":4,"urijs":5}],24:[function(require,module,exports){
var RSVP = require('rsvp');
var core = require('./core');
var EpubCFI = require('./epubcfi');
function View(section, options) {
this.settings = options || {};
this.id = "epubjs-view:" + core.uuid();
this.section = section;
this.index = section.index;
this.element = document.createElement('div');
this.element.classList.add("epub-view");
// this.element.style.minHeight = "100px";
this.element.style.height = "0px";
this.element.style.width = "0px";
this.element.style.overflow = "hidden";
this.added = false;
this.displayed = false;
this.rendered = false;
//this.width = 0;
//this.height = 0;
// Blank Cfi for Parsing
this.epubcfi = new EpubCFI();
if(this.settings.axis && this.settings.axis == "horizontal"){
this.element.style.display = "inline-block";
} else {
this.element.style.display = "block";
}
// Dom events to listen for
this.listenedEvents = ["keydown", "keyup", "keypressed", "mouseup", "mousedown", "click", "touchend", "touchstart"];
};
View.prototype.create = function() {
if(this.iframe) {
return this.iframe;
}
this.iframe = document.createElement('iframe');
this.iframe.id = this.id;
this.iframe.scrolling = "no"; // Might need to be removed: breaks ios width calculations
this.iframe.style.overflow = "hidden";
this.iframe.seamless = "seamless";
// Back up if seamless isn't supported
this.iframe.style.border = "none";
this.resizing = true;
// this.iframe.style.display = "none";
this.element.style.visibility = "hidden";
this.iframe.style.visibility = "hidden";
this.iframe.style.width = "0";
this.iframe.style.height = "0";
this._width = 0;
this._height = 0;
this.element.appendChild(this.iframe);
this.added = true;
this.elementBounds = core.bounds(this.element);
// if(width || height){
// this.resize(width, height);
// } else if(this.width && this.height){
// this.resize(this.width, this.height);
// } else {
// this.iframeBounds = core.bounds(this.iframe);
// }
// Firefox has trouble with baseURI and srcdoc
// Disabled for now
/*
if(!!("srcdoc" in this.iframe)) {
this.supportsSrcdoc = true;
} else {
this.supportsSrcdoc = false;
}
*/
this.supportsSrcdoc = false;
return this.iframe;
};
View.prototype.lock = function(what, width, height) {
var elBorders = core.borders(this.element);
var iframeBorders;
if(this.iframe) {
iframeBorders = core.borders(this.iframe);
} else {
iframeBorders = {width: 0, height: 0};
}
if(what == "width" && core.isNumber(width)){
this.lockedWidth = width - elBorders.width - iframeBorders.width;
this.resize(this.lockedWidth, width); // width keeps ratio correct
}
if(what == "height" && core.isNumber(height)){
this.lockedHeight = height - elBorders.height - iframeBorders.height;
this.resize(width, this.lockedHeight);
}
if(what === "both" &&
core.isNumber(width) &&
core.isNumber(height)){
this.lockedWidth = width - elBorders.width - iframeBorders.width;
this.lockedHeight = height - elBorders.height - iframeBorders.height;
this.resize(this.lockedWidth, this.lockedHeight);
}
if(this.displayed && this.iframe) {
this.layout();
this.expand();
}
};
View.prototype.expand = function(force) {
var width = this.lockedWidth;
var height = this.lockedHeight;
var textWidth, textHeight;
// console.log("expanding a")
if(!this.iframe || this._expanding) return;
this._expanding = true;
// Expand Horizontally
if(height && !width) {
// Get the width of the text
textWidth = this.textWidth();
// Check if the textWidth has changed
if(textWidth != this._textWidth){
// Get the contentWidth by resizing the iframe
// Check with a min reset of the textWidth
width = this.contentWidth(textWidth);
// Save the textWdith
this._textWidth = textWidth;
// Save the contentWidth
this._contentWidth = width;
} else {
// Otherwise assume content height hasn't changed
width = this._contentWidth;
}
}
// Expand Vertically
if(width && !height) {
textHeight = this.textHeight();
if(textHeight != this._textHeight){
height = this.contentHeight(textHeight);
this._textHeight = textHeight;
this._contentHeight = height;
} else {
height = this._contentHeight;
}
}
// Only Resize if dimensions have changed or
// if Frame is still hidden, so needs reframing
if(this._needsReframe || width != this._width || height != this._height){
this.resize(width, height);
}
this._expanding = false;
};
View.prototype.contentWidth = function(min) {
var prev;
var width;
// Save previous width
prev = this.iframe.style.width;
// Set the iframe size to min, width will only ever be greater
// Will preserve the aspect ratio
this.iframe.style.width = (min || 0) + "px";
// Get the scroll overflow width
width = this.document.body.scrollWidth;
// Reset iframe size back
this.iframe.style.width = prev;
return width;
};
View.prototype.contentHeight = function(min) {
var prev;
var height;
prev = this.iframe.style.height;
this.iframe.style.height = (min || 0) + "px";
height = this.document.body.scrollHeight;
this.iframe.style.height = prev;
return height;
};
View.prototype.textWidth = function() {
var width;
var range = this.document.createRange();
// Select the contents of frame
range.selectNodeContents(this.document.body);
// get the width of the text content
width = range.getBoundingClientRect().width;
return width;
};
View.prototype.textHeight = function() {
var height;
var range = this.document.createRange();
range.selectNodeContents(this.document.body);
height = range.getBoundingClientRect().height;
return height;
};
View.prototype.resize = function(width, height) {
if(!this.iframe) return;
if(core.isNumber(width)){
this.iframe.style.width = width + "px";
this._width = width;
}
if(core.isNumber(height)){
this.iframe.style.height = height + "px";
this._height = height;
}
this.iframeBounds = core.bounds(this.iframe);
this.reframe(this.iframeBounds.width, this.iframeBounds.height);
};
View.prototype.reframe = function(width, height) {
//var prevBounds;
if(!this.displayed) {
this._needsReframe = true;
return;
}
if(core.isNumber(width)){
this.element.style.width = width + "px";
}
if(core.isNumber(height)){
this.element.style.height = height + "px";
}
this.prevBounds = this.elementBounds;
this.elementBounds = core.bounds(this.element);
this.trigger("resized", {
width: this.elementBounds.width,
height: this.elementBounds.height,
widthDelta: this.elementBounds.width - this.prevBounds.width,
heightDelta: this.elementBounds.height - this.prevBounds.height,
});
};
View.prototype.resized = function(e) {
/*
if (!this.resizing) {
if(this.iframe) {
// this.expand();
}
} else {
this.resizing = false;
}*/
};
View.prototype.render = function(_request) {
// if(this.rendering){
// return this.displayed;
// }
this.rendering = true;
// this.displayingDefer = new RSVP.defer();
// this.displayedPromise = this.displaying.promise;
return this.section.render(_request)
.then(function(contents){
return this.load(contents);
}.bind(this));
};
View.prototype.load = function(contents) {
var loading = new RSVP.defer();
var loaded = loading.promise;
if(!this.iframe) {
loading.reject(new Error("No Iframe Available"));
return loaded;
}
this.iframe.onload = function(event) {
this.window = this.iframe.contentWindow;
this.document = this.iframe.contentDocument;
this.rendering = false;
loading.resolve(this);
}.bind(this);
if(this.supportsSrcdoc){
this.iframe.srcdoc = contents;
} else {
this.document = this.iframe.contentDocument;
if(!this.document) {
loading.reject(new Error("No Document Available"));
return loaded;
}
this.document.open();
this.document.write(contents);
this.document.close();
}
return loaded;
};
View.prototype.layout = function(layoutFunc) {
this.iframe.style.display = "inline-block";
// Reset Body Styles
this.document.body.style.margin = "0";
//this.document.body.style.display = "inline-block";
//this.document.documentElement.style.width = "auto";
if(layoutFunc){
layoutFunc(this);
}
this.onLayout(this);
};
View.prototype.onLayout = function(view) {
// stub
};
View.prototype.listeners = function() {
/*
setTimeout(function(){
this.window.addEventListener("resize", this.resized.bind(this), false);
}.bind(this), 10); // Wait to listen for resize events
*/
// Wait for fonts to load to finish
// http://dev.w3.org/csswg/css-font-loading/
// Not implemented fully except in chrome
if(this.document.fonts && this.document.fonts.status === "loading") {
// console.log("fonts unloaded");
this.document.fonts.onloadingdone = function(){
// console.log("loaded fonts");
this.expand();
}.bind(this);
}
if(this.section.properties.indexOf("scripted") > -1){
this.observer = this.observe(this.document.body);
}
this.imageLoadListeners();
this.mediaQueryListeners();
// this.resizeListenters();
this.addEventListeners();
this.addSelectionListeners();
};
View.prototype.removeListeners = function() {
this.removeEventListeners();
this.removeSelectionListeners();
};
View.prototype.resizeListenters = function() {
// Test size again
clearTimeout(this.expanding);
this.expanding = setTimeout(this.expand.bind(this), 350);
};
//https://github.com/tylergaw/media-query-events/blob/master/js/mq-events.js
View.prototype.mediaQueryListeners = function() {
var sheets = this.document.styleSheets;
var mediaChangeHandler = function(m){
if(m.matches && !this._expanding) {
setTimeout(this.expand.bind(this), 1);
// this.expand();
}
}.bind(this);
for (var i = 0; i < sheets.length; i += 1) {
var rules = sheets[i].cssRules;
if(!rules) return; // Stylesheets changed
for (var j = 0; j < rules.length; j += 1) {
//if (rules[j].constructor === CSSMediaRule) {
if(rules[j].media){
var mql = this.window.matchMedia(rules[j].media.mediaText);
mql.addListener(mediaChangeHandler);
//mql.onchange = mediaChangeHandler;
}
}
}
};
View.prototype.observe = function(target) {
var renderer = this;
// create an observer instance
var observer = new MutationObserver(function(mutations) {
if(renderer._expanding) {
renderer.expand();
}
// mutations.forEach(function(mutation) {
// console.log(mutation);
// });
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true, subtree: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);
return observer;
};
// View.prototype.appendTo = function(element) {
// this.element = element;
// this.element.appendChild(this.iframe);
// };
//
// View.prototype.prependTo = function(element) {
// this.element = element;
// element.insertBefore(this.iframe, element.firstChild);
// };
View.prototype.imageLoadListeners = function(target) {
var images = this.document.body.querySelectorAll("img");
var img;
for (var i = 0; i < images.length; i++) {
img = images[i];
if (typeof img.naturalWidth !== "undefined" &&
img.naturalWidth === 0) {
img.onload = this.expand.bind(this);
}
}
};
View.prototype.display = function() {
var displayed = new RSVP.defer();
this.displayed = true;
this.layout();
this.listeners();
this.expand();
this.trigger("displayed", this);
this.onDisplayed(this);
displayed.resolve(this);
return displayed.promise;
};
View.prototype.show = function() {
this.element.style.visibility = "visible";
if(this.iframe){
this.iframe.style.visibility = "visible";
}
this.trigger("shown", this);
};
View.prototype.hide = function() {
// this.iframe.style.display = "none";
this.element.style.visibility = "hidden";
this.iframe.style.visibility = "hidden";
this.stopExpanding = true;
this.trigger("hidden", this);
};
View.prototype.position = function() {
return this.element.getBoundingClientRect();
};
View.prototype.onDisplayed = function(view) {
// Stub, override with a custom functions
};
View.prototype.bounds = function() {
if(!this.elementBounds) {
this.elementBounds = core.bounds(this.element);
}
return this.elementBounds;
};
View.prototype.destroy = function() {
// Stop observing
if(this.observer) {
this.observer.disconnect();
}
if(this.displayed){
this.removeListeners();
this.stopExpanding = true;
this.element.removeChild(this.iframe);
this.displayed = false;
this.iframe = null;
this._textWidth = null;
this._textHeight = null;
this._width = null;
this._height = null;
}
// this.element.style.height = "0px";
// this.element.style.width = "0px";
};
View.prototype.root = function() {
if(!this.document) return null;
return this.document.documentElement;
};
View.prototype.locationOf = function(target) {
var parentPos = this.iframe.getBoundingClientRect();
var targetPos = {"left": 0, "top": 0};
if(!this.document) return;
if(this.epubcfi.isCfiString(target)) {
cfi = this.epubcfi.parse(target);
if(typeof document.evaluate === 'undefined') {
marker = this.epubcfi.addMarker(cfi, this.document);
if(marker) {
// Must Clean up Marker before going to page
this.epubcfi.removeMarker(marker, this.document);
targetPos = marker.getBoundingClientRect();
}
} else {
range = this.epubcfi.generateRangeFromCfi(cfi, this.document);
if(range) {
targetPos = range.getBoundingClientRect();
}
}
} else if(typeof target === "string" &&
target.indexOf("#") > -1) {
id = target.substring(target.indexOf("#")+1);
el = this.document.getElementById(id);
if(el) {
targetPos = el.getBoundingClientRect();
}
}
return {
"left": window.scrollX + parentPos.left + targetPos.left,
"top": window.scrollY + parentPos.top + targetPos.top
};
};
View.prototype.addCss = function(src) {
return new RSVP.Promise(function(resolve, reject){
var $stylesheet;
var ready = false;
if(!this.document) {
resolve(false);
return;
}
$stylesheet = this.document.createElement('link');
$stylesheet.type = 'text/css';
$stylesheet.rel = "stylesheet";
$stylesheet.href = src;
$stylesheet.onload = $stylesheet.onreadystatechange = function() {
if ( !ready && (!this.readyState || this.readyState == 'complete') ) {
ready = true;
// Let apply
setTimeout(function(){
resolve(true);
}, 1);
}
};
this.document.head.appendChild($stylesheet);
}.bind(this));
};
// https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet/insertRule
View.prototype.addStylesheetRules = function(rules) {
var styleEl;
var styleSheet;
if(!this.document) return;
styleEl = this.document.createElement('style');
// Append style element to head
this.document.head.appendChild(styleEl);
// Grab style sheet
styleSheet = styleEl.sheet;
for (var i = 0, rl = rules.length; i < rl; i++) {
var j = 1, rule = rules[i], selector = rules[i][0], propStr = '';
// If the second argument of a rule is an array of arrays, correct our variables.
if (Object.prototype.toString.call(rule[1][0]) === '[object Array]') {
rule = rule[1];
j = 0;
}
for (var pl = rule.length; j < pl; j++) {
var prop = rule[j];
propStr += prop[0] + ':' + prop[1] + (prop[2] ? ' !important' : '') + ';\n';
}
// Insert CSS Rule
styleSheet.insertRule(selector + '{' + propStr + '}', styleSheet.cssRules.length);
}
};
View.prototype.addScript = function(src) {
return new RSVP.Promise(function(resolve, reject){
var $script;
var ready = false;
if(!this.document) {
resolve(false);
return;
}
$script = this.document.createElement('script');
$script.type = 'text/javascript';
$script.async = true;
$script.src = src;
$script.onload = $script.onreadystatechange = function() {
if ( !ready && (!this.readyState || this.readyState == 'complete') ) {
ready = true;
setTimeout(function(){
resolve(true);
}, 1);
}
};
this.document.head.appendChild($script);
}.bind(this));
};
View.prototype.addEventListeners = function(){
if(!this.document) {
return;
}
this.listenedEvents.forEach(function(eventName){
this.document.addEventListener(eventName, this.triggerEvent.bind(this), false);
}, this);
};
View.prototype.removeEventListeners = function(){
if(!this.document) {
return;
}
this.listenedEvents.forEach(function(eventName){
this.document.removeEventListener(eventName, this.triggerEvent, false);
}, this);
};
// Pass browser events
View.prototype.triggerEvent = function(e){
this.trigger(e.type, e);
};
View.prototype.addSelectionListeners = function(){
if(!this.document) {
return;
}
this.document.addEventListener("selectionchange", this.onSelectionChange.bind(this), false);
};
View.prototype.removeSelectionListeners = function(){
if(!this.document) {
return;
}
this.document.removeEventListener("selectionchange", this.onSelectionChange, false);
};
View.prototype.onSelectionChange = function(e){
if (this.selectionEndTimeout) {
clearTimeout(this.selectionEndTimeout);
}
this.selectionEndTimeout = setTimeout(function() {
this.selectedRange = this.window.getSelection();
this.trigger("selected", this.selectedRange);
}.bind(this), 500);
};
RSVP.EventTarget.mixin(View.prototype);
module.exports = View;
},{"./core":8,"./epubcfi":9,"rsvp":4}],25:[function(require,module,exports){
function Views(container) {
this.container = container;
this._views = [];
this.length = 0;
this.hidden = false;
};
Views.prototype.first = function() {
return this._views[0];
};
Views.prototype.last = function() {
return this._views[this._views.length-1];
};
Views.prototype.each = function() {
return this._views.forEach.apply(this._views, arguments);
};
Views.prototype.indexOf = function(view) {
return this._views.indexOf(view);
};
Views.prototype.slice = function() {
return this._views.slice.apply(this._views, arguments);
};
Views.prototype.get = function(i) {
return this._views[i];
};
Views.prototype.append = function(view){
this._views.push(view);
this.container.appendChild(view.element);
this.length++;
return view;
};
Views.prototype.prepend = function(view){
this._views.unshift(view);
this.container.insertBefore(view.element, this.container.firstChild);
this.length++;
return view;
};
Views.prototype.insert = function(view, index) {
this._views.splice(index, 0, view);
if(index < this.container.children.length){
this.container.insertBefore(view.element, this.container.children[index]);
} else {
this.container.appendChild(view.element);
}
this.length++;
return view;
};
Views.prototype.remove = function(view) {
var index = this._views.indexOf(view);
if(index > -1) {
this._views.splice(index, 1);
}
this.destroy(view);
this.length--;
};
Views.prototype.destroy = function(view) {
view.off("resized");
if(view.displayed){
view.destroy();
}
this.container.removeChild(view.element);
view = null;
};
// Iterators
Views.prototype.clear = function(){
// Remove all views
var view;
var len = this.length;
if(!this.length) return;
for (var i = 0; i < len; i++) {
view = this._views[i];
this.destroy(view);
}
this._views = [];
this.length = 0;
};
Views.prototype.find = function(section){
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed && view.section.index == section.index) {
return view;
}
}
};
Views.prototype.displayed = function(){
var displayed = [];
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed){
displayed.push(view);
}
}
return displayed;
};
Views.prototype.show = function(){
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed){
view.show();
}
}
this.hidden = false;
};
Views.prototype.hide = function(){
var view;
var len = this.length;
for (var i = 0; i < len; i++) {
view = this._views[i];
if(view.displayed){
view.hide();
}
}
this.hidden = true;
};
module.exports = Views;
},{}],"epub":[function(require,module,exports){
(function (global){
if (typeof EPUBJS === 'undefined') {
(typeof window !== 'undefined' ? window : global).EPUBJS = {};
}
EPUBJS.VERSION = "0.3.0";
var Book = require('./book');
function ePub(_url) {
return new Book(_url);
};
module.exports = ePub;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"./book":6}]},{},["epub"])("epub")
});
//# sourceMappingURL=epub.js.map