2019-01-21 11:39:49 +00:00
/ * !
* @ overview RSVP - a tiny implementation of Promises / A + .
* @ copyright Copyright ( c ) 2016 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.6 . 2
* /
( function ( global , factory ) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory ( exports ) :
typeof define === 'function' && define . amd ? define ( [ 'exports' ] , factory ) :
( factory ( ( global . RSVP = global . RSVP || { } ) ) ) ;
} ( this , ( function ( exports ) { 'use strict' ;
function indexOf ( callbacks , callback ) {
for ( var i = 0 , l = callbacks . length ; i < l ; i ++ ) {
if ( callbacks [ i ] === callback ) {
return i ;
}
}
return - 1 ;
}
function callbacksFor ( object ) {
var callbacks = object . _promiseCallbacks ;
if ( ! callbacks ) {
callbacks = object . _promiseCallbacks = { } ;
}
return callbacks ;
}
/ * *
@ class RSVP . EventTarget
* /
var EventTarget = {
/ * *
` RSVP.EventTarget.mixin ` extends an object with EventTarget methods . For
Example :
` ` ` javascript
let object = { } ;
RSVP . EventTarget . mixin ( object ) ;
object . on ( 'finished' , function ( event ) {
// handle event
} ) ;
object . trigger ( 'finished' , { detail : value } ) ;
` ` `
` EventTarget.mixin ` also works with prototypes :
` ` ` javascript
let Person = function ( ) { } ;
RSVP . EventTarget . mixin ( Person . prototype ) ;
let yehuda = new Person ( ) ;
let 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 ) {
if ( typeof callback !== 'function' ) {
throw new TypeError ( 'Callback must be a function' ) ;
}
var allCallbacks = callbacksFor ( this ) ,
callbacks = void 0 ;
callbacks = allCallbacks [ eventName ] ;
if ( ! callbacks ) {
callbacks = allCallbacks [ eventName ] = [ ] ;
}
if ( 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
let callback1 = function ( ) { } ;
let 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 = callbacksFor ( this ) ,
callbacks = void 0 ,
index = void 0 ;
if ( ! callback ) {
allCallbacks [ eventName ] = [ ] ;
return ;
}
callbacks = allCallbacks [ eventName ] ;
index = 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 { * } options optional value to be passed to any event handlers for
the given ` eventName `
* /
trigger : function ( eventName , options , label ) {
var allCallbacks = callbacksFor ( this ) ,
callbacks = void 0 ,
callback = void 0 ;
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 , label ) ;
}
}
}
} ;
var config = {
instrument : false
} ;
EventTarget [ 'mixin' ] ( config ) ;
function configure ( name , value ) {
if ( arguments . length === 2 ) {
config [ name ] = value ;
} else {
return config [ name ] ;
}
}
function objectOrFunction ( x ) {
var type = typeof x ;
return x !== null && ( type === 'object' || type === 'function' ) ;
}
function isFunction ( x ) {
return typeof x === 'function' ;
}
function isObject ( x ) {
return x !== null && typeof x === 'object' ;
}
function isMaybeThenable ( x ) {
return x !== null && typeof x === 'object' ;
}
var _isArray = void 0 ;
if ( Array . isArray ) {
_isArray = Array . isArray ;
} else {
_isArray = function ( x ) {
return Object . prototype . toString . call ( x ) === '[object Array]' ;
} ;
}
var isArray = _isArray ;
// Date.now is not available in browsers < IE9
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility
var now = Date . now || function ( ) {
return new Date ( ) . getTime ( ) ;
} ;
var queue = [ ] ;
function scheduleFlush ( ) {
setTimeout ( function ( ) {
for ( var i = 0 ; i < queue . length ; i ++ ) {
var entry = queue [ i ] ;
var payload = entry . payload ;
payload . guid = payload . key + payload . id ;
payload . childGuid = payload . key + payload . childId ;
if ( payload . error ) {
payload . stack = payload . error . stack ;
}
config [ 'trigger' ] ( entry . name , entry . payload ) ;
}
queue . length = 0 ;
} , 50 ) ;
}
function instrument ( eventName , promise , child ) {
if ( 1 === queue . push ( {
name : eventName ,
payload : {
key : promise . _guidKey ,
id : promise . _id ,
eventName : eventName ,
detail : promise . _result ,
childId : child && child . _id ,
label : promise . _label ,
timeStamp : now ( ) ,
error : config [ "instrument-with-stack" ] ? new Error ( promise . _label ) : null
} } ) ) {
scheduleFlush ( ) ;
}
}
/ * *
` RSVP.Promise.resolve ` returns a promise that will become resolved with the
passed ` value ` . It is shorthand for the following :
` ` ` javascript
let promise = new RSVP . Promise ( function ( resolve , reject ) {
resolve ( 1 ) ;
} ) ;
promise . then ( function ( value ) {
// value === 1
} ) ;
` ` `
Instead of writing the above , your code now simply becomes the following :
` ` ` javascript
let promise = RSVP . Promise . resolve ( 1 ) ;
promise . then ( function ( value ) {
// value === 1
} ) ;
` ` `
@ method resolve
@ static
@ param { * } object value that the returned promise will be resolved with
@ param { String } label optional string for identifying the returned promise .
Useful for tooling .
@ return { Promise } a promise that will become fulfilled with the given
` value `
* /
function resolve$1 ( object , label ) {
/*jshint validthis:true */
var Constructor = this ;
if ( object && typeof object === 'object' && object . constructor === Constructor ) {
return object ;
}
var promise = new Constructor ( noop , label ) ;
resolve ( promise , object ) ;
return promise ;
}
function withOwnPromise ( ) {
return new TypeError ( 'A promises callback cannot return that same promise.' ) ;
}
function noop ( ) { }
var PENDING = void 0 ;
var FULFILLED = 1 ;
var REJECTED = 2 ;
var GET _THEN _ERROR = new ErrorObject ( ) ;
function getThen ( promise ) {
try {
return promise . then ;
} catch ( error ) {
GET _THEN _ERROR . error = error ;
return GET _THEN _ERROR ;
}
}
function tryThen ( then$$1 , value , fulfillmentHandler , rejectionHandler ) {
try {
then$$1 . call ( value , fulfillmentHandler , rejectionHandler ) ;
} catch ( e ) {
return e ;
}
}
function handleForeignThenable ( promise , thenable , then$$1 ) {
config . async ( function ( promise ) {
var sealed = false ;
var error = tryThen ( then$$1 , thenable , function ( value ) {
if ( sealed ) {
return ;
}
sealed = true ;
if ( thenable !== value ) {
resolve ( promise , value , undefined ) ;
} else {
fulfill ( promise , value ) ;
}
} , function ( reason ) {
if ( sealed ) {
return ;
}
sealed = true ;
reject ( promise , reason ) ;
} , 'Settle: ' + ( promise . _label || ' unknown promise' ) ) ;
if ( ! sealed && error ) {
sealed = true ;
reject ( promise , error ) ;
}
} , promise ) ;
}
function handleOwnThenable ( promise , thenable ) {
if ( thenable . _state === FULFILLED ) {
fulfill ( promise , thenable . _result ) ;
} else if ( thenable . _state === REJECTED ) {
thenable . _onError = null ;
reject ( promise , thenable . _result ) ;
} else {
subscribe ( thenable , undefined , function ( value ) {
if ( thenable !== value ) {
resolve ( promise , value , undefined ) ;
} else {
fulfill ( promise , value ) ;
}
} , function ( reason ) {
return reject ( promise , reason ) ;
} ) ;
}
}
function handleMaybeThenable ( promise , maybeThenable , then$$1 ) {
var isOwnThenable = maybeThenable . constructor === promise . constructor && then$$1 === then && promise . constructor . resolve === resolve$1 ;
if ( isOwnThenable ) {
handleOwnThenable ( promise , maybeThenable ) ;
} else if ( then$$1 === GET _THEN _ERROR ) {
reject ( promise , GET _THEN _ERROR . error ) ;
GET _THEN _ERROR . error = null ;
} else if ( isFunction ( then$$1 ) ) {
handleForeignThenable ( promise , maybeThenable , then$$1 ) ;
} else {
fulfill ( promise , maybeThenable ) ;
}
}
function resolve ( promise , value ) {
if ( promise === value ) {
fulfill ( promise , value ) ;
} else if ( objectOrFunction ( value ) ) {
handleMaybeThenable ( promise , value , getThen ( value ) ) ;
} else {
fulfill ( promise , value ) ;
}
}
function publishRejection ( promise ) {
if ( promise . _onError ) {
promise . _onError ( promise . _result ) ;
}
publish ( promise ) ;
}
function fulfill ( promise , value ) {
if ( promise . _state !== PENDING ) {
return ;
}
promise . _result = value ;
promise . _state = FULFILLED ;
if ( promise . _subscribers . length === 0 ) {
if ( config . instrument ) {
instrument ( 'fulfilled' , promise ) ;
}
} else {
config . async ( publish , promise ) ;
}
}
function reject ( promise , reason ) {
if ( promise . _state !== PENDING ) {
return ;
}
promise . _state = REJECTED ;
promise . _result = reason ;
config . async ( publishRejection , promise ) ;
}
function subscribe ( parent , child , onFulfillment , onRejection ) {
var subscribers = parent . _subscribers ;
var length = subscribers . length ;
parent . _onError = null ;
subscribers [ length ] = child ;
subscribers [ length + FULFILLED ] = onFulfillment ;
subscribers [ length + REJECTED ] = onRejection ;
if ( length === 0 && parent . _state ) {
config . async ( publish , parent ) ;
}
}
function publish ( promise ) {
var subscribers = promise . _subscribers ;
var settled = promise . _state ;
if ( config . instrument ) {
instrument ( settled === FULFILLED ? 'fulfilled' : 'rejected' , promise ) ;
}
if ( subscribers . length === 0 ) {
return ;
}
var child = void 0 ,
callback = void 0 ,
result = promise . _result ;
for ( var i = 0 ; i < subscribers . length ; i += 3 ) {
child = subscribers [ i ] ;
callback = subscribers [ i + settled ] ;
if ( child ) {
invokeCallback ( settled , child , callback , result ) ;
} else {
callback ( result ) ;
}
}
promise . _subscribers . length = 0 ;
}
function ErrorObject ( ) {
this . error = null ;
}
var TRY _CATCH _ERROR = new ErrorObject ( ) ;
function tryCatch ( callback , result ) {
try {
return callback ( result ) ;
} catch ( e ) {
TRY _CATCH _ERROR . error = e ;
return TRY _CATCH _ERROR ;
}
}
function invokeCallback ( state , promise , callback , result ) {
var hasCallback = isFunction ( callback ) ;
var value = void 0 ,
error = void 0 ;
if ( hasCallback ) {
value = tryCatch ( callback , result ) ;
if ( value === TRY _CATCH _ERROR ) {
error = value . error ;
value . error = null ; // release
} else if ( value === promise ) {
reject ( promise , withOwnPromise ( ) ) ;
return ;
}
} else {
value = result ;
}
if ( promise . _state !== PENDING ) {
// noop
} else if ( hasCallback && error === undefined ) {
resolve ( promise , value ) ;
} else if ( error !== undefined ) {
reject ( promise , error ) ;
} else if ( state === FULFILLED ) {
fulfill ( promise , value ) ;
} else if ( state === REJECTED ) {
reject ( promise , value ) ;
}
}
function initializePromise ( promise , resolver ) {
var resolved = false ;
try {
resolver ( function ( value ) {
if ( resolved ) {
return ;
}
resolved = true ;
resolve ( promise , value ) ;
} , function ( reason ) {
if ( resolved ) {
return ;
}
resolved = true ;
reject ( promise , reason ) ;
} ) ;
} catch ( e ) {
reject ( promise , e ) ;
}
}
function then ( onFulfillment , onRejection , label ) {
var parent = this ;
var state = parent . _state ;
if ( state === FULFILLED && ! onFulfillment || state === REJECTED && ! onRejection ) {
config . instrument && instrument ( 'chained' , parent , parent ) ;
return parent ;
}
parent . _onError = null ;
var child = new parent . constructor ( noop , label ) ;
var result = parent . _result ;
config . instrument && instrument ( 'chained' , parent , child ) ;
if ( state === PENDING ) {
subscribe ( parent , child , onFulfillment , onRejection ) ;
} else {
var callback = state === FULFILLED ? onFulfillment : onRejection ;
config . async ( function ( ) {
return invokeCallback ( state , child , callback , result ) ;
} ) ;
}
return child ;
}
var Enumerator = function ( ) {
function Enumerator ( Constructor , input , abortOnReject , label ) {
this . _instanceConstructor = Constructor ;
this . promise = new Constructor ( noop , label ) ;
this . _abortOnReject = abortOnReject ;
this . _init . apply ( this , arguments ) ;
}
Enumerator . prototype . _init = function _init ( Constructor , input ) {
var len = input . length || 0 ;
this . length = len ;
this . _remaining = len ;
this . _result = new Array ( len ) ;
this . _enumerate ( input ) ;
if ( this . _remaining === 0 ) {
fulfill ( this . promise , this . _result ) ;
}
} ;
Enumerator . prototype . _enumerate = function _enumerate ( input ) {
var length = this . length ;
var promise = this . promise ;
for ( var i = 0 ; promise . _state === PENDING && i < length ; i ++ ) {
this . _eachEntry ( input [ i ] , i ) ;
}
} ;
Enumerator . prototype . _settleMaybeThenable = function _settleMaybeThenable ( entry , i ) {
var c = this . _instanceConstructor ;
var resolve$$1 = c . resolve ;
if ( resolve$$1 === resolve$1 ) {
var then$$1 = getThen ( entry ) ;
if ( then$$1 === then && entry . _state !== PENDING ) {
entry . _onError = null ;
this . _settledAt ( entry . _state , i , entry . _result ) ;
} else if ( typeof then$$1 !== 'function' ) {
this . _remaining -- ;
this . _result [ i ] = this . _makeResult ( FULFILLED , i , entry ) ;
} else if ( c === Promise ) {
var promise = new c ( noop ) ;
handleMaybeThenable ( promise , entry , then$$1 ) ;
this . _willSettleAt ( promise , i ) ;
} else {
this . _willSettleAt ( new c ( function ( resolve$$1 ) {
return resolve$$1 ( entry ) ;
} ) , i ) ;
}
} else {
this . _willSettleAt ( resolve$$1 ( entry ) , i ) ;
}
} ;
Enumerator . prototype . _eachEntry = function _eachEntry ( entry , i ) {
if ( isMaybeThenable ( entry ) ) {
this . _settleMaybeThenable ( entry , i ) ;
} else {
this . _remaining -- ;
this . _result [ i ] = this . _makeResult ( FULFILLED , i , entry ) ;
}
} ;
Enumerator . prototype . _settledAt = function _settledAt ( state , i , value ) {
var promise = this . promise ;
if ( promise . _state === PENDING ) {
if ( this . _abortOnReject && state === REJECTED ) {
reject ( promise , value ) ;
} else {
this . _remaining -- ;
this . _result [ i ] = this . _makeResult ( state , i , value ) ;
if ( this . _remaining === 0 ) {
fulfill ( promise , this . _result ) ;
}
}
}
} ;
Enumerator . prototype . _makeResult = function _makeResult ( state , i , value ) {
return value ;
} ;
Enumerator . prototype . _willSettleAt = function _willSettleAt ( promise , i ) {
var enumerator = this ;
subscribe ( promise , undefined , function ( value ) {
return enumerator . _settledAt ( FULFILLED , i , value ) ;
} , function ( reason ) {
return enumerator . _settledAt ( REJECTED , i , reason ) ;
} ) ;
} ;
return Enumerator ;
} ( ) ;
function makeSettledResult ( state , position , value ) {
if ( state === FULFILLED ) {
return {
state : 'fulfilled' ,
value : value
} ;
} else {
return {
state : 'rejected' ,
reason : value
} ;
}
}
/ * *
` RSVP.Promise.all ` accepts an array of promises , and returns a new promise which
is fulfilled with an array of fulfillment values for the passed promises , or
rejected with the reason of the first passed promise to be rejected . It casts all
elements of the passed iterable to promises as it runs this algorithm .
Example :
` ` ` javascript
let promise1 = RSVP . resolve ( 1 ) ;
let promise2 = RSVP . resolve ( 2 ) ;
let promise3 = RSVP . resolve ( 3 ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
RSVP . Promise . all ( promises ) . then ( function ( array ) {
// The array here would be [ 1, 2, 3 ];
} ) ;
` ` `
If any of the ` promises ` given to ` RSVP.all ` are rejected , the first promise
that is rejected will be given as an argument to the returned promises ' s
rejection handler . For example :
Example :
` ` ` javascript
let promise1 = RSVP . resolve ( 1 ) ;
let promise2 = RSVP . reject ( new Error ( "2" ) ) ;
let promise3 = RSVP . reject ( new Error ( "3" ) ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
RSVP . Promise . all ( promises ) . then ( function ( array ) {
// Code here never runs because there are rejected promises!
} , function ( error ) {
// error.message === "2"
} ) ;
` ` `
@ method all
@ static
@ param { Array } entries array of promises
@ param { String } label optional string for labeling the promise .
Useful for tooling .
@ return { Promise } promise that is fulfilled when all ` promises ` have been
fulfilled , or rejected if any of them become rejected .
@ static
* /
function all ( entries , label ) {
if ( ! isArray ( entries ) ) {
return this . reject ( new TypeError ( "Promise.all must be called with an array" ) , label ) ;
}
return new Enumerator ( this , entries , true /* abort on reject */ , label ) . promise ;
}
/ * *
` RSVP.Promise.race ` returns a new promise which is settled in the same way as the
first passed promise to settle .
Example :
` ` ` javascript
let promise1 = new RSVP . Promise ( function ( resolve , reject ) {
setTimeout ( function ( ) {
resolve ( 'promise 1' ) ;
} , 200 ) ;
} ) ;
let promise2 = new RSVP . Promise ( function ( resolve , reject ) {
setTimeout ( function ( ) {
resolve ( 'promise 2' ) ;
} , 100 ) ;
} ) ;
RSVP . Promise . race ( [ promise1 , promise2 ] ) . then ( function ( result ) {
// result === 'promise 2' because it was resolved before promise1
// was resolved.
} ) ;
` ` `
` RSVP.Promise.race ` is deterministic in that only the state of the first
settled promise matters . For example , even if other promises given to the
` promises ` array argument are resolved , but the first settled promise has
become rejected before the other promises became fulfilled , the returned
promise will become rejected :
` ` ` javascript
let promise1 = new RSVP . Promise ( function ( resolve , reject ) {
setTimeout ( function ( ) {
resolve ( 'promise 1' ) ;
} , 200 ) ;
} ) ;
let promise2 = new RSVP . Promise ( function ( resolve , reject ) {
setTimeout ( function ( ) {
reject ( new Error ( 'promise 2' ) ) ;
} , 100 ) ;
} ) ;
RSVP . Promise . race ( [ promise1 , promise2 ] ) . then ( function ( result ) {
// Code here never runs
} , function ( reason ) {
// reason.message === 'promise 2' because promise 2 became rejected before
// promise 1 became fulfilled
} ) ;
` ` `
An example real - world use case is implementing timeouts :
` ` ` javascript
RSVP . Promise . race ( [ ajax ( 'foo.json' ) , timeout ( 5000 ) ] )
` ` `
@ method race
@ static
@ param { Array } entries array of promises to observe
@ param { String } label optional string for describing the promise returned .
Useful for tooling .
@ return { Promise } a promise which settles in the same way as the first passed
promise to settle .
* /
function race ( entries , label ) {
/*jshint validthis:true */
var Constructor = this ;
var promise = new Constructor ( noop , label ) ;
if ( ! isArray ( entries ) ) {
reject ( promise , new TypeError ( 'Promise.race must be called with an array' ) ) ;
return promise ;
}
for ( var i = 0 ; promise . _state === PENDING && i < entries . length ; i ++ ) {
subscribe ( Constructor . resolve ( entries [ i ] ) , undefined , function ( value ) {
return resolve ( promise , value ) ;
} , function ( reason ) {
return reject ( promise , reason ) ;
} ) ;
}
return promise ;
}
/ * *
` RSVP.Promise.reject ` returns a promise rejected with the passed ` reason ` .
It is shorthand for the following :
` ` ` javascript
let promise = new RSVP . Promise ( function ( resolve , reject ) {
reject ( new Error ( 'WHOOPS' ) ) ;
} ) ;
promise . then ( function ( value ) {
// Code here doesn't run because the promise is rejected!
} , function ( reason ) {
// reason.message === 'WHOOPS'
} ) ;
` ` `
Instead of writing the above , your code now simply becomes the following :
` ` ` javascript
let promise = RSVP . Promise . reject ( new Error ( 'WHOOPS' ) ) ;
promise . then ( function ( value ) {
// Code here doesn't run because the promise is rejected!
} , function ( reason ) {
// reason.message === 'WHOOPS'
} ) ;
` ` `
@ method reject
@ static
@ param { * } reason value that the returned promise will be rejected with .
@ param { String } label optional string for identifying the returned promise .
Useful for tooling .
@ return { Promise } a promise rejected with the given ` reason ` .
* /
function reject$1 ( reason , label ) {
/*jshint validthis:true */
var Constructor = this ;
var promise = new Constructor ( noop , label ) ;
reject ( promise , reason ) ;
return promise ;
}
var guidKey = 'rsvp_' + now ( ) + '-' ;
var counter = 0 ;
function needsResolver ( ) {
throw new TypeError ( 'You must pass a resolver function as the first argument to the promise constructor' ) ;
}
function needsNew ( ) {
throw new TypeError ( "Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function." ) ;
}
/ * *
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 promise ’ s 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
let 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 ) {
let 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
* /
var Promise = function ( ) {
function Promise ( resolver , label ) {
this . _id = counter ++ ;
this . _label = label ;
this . _state = undefined ;
this . _result = undefined ;
this . _subscribers = [ ] ;
config . instrument && instrument ( 'created' , this ) ;
if ( noop !== resolver ) {
typeof resolver !== 'function' && needsResolver ( ) ;
this instanceof Promise ? initializePromise ( this , resolver ) : needsNew ( ) ;
}
}
Promise . prototype . _onError = function _onError ( reason ) {
var _this = this ;
config . after ( function ( ) {
if ( _this . _onError ) {
config . trigger ( 'error' , reason , _this . _label ) ;
}
} ) ;
} ;
/ * *
` catch ` is simply sugar for ` then(undefined, onRejection) ` which makes it the same
as the catch block of a try / c a t c h s t a t e m e n t .
` ` ` 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 }
* /
Promise . prototype . catch = function _catch ( onRejection , label ) {
return this . then ( undefined , onRejection , label ) ;
} ;
/ * *
` finally ` will be invoked regardless of the promise ' s fate just as native
try / c a t c h / f i n a l l y b e h a v e s
Synchronous example :
` ` ` js
findAuthor ( ) {
if ( Math . random ( ) > 0.5 ) {
throw new Error ( ) ;
}
return new Author ( ) ;
}
try {
return findAuthor ( ) ; // succeed or fail
} catch ( error ) {
return findOtherAuthor ( ) ;
} finally {
// always runs
// doesn't affect the return value
}
` ` `
Asynchronous example :
` ` ` js
findAuthor ( ) . catch ( function ( reason ) {
return findOtherAuthor ( ) ;
} ) . 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 }
* /
Promise . prototype . finally = function _finally ( callback , label ) {
var promise = this ;
var constructor = promise . constructor ;
return promise . then ( function ( value ) {
return constructor . resolve ( callback ( ) ) . then ( function ( ) {
return value ;
} ) ;
} , function ( reason ) {
return constructor . resolve ( callback ( ) ) . then ( function ( ) {
throw reason ;
} ) ;
} , label ) ;
} ;
return Promise ;
} ( ) ;
Promise . cast = resolve$1 ; // deprecated
Promise . all = all ;
Promise . race = race ;
Promise . resolve = resolve$1 ;
Promise . reject = reject$1 ;
Promise . prototype . _guidKey = guidKey ;
/ * *
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
let 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
let 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 } onFulfillment
@ param { Function } onRejection
@ param { String } label optional string for labeling the promise .
Useful for tooling .
@ return { Promise }
* /
Promise . prototype . then = then ;
function Result ( ) {
this . value = undefined ;
}
var ERROR = new Result ( ) ;
var GET _THEN _ERROR$1 = new Result ( ) ;
function getThen$1 ( obj ) {
try {
return obj . then ;
} catch ( error ) {
ERROR . value = error ;
return ERROR ;
}
}
function tryApply ( f , s , a ) {
try {
f . apply ( s , a ) ;
} catch ( error ) {
ERROR . value = error ;
return ERROR ;
}
}
function makeObject ( _ , argumentNames ) {
var obj = { } ;
var length = _ . length ;
var args = new Array ( length ) ;
for ( var x = 0 ; x < length ; x ++ ) {
args [ x ] = _ [ x ] ;
}
for ( var i = 0 ; i < argumentNames . length ; i ++ ) {
var name = argumentNames [ i ] ;
obj [ name ] = args [ i + 1 ] ;
}
return obj ;
}
function arrayResult ( _ ) {
var length = _ . length ;
var args = new Array ( length - 1 ) ;
for ( var i = 1 ; i < length ; i ++ ) {
args [ i - 1 ] = _ [ i ] ;
}
return args ;
}
function wrapThenable ( then , promise ) {
return {
then : function ( onFulFillment , onRejection ) {
return then . call ( promise , onFulFillment , onRejection ) ;
}
} ;
}
/ * *
` RSVP.denodeify ` takes a 'node-style' function and returns a function that
will return an ` RSVP.Promise ` . You can use ` denodeify ` in Node . js or the
browser when you ' d prefer to use promises over using callbacks . For example ,
` denodeify ` transforms the following :
` ` ` javascript
let fs = require ( 'fs' ) ;
fs . readFile ( 'myfile.txt' , function ( err , data ) {
if ( err ) return handleError ( err ) ;
handleData ( data ) ;
} ) ;
` ` `
into :
` ` ` javascript
let fs = require ( 'fs' ) ;
let readFile = RSVP . denodeify ( fs . readFile ) ;
readFile ( 'myfile.txt' ) . then ( handleData , handleError ) ;
` ` `
If the node function has multiple success parameters , then ` denodeify `
just returns the first one :
` ` ` javascript
let request = RSVP . denodeify ( require ( 'request' ) ) ;
request ( 'http://example.com' ) . then ( function ( res ) {
// ...
} ) ;
` ` `
However , if you need all success parameters , setting ` denodeify ` ' s
second parameter to ` true ` causes it to return all success parameters
as an array :
` ` ` javascript
let request = RSVP . denodeify ( require ( 'request' ) , true ) ;
request ( 'http://example.com' ) . then ( function ( result ) {
// result[0] -> res
// result[1] -> body
} ) ;
` ` `
Or if you pass it an array with names it returns the parameters as a hash :
` ` ` javascript
let request = RSVP . denodeify ( require ( 'request' ) , [ 'res' , 'body' ] ) ;
request ( 'http://example.com' ) . then ( function ( result ) {
// result.res
// result.body
} ) ;
` ` `
Sometimes you need to retain the ` this ` :
` ` ` javascript
let app = require ( 'express' ) ( ) ;
let render = RSVP . denodeify ( app . render . bind ( app ) ) ;
` ` `
The denodified function inherits from the original function . It works in all
environments , except IE 10 and below . Consequently all properties of the original
function are available to you . However , any properties you change on the
denodeified function won ' t be changed on the original function . Example :
` ` ` javascript
let request = RSVP . denodeify ( require ( 'request' ) ) ,
cookieJar = request . jar ( ) ; // <- Inheritance is used here
request ( 'http://example.com' , { jar : cookieJar } ) . then ( function ( res ) {
// cookieJar.cookies holds now the cookies returned by example.com
} ) ;
` ` `
Using ` denodeify ` makes it easier to compose asynchronous operations instead
of using callbacks . For example , instead of :
` ` ` javascript
let fs = require ( 'fs' ) ;
fs . readFile ( 'myfile.txt' , function ( err , data ) {
if ( err ) { ... } // Handle error
fs . writeFile ( 'myfile2.txt' , data , function ( err ) {
if ( err ) { ... } // Handle error
console . log ( 'done' )
} ) ;
} ) ;
` ` `
you can chain the operations together using ` then ` from the returned promise :
` ` ` javascript
let fs = require ( 'fs' ) ;
let readFile = RSVP . denodeify ( fs . readFile ) ;
let writeFile = RSVP . denodeify ( fs . writeFile ) ;
readFile ( 'myfile.txt' ) . then ( function ( data ) {
return writeFile ( 'myfile2.txt' , data ) ;
} ) . then ( function ( ) {
console . log ( 'done' )
} ) . catch ( function ( error ) {
// Handle error
} ) ;
` ` `
@ method denodeify
@ static
@ for RSVP
@ param { Function } nodeFunc a 'node-style' function that takes a callback as
its last argument . The callback expects an error to be passed as its first
argument ( if an error occurred , otherwise null ) , and the value from the
operation as its second argument ( 'function(err, value){ }' ) .
@ param { Boolean | Array } [ options ] An optional paramter that if set
to ` true ` causes the promise to fulfill with the callback ' s success arguments
as an array . This is useful if the node function has multiple success
paramters . If you set this paramter to an array with names , the promise will
fulfill with a hash with these names as keys and the success parameters as
values .
@ return { Function } a function that wraps ` nodeFunc ` to return an
` RSVP.Promise `
@ static
* /
function denodeify ( nodeFunc , options ) {
var fn = function ( ) {
var self = this ;
var l = arguments . length ;
var args = new Array ( l + 1 ) ;
var promiseInput = false ;
for ( var i = 0 ; i < l ; ++ i ) {
var arg = arguments [ i ] ;
if ( ! promiseInput ) {
// TODO: clean this up
promiseInput = needsPromiseInput ( arg ) ;
if ( promiseInput === GET _THEN _ERROR$1 ) {
var p = new Promise ( noop ) ;
reject ( p , GET _THEN _ERROR$1 . value ) ;
return p ;
} else if ( promiseInput && promiseInput !== true ) {
arg = wrapThenable ( promiseInput , arg ) ;
}
}
args [ i ] = arg ;
}
var promise = new Promise ( noop ) ;
args [ l ] = function ( err , val ) {
if ( err ) reject ( promise , err ) ; else if ( options === undefined ) resolve ( promise , val ) ; else if ( options === true ) resolve ( promise , arrayResult ( arguments ) ) ; else if ( isArray ( options ) ) resolve ( promise , makeObject ( arguments , options ) ) ; else resolve ( promise , val ) ;
} ;
if ( promiseInput ) {
return handlePromiseInput ( promise , args , nodeFunc , self ) ;
} else {
return handleValueInput ( promise , args , nodeFunc , self ) ;
}
} ;
fn . _ _proto _ _ = nodeFunc ;
return fn ;
}
function handleValueInput ( promise , args , nodeFunc , self ) {
var result = tryApply ( nodeFunc , self , args ) ;
if ( result === ERROR ) {
reject ( promise , result . value ) ;
}
return promise ;
}
function handlePromiseInput ( promise , args , nodeFunc , self ) {
return Promise . all ( args ) . then ( function ( args ) {
var result = tryApply ( nodeFunc , self , args ) ;
if ( result === ERROR ) {
reject ( promise , result . value ) ;
}
return promise ;
} ) ;
}
function needsPromiseInput ( arg ) {
if ( arg && typeof arg === 'object' ) {
if ( arg . constructor === Promise ) {
return true ;
} else {
return getThen$1 ( arg ) ;
}
} else {
return false ;
}
}
/ * *
This is a convenient alias for ` RSVP.Promise.all ` .
@ method all
@ static
@ for RSVP
@ param { Array } array Array of promises .
@ param { String } label An optional label . This is useful
for tooling .
* /
function all$1 ( array , label ) {
return Promise . all ( array , label ) ;
}
function _possibleConstructorReturn ( self , call ) { if ( ! self ) { throw new ReferenceError ( "this hasn't been initialised - super() hasn't been called" ) ; } return call && ( typeof call === "object" || typeof call === "function" ) ? call : self ; }
function _inherits ( subClass , superClass ) { if ( typeof superClass !== "function" && superClass !== null ) { throw new TypeError ( "Super expression must either be null or a function, not " + typeof superClass ) ; } subClass . prototype = Object . create ( superClass && superClass . prototype , { constructor : { value : subClass , enumerable : false , writable : true , configurable : true } } ) ; if ( superClass ) Object . setPrototypeOf ? Object . setPrototypeOf ( subClass , superClass ) : subClass . _ _proto _ _ = superClass ; }
var AllSettled = function ( _Enumerator ) {
_inherits ( AllSettled , _Enumerator ) ;
function AllSettled ( Constructor , entries , label ) {
return _possibleConstructorReturn ( this , _Enumerator . call ( this , Constructor , entries , false /* don't abort on reject */ , label ) ) ;
}
return AllSettled ;
} ( Enumerator ) ;
AllSettled . prototype . _makeResult = makeSettledResult ;
/ * *
` RSVP.allSettled ` is similar to ` RSVP.all ` , but instead of implementing
a fail - fast method , it waits until all the promises have returned and
shows you all the results . This is useful if you want to handle multiple
promises ' failure states together as a set .
Returns a promise that is fulfilled when all the given promises have been
settled . The return promise is fulfilled with an array of the states of
the promises passed into the ` promises ` array argument .
Each state object will either indicate fulfillment or rejection , and
provide the corresponding value or reason . The states will take one of
the following formats :
` ` ` javascript
{ state : 'fulfilled' , value : value }
or
{ state : 'rejected' , reason : reason }
` ` `
Example :
` ` ` javascript
let promise1 = RSVP . Promise . resolve ( 1 ) ;
let promise2 = RSVP . Promise . reject ( new Error ( '2' ) ) ;
let promise3 = RSVP . Promise . reject ( new Error ( '3' ) ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
RSVP . allSettled ( promises ) . then ( function ( array ) {
// array == [
// { state: 'fulfilled', value: 1 },
// { state: 'rejected', reason: Error },
// { state: 'rejected', reason: Error }
// ]
// Note that for the second item, reason.message will be '2', and for the
// third item, reason.message will be '3'.
} , function ( error ) {
// Not run. (This block would only be called if allSettled had failed,
// for instance if passed an incorrect argument type.)
} ) ;
` ` `
@ method allSettled
@ static
@ for RSVP
@ param { Array } entries
@ param { String } label - optional string that describes the promise .
Useful for tooling .
@ return { Promise } promise that is fulfilled with an array of the settled
states of the constituent promises .
* /
function allSettled ( entries , label ) {
if ( ! isArray ( entries ) ) {
return Promise . reject ( new TypeError ( "Promise.allSettled must be called with an array" ) , label ) ;
}
return new AllSettled ( Promise , entries , label ) . promise ;
}
/ * *
This is a convenient alias for ` RSVP.Promise.race ` .
@ method race
@ static
@ for RSVP
@ param { Array } array Array of promises .
@ param { String } label An optional label . This is useful
for tooling .
* /
function race$1 ( array , label ) {
return Promise . race ( array , label ) ;
}
function _possibleConstructorReturn$1 ( self , call ) { if ( ! self ) { throw new ReferenceError ( "this hasn't been initialised - super() hasn't been called" ) ; } return call && ( typeof call === "object" || typeof call === "function" ) ? call : self ; }
function _inherits$1 ( subClass , superClass ) { if ( typeof superClass !== "function" && superClass !== null ) { throw new TypeError ( "Super expression must either be null or a function, not " + typeof superClass ) ; } subClass . prototype = Object . create ( superClass && superClass . prototype , { constructor : { value : subClass , enumerable : false , writable : true , configurable : true } } ) ; if ( superClass ) Object . setPrototypeOf ? Object . setPrototypeOf ( subClass , superClass ) : subClass . _ _proto _ _ = superClass ; }
var hasOwnProperty = Object . prototype . hasOwnProperty ;
var PromiseHash = function ( _Enumerator ) {
_inherits$1 ( PromiseHash , _Enumerator ) ;
function PromiseHash ( Constructor , object ) {
var abortOnReject = arguments . length > 2 && arguments [ 2 ] !== undefined ? arguments [ 2 ] : true ;
var label = arguments [ 3 ] ;
return _possibleConstructorReturn$1 ( this , _Enumerator . call ( this , Constructor , object , abortOnReject , label ) ) ;
}
PromiseHash . prototype . _init = function _init ( Constructor , object ) {
this . _result = { } ;
this . _enumerate ( object ) ;
if ( this . _remaining === 0 ) {
fulfill ( this . promise , this . _result ) ;
}
} ;
PromiseHash . prototype . _enumerate = function _enumerate ( input ) {
var promise = this . promise ;
var results = [ ] ;
for ( var key in input ) {
if ( hasOwnProperty . call ( input , key ) ) {
results . push ( {
position : key ,
entry : input [ key ]
} ) ;
}
}
var length = results . length ;
this . _remaining = length ;
var result = void 0 ;
for ( var i = 0 ; promise . _state === PENDING && i < length ; i ++ ) {
result = results [ i ] ;
this . _eachEntry ( result . entry , result . position ) ;
}
} ;
return PromiseHash ;
} ( Enumerator ) ;
/ * *
` RSVP.hash ` is similar to ` RSVP.all ` , but takes an object instead of an array
for its ` promises ` argument .
Returns a promise that is fulfilled when all the given promises have been
fulfilled , or rejected if any of them become rejected . The returned promise
is fulfilled with a hash that has the same key names as the ` promises ` object
argument . If any of the values in the object are not promises , they will
simply be copied over to the fulfilled object .
Example :
` ` ` javascript
let promises = {
myPromise : RSVP . resolve ( 1 ) ,
yourPromise : RSVP . resolve ( 2 ) ,
theirPromise : RSVP . resolve ( 3 ) ,
notAPromise : 4
} ;
RSVP . hash ( promises ) . then ( function ( hash ) {
// hash here is an object that looks like:
// {
// myPromise: 1,
// yourPromise: 2,
// theirPromise: 3,
// notAPromise: 4
// }
} ) ;
` ` ` `
If any of the ` promises ` given to ` RSVP.hash ` are rejected , the first promise
that is rejected will be given as the reason to the rejection handler .
Example :
` ` ` javascript
let promises = {
myPromise : RSVP . resolve ( 1 ) ,
rejectedPromise : RSVP . reject ( new Error ( 'rejectedPromise' ) ) ,
anotherRejectedPromise : RSVP . reject ( new Error ( 'anotherRejectedPromise' ) ) ,
} ;
RSVP . hash ( promises ) . then ( function ( hash ) {
// Code here never runs because there are rejected promises!
} , function ( reason ) {
// reason.message === 'rejectedPromise'
} ) ;
` ` `
An important note : ` RSVP.hash ` is intended for plain JavaScript objects that
are just a set of keys and values . ` RSVP.hash ` will NOT preserve prototype
chains .
Example :
` ` ` javascript
function MyConstructor ( ) {
this . example = RSVP . resolve ( 'Example' ) ;
}
MyConstructor . prototype = {
protoProperty : RSVP . resolve ( 'Proto Property' )
} ;
let myObject = new MyConstructor ( ) ;
RSVP . hash ( myObject ) . then ( function ( hash ) {
// protoProperty will not be present, instead you will just have an
// object that looks like:
// {
// example: 'Example'
// }
//
// hash.hasOwnProperty('protoProperty'); // false
// 'undefined' === typeof hash.protoProperty
} ) ;
` ` `
@ method hash
@ static
@ for RSVP
@ param { Object } object
@ param { String } label optional string that describes the promise .
Useful for tooling .
@ return { Promise } promise that is fulfilled when all properties of ` promises `
have been fulfilled , or rejected if any of them become rejected .
* /
function hash ( object , label ) {
if ( ! isObject ( object ) ) {
return Promise . reject ( new TypeError ( "Promise.hash must be called with an object" ) , label ) ;
}
return new PromiseHash ( Promise , object , label ) . promise ;
}
function _possibleConstructorReturn$2 ( self , call ) { if ( ! self ) { throw new ReferenceError ( "this hasn't been initialised - super() hasn't been called" ) ; } return call && ( typeof call === "object" || typeof call === "function" ) ? call : self ; }
function _inherits$2 ( subClass , superClass ) { if ( typeof superClass !== "function" && superClass !== null ) { throw new TypeError ( "Super expression must either be null or a function, not " + typeof superClass ) ; } subClass . prototype = Object . create ( superClass && superClass . prototype , { constructor : { value : subClass , enumerable : false , writable : true , configurable : true } } ) ; if ( superClass ) Object . setPrototypeOf ? Object . setPrototypeOf ( subClass , superClass ) : subClass . _ _proto _ _ = superClass ; }
var HashSettled = function ( _PromiseHash ) {
_inherits$2 ( HashSettled , _PromiseHash ) ;
function HashSettled ( Constructor , object , label ) {
return _possibleConstructorReturn$2 ( this , _PromiseHash . call ( this , Constructor , object , false , label ) ) ;
}
return HashSettled ;
} ( PromiseHash ) ;
HashSettled . prototype . _makeResult = makeSettledResult ;
/ * *
` RSVP.hashSettled ` is similar to ` RSVP.allSettled ` , but takes an object
instead of an array for its ` promises ` argument .
Unlike ` RSVP.all ` or ` RSVP.hash ` , which implement a fail - fast method ,
but like ` RSVP.allSettled ` , ` hashSettled ` waits until all the
constituent promises have returned and then shows you all the results
with their states and values / reasons . This is useful if you want to
handle multiple promises ' failure states together as a set .
Returns a promise that is fulfilled when all the given promises have been
settled , or rejected if the passed parameters are invalid .
The returned promise is fulfilled with a hash that has the same key names as
the ` promises ` object argument . If any of the values in the object are not
promises , they will be copied over to the fulfilled object and marked with state
'fulfilled' .
Example :
` ` ` javascript
let promises = {
myPromise : RSVP . Promise . resolve ( 1 ) ,
yourPromise : RSVP . Promise . resolve ( 2 ) ,
theirPromise : RSVP . Promise . resolve ( 3 ) ,
notAPromise : 4
} ;
RSVP . hashSettled ( promises ) . then ( function ( hash ) {
// hash here is an object that looks like:
// {
// myPromise: { state: 'fulfilled', value: 1 },
// yourPromise: { state: 'fulfilled', value: 2 },
// theirPromise: { state: 'fulfilled', value: 3 },
// notAPromise: { state: 'fulfilled', value: 4 }
// }
} ) ;
` ` `
If any of the ` promises ` given to ` RSVP.hash ` are rejected , the state will
be set to 'rejected' and the reason for rejection provided .
Example :
` ` ` javascript
let promises = {
myPromise : RSVP . Promise . resolve ( 1 ) ,
rejectedPromise : RSVP . Promise . reject ( new Error ( 'rejection' ) ) ,
anotherRejectedPromise : RSVP . Promise . reject ( new Error ( 'more rejection' ) ) ,
} ;
RSVP . hashSettled ( promises ) . then ( function ( hash ) {
// hash here is an object that looks like:
// {
// myPromise: { state: 'fulfilled', value: 1 },
// rejectedPromise: { state: 'rejected', reason: Error },
// anotherRejectedPromise: { state: 'rejected', reason: Error },
// }
// Note that for rejectedPromise, reason.message == 'rejection',
// and for anotherRejectedPromise, reason.message == 'more rejection'.
} ) ;
` ` `
An important note : ` RSVP.hashSettled ` is intended for plain JavaScript objects that
are just a set of keys and values . ` RSVP.hashSettled ` will NOT preserve prototype
chains .
Example :
` ` ` javascript
function MyConstructor ( ) {
this . example = RSVP . Promise . resolve ( 'Example' ) ;
}
MyConstructor . prototype = {
protoProperty : RSVP . Promise . resolve ( 'Proto Property' )
} ;
let myObject = new MyConstructor ( ) ;
RSVP . hashSettled ( myObject ) . then ( function ( hash ) {
// protoProperty will not be present, instead you will just have an
// object that looks like:
// {
// example: { state: 'fulfilled', value: 'Example' }
// }
//
// hash.hasOwnProperty('protoProperty'); // false
// 'undefined' === typeof hash.protoProperty
} ) ;
` ` `
@ method hashSettled
@ for RSVP
@ param { Object } object
@ param { String } label optional string that describes the promise .
Useful for tooling .
@ return { Promise } promise that is fulfilled when when all properties of ` promises `
have been settled .
@ static
* /
function hashSettled ( object , label ) {
if ( ! isObject ( object ) ) {
return Promise . reject ( new TypeError ( "RSVP.hashSettled must be called with an object" ) , label ) ;
}
return new HashSettled ( Promise , object , false , label ) . promise ;
}
/ * *
` RSVP.rethrow ` will rethrow an error on the next turn of the JavaScript event
loop in order to aid debugging .
Promises A + specifies that any exceptions that occur with a promise must be
caught by the promises implementation and bubbled to the last handler . For
this reason , it is recommended that you always specify a second rejection
handler function to ` then ` . However , ` RSVP.rethrow ` will throw the exception
outside of the promise , so it bubbles up to your console if in the browser ,
or domain / cause uncaught exception in Node . ` rethrow ` will also throw the
error again so the error can be handled by the promise per the spec .
` ` ` javascript
function throws ( ) {
throw new Error ( 'Whoops!' ) ;
}
let promise = new RSVP . Promise ( function ( resolve , reject ) {
throws ( ) ;
} ) ;
promise . catch ( RSVP . rethrow ) . then ( function ( ) {
// Code here doesn't run because the promise became rejected due to an
// error!
} , function ( err ) {
// handle the error here
} ) ;
` ` `
The 'Whoops' error will be thrown on the next turn of the event loop
and you can watch for it in your console . You can also handle it using a
rejection handler given to ` .then ` or ` .catch ` on the returned promise .
@ method rethrow
@ static
@ for RSVP
@ param { Error } reason reason the promise became rejected .
@ throws Error
@ static
* /
function rethrow ( reason ) {
setTimeout ( function ( ) {
throw reason ;
} ) ;
throw reason ;
}
/ * *
` RSVP.defer ` returns an object similar to jQuery ' s ` $ .Deferred ` .
` RSVP.defer ` should be used when porting over code reliant on ` $ .Deferred ` ' s
interface . New code should use the ` RSVP.Promise ` constructor instead .
The object returned from ` RSVP.defer ` is a plain object with three properties :
* promise - an ` RSVP.Promise ` .
* reject - a function that causes the ` promise ` property on this object to
become rejected
* resolve - a function that causes the ` promise ` property on this object to
become fulfilled .
Example :
` ` ` javascript
let deferred = RSVP . defer ( ) ;
deferred . resolve ( "Success!" ) ;
deferred . promise . then ( function ( value ) {
// value here is "Success!"
} ) ;
` ` `
@ method defer
@ static
@ for RSVP
@ param { String } label optional string for labeling the promise .
Useful for tooling .
@ return { Object }
* /
function defer ( label ) {
var deferred = { resolve : undefined , reject : undefined } ;
deferred . promise = new Promise ( function ( resolve , reject ) {
deferred . resolve = resolve ;
deferred . reject = reject ;
} , label ) ;
return deferred ;
}
/ * *
` RSVP.map ` is similar to JavaScript ' s native ` map ` method , except that it
waits for all promises to become fulfilled before running the ` mapFn ` on
each item in given to ` promises ` . ` RSVP.map ` returns a promise that will
become fulfilled with the result of running ` mapFn ` on the values the promises
become fulfilled with .
For example :
` ` ` javascript
let promise1 = RSVP . resolve ( 1 ) ;
let promise2 = RSVP . resolve ( 2 ) ;
let promise3 = RSVP . resolve ( 3 ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
let mapFn = function ( item ) {
return item + 1 ;
} ;
RSVP . map ( promises , mapFn ) . then ( function ( result ) {
// result is [ 2, 3, 4 ]
} ) ;
` ` `
If any of the ` promises ` given to ` RSVP.map ` are rejected , the first promise
that is rejected will be given as an argument to the returned promise ' s
rejection handler . For example :
` ` ` javascript
let promise1 = RSVP . resolve ( 1 ) ;
let promise2 = RSVP . reject ( new Error ( '2' ) ) ;
let promise3 = RSVP . reject ( new Error ( '3' ) ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
let mapFn = function ( item ) {
return item + 1 ;
} ;
RSVP . map ( promises , mapFn ) . then ( function ( array ) {
// Code here never runs because there are rejected promises!
} , function ( reason ) {
// reason.message === '2'
} ) ;
` ` `
` RSVP.map ` will also wait if a promise is returned from ` mapFn ` . For example ,
say you want to get all comments from a set of blog posts , but you need
the blog posts first because they contain a url to those comments .
` ` ` javscript
let mapFn = function ( blogPost ) {
// getComments does some ajax and returns an RSVP.Promise that is fulfilled
// with some comments data
return getComments ( blogPost . comments _url ) ;
} ;
// getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled
// with some blog post data
RSVP . map ( getBlogPosts ( ) , mapFn ) . then ( function ( comments ) {
// comments is the result of asking the server for the comments
// of all blog posts returned from getBlogPosts()
} ) ;
` ` `
@ method map
@ static
@ for RSVP
@ param { Array } promises
@ param { Function } mapFn function to be called on each fulfilled promise .
@ param { String } label optional string for labeling the promise .
Useful for tooling .
@ return { Promise } promise that is fulfilled with the result of calling
` mapFn ` on each fulfilled promise or value when they become fulfilled .
The promise will be rejected if any of the given ` promises ` become rejected .
@ static
* /
function map ( promises , mapFn , label ) {
if ( ! isArray ( promises ) ) {
return Promise . reject ( new TypeError ( "RSVP.map must be called with an array" ) , label ) ;
}
if ( ! isFunction ( mapFn ) ) {
return Promise . reject ( new TypeError ( "RSVP.map expects a function as a second argument" ) , label ) ;
}
return Promise . all ( promises , label ) . then ( function ( values ) {
var length = values . length ;
var results = new Array ( length ) ;
for ( var i = 0 ; i < length ; i ++ ) {
results [ i ] = mapFn ( values [ i ] ) ;
}
return Promise . all ( results , label ) ;
} ) ;
}
/ * *
This is a convenient alias for ` RSVP.Promise.resolve ` .
@ method resolve
@ static
@ for RSVP
@ param { * } value value that the returned promise will be resolved with
@ param { String } label optional string for identifying the returned promise .
Useful for tooling .
@ return { Promise } a promise that will become fulfilled with the given
` value `
* /
function resolve$2 ( value , label ) {
return Promise . resolve ( value , label ) ;
}
/ * *
This is a convenient alias for ` RSVP.Promise.reject ` .
@ method reject
@ static
@ for RSVP
@ param { * } reason value that the returned promise will be rejected with .
@ param { String } label optional string for identifying the returned promise .
Useful for tooling .
@ return { Promise } a promise rejected with the given ` reason ` .
* /
function reject$2 ( reason , label ) {
return Promise . reject ( reason , label ) ;
}
/ * *
` RSVP.filter ` is similar to JavaScript ' s native ` filter ` method , except that it
waits for all promises to become fulfilled before running the ` filterFn ` on
each item in given to ` promises ` . ` RSVP.filter ` returns a promise that will
become fulfilled with the result of running ` filterFn ` on the values the
promises become fulfilled with .
For example :
` ` ` javascript
let promise1 = RSVP . resolve ( 1 ) ;
let promise2 = RSVP . resolve ( 2 ) ;
let promise3 = RSVP . resolve ( 3 ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
let filterFn = function ( item ) {
return item > 1 ;
} ;
RSVP . filter ( promises , filterFn ) . then ( function ( result ) {
// result is [ 2, 3 ]
} ) ;
` ` `
If any of the ` promises ` given to ` RSVP.filter ` are rejected , the first promise
that is rejected will be given as an argument to the returned promise ' s
rejection handler . For example :
` ` ` javascript
let promise1 = RSVP . resolve ( 1 ) ;
let promise2 = RSVP . reject ( new Error ( '2' ) ) ;
let promise3 = RSVP . reject ( new Error ( '3' ) ) ;
let promises = [ promise1 , promise2 , promise3 ] ;
let filterFn = function ( item ) {
return item > 1 ;
} ;
RSVP . filter ( promises , filterFn ) . then ( function ( array ) {
// Code here never runs because there are rejected promises!
} , function ( reason ) {
// reason.message === '2'
} ) ;
` ` `
` RSVP.filter ` will also wait for any promises returned from ` filterFn ` .
For instance , you may want to fetch a list of users then return a subset
of those users based on some asynchronous operation :
` ` ` javascript
let alice = { name : 'alice' } ;
let bob = { name : 'bob' } ;
let users = [ alice , bob ] ;
let promises = users . map ( function ( user ) {
return RSVP . resolve ( user ) ;
} ) ;
let filterFn = function ( user ) {
// Here, Alice has permissions to create a blog post, but Bob does not.
return getPrivilegesForUser ( user ) . then ( function ( privs ) {
return privs . can _create _blog _post === true ;
} ) ;
} ;
RSVP . filter ( promises , filterFn ) . then ( function ( users ) {
// true, because the server told us only Alice can create a blog post.
users . length === 1 ;
// false, because Alice is the only user present in `users`
users [ 0 ] === bob ;
} ) ;
` ` `
@ method filter
@ static
@ for RSVP
@ param { Array } promises
@ param { Function } filterFn - function to be called on each resolved value to
filter the final results .
@ param { String } label optional string describing the promise . Useful for
tooling .
@ return { Promise }
* /
function resolveAll ( promises , label ) {
return Promise . all ( promises , label ) ;
}
function resolveSingle ( promise , label ) {
return Promise . resolve ( promise , label ) . then ( function ( promises ) {
return resolveAll ( promises , label ) ;
} ) ;
}
function filter ( promises , filterFn , label ) {
if ( ! isArray ( promises ) && ! ( isObject ( promises ) && promises . then !== undefined ) ) {
return Promise . reject ( new TypeError ( "RSVP.filter must be called with an array or promise" ) , label ) ;
}
if ( ! isFunction ( filterFn ) ) {
return Promise . reject ( new TypeError ( "RSVP.filter expects function as a second argument" ) , label ) ;
}
var promise = isArray ( promises ) ? resolveAll ( promises , label ) : resolveSingle ( promises , label ) ;
return promise . then ( function ( values ) {
var length = values . length ;
var filtered = new Array ( length ) ;
for ( var i = 0 ; i < length ; i ++ ) {
filtered [ i ] = filterFn ( values [ i ] ) ;
}
return resolveAll ( 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 len = 0 ;
var vertxNext = void 0 ;
function asap ( callback , arg ) {
queue$1 [ len ] = callback ;
queue$1 [ len + 1 ] = arg ;
len += 2 ;
if ( 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.
scheduleFlush$1 ( ) ;
}
}
var browserWindow = typeof window !== 'undefined' ? window : undefined ;
var browserGlobal = browserWindow || { } ;
var BrowserMutationObserver = browserGlobal . MutationObserver || browserGlobal . WebKitMutationObserver ;
var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && { } . toString . call ( process ) === '[object process]' ;
// test for web worker but not in IE10
var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined' ;
// node
function useNextTick ( ) {
var nextTick = process . nextTick ;
// node version 0.10.x displays a deprecation warning when nextTick is used recursively
// setImmediate should be used instead instead
var version = process . versions . node . match ( /^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/ ) ;
if ( Array . isArray ( version ) && version [ 1 ] === '0' && version [ 2 ] === '10' ) {
nextTick = setImmediate ;
}
return function ( ) {
return nextTick ( flush ) ;
} ;
}
// vertx
function useVertxTimer ( ) {
if ( typeof vertxNext !== 'undefined' ) {
return function ( ) {
vertxNext ( flush ) ;
} ;
}
return useSetTimeout ( ) ;
}
function useMutationObserver ( ) {
var iterations = 0 ;
var observer = new BrowserMutationObserver ( flush ) ;
var node = document . createTextNode ( '' ) ;
observer . observe ( node , { characterData : true } ) ;
return function ( ) {
return node . data = iterations = ++ iterations % 2 ;
} ;
}
// web worker
function useMessageChannel ( ) {
var channel = new MessageChannel ( ) ;
channel . port1 . onmessage = flush ;
return function ( ) {
return channel . port2 . postMessage ( 0 ) ;
} ;
}
function useSetTimeout ( ) {
return function ( ) {
return setTimeout ( flush , 1 ) ;
} ;
}
var queue$1 = new Array ( 1000 ) ;
function flush ( ) {
for ( var i = 0 ; i < len ; i += 2 ) {
var callback = queue$1 [ i ] ;
var arg = queue$1 [ i + 1 ] ;
callback ( arg ) ;
queue$1 [ i ] = undefined ;
queue$1 [ i + 1 ] = undefined ;
}
len = 0 ;
}
function attemptVertex ( ) {
try {
var r = require ;
var vertx = r ( 'vertx' ) ;
vertxNext = vertx . runOnLoop || vertx . runOnContext ;
return useVertxTimer ( ) ;
} catch ( e ) {
return useSetTimeout ( ) ;
}
}
var scheduleFlush$1 = void 0 ;
// Decide what async method to use to triggering processing of queued callbacks:
if ( isNode ) {
scheduleFlush$1 = useNextTick ( ) ;
} else if ( BrowserMutationObserver ) {
scheduleFlush$1 = useMutationObserver ( ) ;
} else if ( isWorker ) {
scheduleFlush$1 = useMessageChannel ( ) ;
} else if ( browserWindow === undefined && typeof require === 'function' ) {
scheduleFlush$1 = attemptVertex ( ) ;
} else {
scheduleFlush$1 = useSetTimeout ( ) ;
}
var platform = void 0 ;
/* global self */
if ( typeof self === 'object' ) {
platform = self ;
/* global global */
} else if ( typeof global === 'object' ) {
platform = global ;
} else {
throw new Error ( 'no global: `self` or `global` found' ) ;
}
var _asap$cast$Promise$Ev ;
function _defineProperty ( obj , key , value ) { if ( key in obj ) { Object . defineProperty ( obj , key , { value : value , enumerable : true , configurable : true , writable : true } ) ; } else { obj [ key ] = value ; } return obj ; }
// defaults
config . async = asap ;
config . after = function ( cb ) {
return setTimeout ( cb , 0 ) ;
} ;
var cast = resolve$2 ;
var async = function ( callback , arg ) {
return config . async ( callback , arg ) ;
} ;
function on ( ) {
config [ 'on' ] . apply ( config , arguments ) ;
}
function off ( ) {
config [ 'off' ] . apply ( config , arguments ) ;
}
// Set up instrumentation through `window.__PROMISE_INTRUMENTATION__`
if ( typeof window !== 'undefined' && typeof window [ '__PROMISE_INSTRUMENTATION__' ] === 'object' ) {
var callbacks = window [ '__PROMISE_INSTRUMENTATION__' ] ;
configure ( 'instrument' , true ) ;
for ( var eventName in callbacks ) {
if ( callbacks . hasOwnProperty ( eventName ) ) {
on ( eventName , callbacks [ eventName ] ) ;
}
}
}
// the default export here is for backwards compat:
// https://github.com/tildeio/rsvp.js/issues/434
var rsvp = ( _asap$cast$Promise$Ev = {
asap : asap ,
cast : cast ,
Promise : Promise ,
EventTarget : EventTarget ,
all : all$1 ,
allSettled : allSettled ,
race : race$1 ,
hash : hash ,
hashSettled : hashSettled ,
rethrow : rethrow ,
defer : defer ,
denodeify : denodeify ,
configure : configure ,
on : on ,
off : off ,
resolve : resolve$2 ,
reject : reject$2 ,
map : map
} , _defineProperty ( _asap$cast$Promise$Ev , 'async' , async ) , _defineProperty ( _asap$cast$Promise$Ev , 'filter' , filter ) , _asap$cast$Promise$Ev ) ;
exports [ 'default' ] = rsvp ;
exports . asap = asap ;
exports . cast = cast ;
exports . Promise = Promise ;
exports . EventTarget = EventTarget ;
exports . all = all$1 ;
exports . allSettled = allSettled ;
exports . race = race$1 ;
exports . hash = hash ;
exports . hashSettled = hashSettled ;
exports . rethrow = rethrow ;
exports . defer = defer ;
exports . denodeify = denodeify ;
exports . configure = configure ;
exports . on = on ;
exports . off = off ;
exports . resolve = resolve$2 ;
exports . reject = reject$2 ;
exports . map = map ;
exports . async = async ;
exports . filter = filter ;
Object . defineProperty ( exports , '__esModule' , { value : true } ) ;
} ) ) ) ;
//
var EPUBJS = EPUBJS || { } ;
EPUBJS . core = { } ;
var ELEMENT _NODE = 1 ;
var TEXT _NODE = 3 ;
var COMMENT _NODE = 8 ;
var DOCUMENT _NODE = 9 ;
//-- Get a element for an id
EPUBJS . core . getEl = function ( elem ) {
return document . getElementById ( elem ) ;
} ;
//-- Get all elements for a class
EPUBJS . core . getEls = function ( classes ) {
return document . getElementsByClassName ( classes ) ;
} ;
EPUBJS . core . request = function ( url , type , withCredentials ) {
var supportsURL = window . URL ;
var BLOB _RESPONSE = supportsURL ? "blob" : "arraybuffer" ;
var deferred = new RSVP . defer ( ) ;
var xhr = new XMLHttpRequest ( ) ;
var uri ;
//-- Check from PDF.js:
// https://github.com/mozilla/pdf.js/blob/master/web/compatibility.js
var xhrPrototype = XMLHttpRequest . prototype ;
var handler = function ( ) {
var r ;
if ( this . readyState != this . DONE ) return ;
if ( ( this . status === 200 || this . status === 0 ) && this . response ) { // Android & Firefox reporting 0 for local & blob urls
if ( type == 'xml' ) {
// If this.responseXML wasn't set, try to parse using a DOMParser from text
if ( ! this . responseXML ) {
r = new DOMParser ( ) . parseFromString ( this . response , "application/xml" ) ;
} else {
r = this . responseXML ;
}
} else if ( type == 'xhtml' ) {
if ( ! this . responseXML ) {
r = new DOMParser ( ) . parseFromString ( this . response , "application/xhtml+xml" ) ;
} else {
r = this . responseXML ;
}
} else if ( type == 'html' ) {
if ( ! this . responseXML ) {
r = new DOMParser ( ) . parseFromString ( this . response , "text/html" ) ;
} else {
r = this . responseXML ;
}
} 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 ( {
message : this . response ,
stack : new Error ( ) . stack
} ) ;
}
} ;
if ( ! ( 'overrideMimeType' in xhrPrototype ) ) {
// IE10 might have response, but not overrideMimeType
Object . defineProperty ( xhrPrototype , 'overrideMimeType' , {
value : function xmlHttpRequestOverrideMimeType ( mimeType ) { }
} ) ;
}
xhr . onreadystatechange = handler ;
xhr . open ( "GET" , url , true ) ;
if ( withCredentials ) {
xhr . withCredentials = true ;
}
// If type isn't set, determine it from the file extension
if ( ! type ) {
uri = EPUBJS . core . uri ( url ) ;
type = uri . extension ;
type = {
'htm' : 'html'
} [ type ] || type ;
}
if ( type == 'blob' ) {
xhr . responseType = BLOB _RESPONSE ;
}
if ( type == "json" ) {
xhr . setRequestHeader ( "Accept" , "application/json" ) ;
}
if ( type == 'xml' ) {
xhr . responseType = "document" ;
xhr . overrideMimeType ( 'text/xml' ) ; // for OPF parsing
}
if ( type == 'xhtml' ) {
xhr . responseType = "document" ;
}
if ( type == 'html' ) {
xhr . responseType = "document" ;
}
if ( type == "binary" ) {
xhr . responseType = "arraybuffer" ;
}
xhr . send ( ) ;
return deferred . promise ;
} ;
EPUBJS . core . toArray = function ( obj ) {
var arr = [ ] ;
for ( var member in obj ) {
var newitm ;
if ( obj . hasOwnProperty ( member ) ) {
newitm = obj [ member ] ;
newitm . ident = member ;
arr . push ( newitm ) ;
}
}
return arr ;
} ;
//-- Parse the different parts of a url, returning a object
EPUBJS . core . uri = function ( url ) {
var uri = {
protocol : '' ,
host : '' ,
path : '' ,
origin : '' ,
directory : '' ,
base : '' ,
filename : '' ,
extension : '' ,
fragment : '' ,
href : url
} ,
blob = url . indexOf ( 'blob:' ) ,
doubleSlash = url . indexOf ( '://' ) ,
search = url . indexOf ( '?' ) ,
fragment = url . indexOf ( "#" ) ,
withoutProtocol ,
dot ,
firstSlash ;
if ( blob === 0 ) {
uri . protocol = "blob" ;
uri . base = url . indexOf ( 0 , fragment ) ;
return uri ;
}
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 = uri . href ;
}
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 = EPUBJS . core . folder ( uri . path ) ;
uri . base = uri . origin + uri . directory ;
// return origin;
} else {
uri . path = url ;
uri . directory = EPUBJS . core . 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
EPUBJS . core . folder = function ( url ) {
var lastSlash = url . lastIndexOf ( '/' ) ;
if ( lastSlash == - 1 ) var folder = '' ;
folder = url . slice ( 0 , lastSlash + 1 ) ;
return folder ;
} ;
//-- https://github.com/ebidel/filer.js/blob/master/src/filer.js#L128
EPUBJS . core . dataURLToBlob = function ( dataURL ) {
var BASE64 _MARKER = ';base64,' ,
parts , contentType , raw , rawLength , uInt8Array ;
if ( dataURL . indexOf ( BASE64 _MARKER ) == - 1 ) {
parts = dataURL . split ( ',' ) ;
contentType = parts [ 0 ] . split ( ':' ) [ 1 ] ;
raw = parts [ 1 ] ;
return new Blob ( [ raw ] , { type : contentType } ) ;
}
parts = dataURL . split ( BASE64 _MARKER ) ;
contentType = parts [ 0 ] . split ( ':' ) [ 1 ] ;
raw = window . atob ( parts [ 1 ] ) ;
rawLength = raw . length ;
uInt8Array = new Uint8Array ( rawLength ) ;
for ( var i = 0 ; i < rawLength ; ++ i ) {
uInt8Array [ i ] = raw . charCodeAt ( i ) ;
}
return new Blob ( [ uInt8Array ] , { type : contentType } ) ;
} ;
//-- Load scripts async: http://stackoverflow.com/questions/7718935/load-scripts-asynchronously
EPUBJS . core . addScript = function ( src , callback , target ) {
var s , r ;
r = false ;
s = document . createElement ( 'script' ) ;
s . type = 'text/javascript' ;
s . async = false ;
s . src = src ;
s . onload = s . onreadystatechange = function ( ) {
if ( ! r && ( ! this . readyState || this . readyState == 'complete' ) ) {
r = true ;
if ( callback ) callback ( ) ;
}
} ;
target = target || document . body ;
target . appendChild ( s ) ;
} ;
EPUBJS . core . addScripts = function ( srcArr , callback , target ) {
var total = srcArr . length ,
curr = 0 ,
cb = function ( ) {
curr ++ ;
if ( total == curr ) {
if ( callback ) callback ( ) ;
} else {
EPUBJS . core . addScript ( srcArr [ curr ] , cb , target ) ;
}
} ;
EPUBJS . core . addScript ( srcArr [ curr ] , cb , target ) ;
} ;
EPUBJS . core . addCss = function ( src , callback , target ) {
var s , r ;
r = false ;
s = document . createElement ( 'link' ) ;
s . type = 'text/css' ;
s . rel = "stylesheet" ;
s . href = src ;
s . onload = s . onreadystatechange = function ( ) {
if ( ! r && ( ! this . readyState || this . readyState == 'complete' ) ) {
r = true ;
if ( callback ) callback ( ) ;
}
} ;
target = target || document . body ;
target . appendChild ( s ) ;
} ;
EPUBJS . core . prefixed = function ( 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 . documentElement . style [ unprefixed ] ) != 'undefined' ) {
return unprefixed ;
}
for ( var i = 0 ; i < length ; i ++ ) {
if ( typeof ( document . documentElement . style [ vendors [ i ] + upper ] ) != 'undefined' ) {
return vendors [ i ] + upper ;
}
}
return unprefixed ;
} ;
EPUBJS . core . resolveUrl = function ( base , path ) {
var url ,
segments = [ ] ,
uri = EPUBJS . core . uri ( path ) ,
folders = base . split ( "/" ) ,
paths ;
if ( uri . host ) {
return path ;
}
folders . pop ( ) ;
paths = path . split ( "/" ) ;
paths . forEach ( function ( p ) {
if ( p === ".." ) {
folders . pop ( ) ;
} else {
segments . push ( p ) ;
}
} ) ;
url = folders . concat ( segments ) ;
return url . join ( "/" ) ;
} ;
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
EPUBJS . core . uuid = function ( ) {
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 ;
} ;
// 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
EPUBJS . core . insert = function ( item , array , compareFunction ) {
var location = EPUBJS . core . locationOf ( item , array , compareFunction ) ;
array . splice ( location , 0 , item ) ;
return location ;
} ;
EPUBJS . core . locationOf = function ( 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 EPUBJS . core . locationOf ( item , array , compareFunction , pivot , end ) ;
} else {
return EPUBJS . core . locationOf ( item , array , compareFunction , start , pivot ) ;
}
} ;
EPUBJS . core . indexOfSorted = function ( 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 EPUBJS . core . indexOfSorted ( item , array , compareFunction , pivot , end ) ;
} else {
return EPUBJS . core . indexOfSorted ( item , array , compareFunction , start , pivot ) ;
}
} ;
EPUBJS . core . queue = function ( _scope ) {
var _q = [ ] ;
var scope = _scope ;
// Add an item to the queue
var enqueue = function ( funcName , args , context ) {
_q . push ( {
"funcName" : funcName ,
"args" : args ,
"context" : context
} ) ;
return _q ;
} ;
// Run one item
var dequeue = function ( ) {
var inwait ;
if ( _q . length ) {
inwait = _q . shift ( ) ;
// Defer to any current tasks
// setTimeout(function(){
scope [ inwait . funcName ] . apply ( inwait . context || scope , inwait . args ) ;
// }, 0);
}
} ;
// Run All
var flush = function ( ) {
while ( _q . length ) {
dequeue ( ) ;
}
} ;
// Clear all items in wait
var clear = function ( ) {
_q = [ ] ;
} ;
var length = function ( ) {
return _q . length ;
} ;
return {
"enqueue" : enqueue ,
"dequeue" : dequeue ,
"flush" : flush ,
"clear" : clear ,
"length" : length
} ;
} ;
// From: https://code.google.com/p/fbug/source/browse/branches/firebug1.10/content/firebug/lib/xpath.js
/ * *
* Gets an XPath for an element which describes its hierarchical location .
* /
EPUBJS . core . getElementXPath = function ( element ) {
if ( element && element . id ) {
return '//*[@id="' + element . id + '"]' ;
} else {
return EPUBJS . core . getElementTreeXPath ( element ) ;
}
} ;
EPUBJS . core . getElementTreeXPath = function ( element ) {
var paths = [ ] ;
var isXhtml = ( element . ownerDocument . documentElement . getAttribute ( 'xmlns' ) === "http://www.w3.org/1999/xhtml" ) ;
var index , nodeName , tagName , pathIndex ;
if ( element . nodeType === Node . TEXT _NODE ) {
// index = Array.prototype.indexOf.call(element.parentNode.childNodes, element) + 1;
index = EPUBJS . core . indexOfTextNode ( element ) + 1 ;
paths . push ( "text()[" + index + "]" ) ;
element = element . parentNode ;
}
// Use nodeName (instead of localName) so namespace prefix is included (if any).
for ( ; element && element . nodeType == 1 ; element = element . parentNode )
{
index = 0 ;
for ( var sibling = element . previousSibling ; sibling ; sibling = sibling . previousSibling )
{
// Ignore document type declaration.
if ( sibling . nodeType == Node . DOCUMENT _TYPE _NODE ) {
continue ;
}
if ( sibling . nodeName == element . nodeName ) {
++ index ;
}
}
nodeName = element . nodeName . toLowerCase ( ) ;
tagName = ( isXhtml ? "xhtml:" + nodeName : nodeName ) ;
pathIndex = ( index ? "[" + ( index + 1 ) + "]" : "" ) ;
paths . splice ( 0 , 0 , tagName + pathIndex ) ;
}
return paths . length ? "./" + paths . join ( "/" ) : null ;
} ;
EPUBJS . core . nsResolver = function ( prefix ) {
var ns = {
'xhtml' : 'http://www.w3.org/1999/xhtml' ,
'epub' : 'http://www.idpf.org/2007/ops'
} ;
return ns [ prefix ] || null ;
} ;
//https://stackoverflow.com/questions/13482352/xquery-looking-for-text-with-single-quote/13483496#13483496
EPUBJS . core . cleanStringForXpath = function ( 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 ( "," ) + ")" ;
} ;
EPUBJS . core . indexOfTextNode = function ( 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 ;
} ;
// Underscore
EPUBJS . core . defaults = function ( 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 ;
} ;
EPUBJS . core . extend = function ( 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 ;
} ;
EPUBJS . core . clone = function ( obj ) {
return EPUBJS . core . isArray ( obj ) ? obj . slice ( ) : EPUBJS . core . extend ( { } , obj ) ;
} ;
EPUBJS . core . isElement = function ( obj ) {
return ! ! ( obj && obj . nodeType == 1 ) ;
} ;
EPUBJS . core . isNumber = function ( n ) {
return ! isNaN ( parseFloat ( n ) ) && isFinite ( n ) ;
} ;
EPUBJS . core . isString = function ( str ) {
return ( typeof str === 'string' || str instanceof String ) ;
} ;
EPUBJS . core . isArray = Array . isArray || function ( obj ) {
return Object . prototype . toString . call ( obj ) === '[object Array]' ;
} ;
// Lodash
EPUBJS . core . values = function ( object ) {
var index = - 1 ;
var props , length , result ;
if ( ! object ) return [ ] ;
props = Object . keys ( object ) ;
length = props . length ;
result = Array ( length ) ;
while ( ++ index < length ) {
result [ index ] = object [ props [ index ] ] ;
}
return result ;
} ;
EPUBJS . core . indexOfNode = function ( node , typeId ) {
var parent = node . parentNode ;
var children = parent . childNodes ;
var sib ;
var index = - 1 ;
for ( var i = 0 ; i < children . length ; i ++ ) {
sib = children [ i ] ;
if ( sib . nodeType === typeId ) {
index ++ ;
}
if ( sib == node ) break ;
}
return index ;
}
EPUBJS . core . indexOfTextNode = function ( textNode ) {
return EPUBJS . core . indexOfNode ( textNode , TEXT _NODE ) ;
}
EPUBJS . core . indexOfElementNode = function ( elementNode ) {
return EPUBJS . core . indexOfNode ( elementNode , ELEMENT _NODE ) ;
}
var EPUBJS = EPUBJS || { } ;
EPUBJS . reader = { } ;
EPUBJS . reader . plugins = { } ; //-- Attach extra Controllers as plugins (like search?)
( function ( root , $ ) {
var previousReader = root . ePubReader || { } ;
var ePubReader = root . ePubReader = function ( path , options ) {
return new EPUBJS . Reader ( path , options ) ;
} ;
//exports to multiple environments
if ( typeof define === 'function' && define . amd ) {
//AMD
define ( function ( ) { return Reader ; } ) ;
} else if ( typeof module != "undefined" && module . exports ) {
//Node
module . exports = ePubReader ;
}
} ) ( window , jQuery ) ;
EPUBJS . Reader = function ( bookPath , _options ) {
var reader = this ;
var book ;
var plugin ;
var $viewer = $ ( "#viewer" ) ;
var search = window . location . search ;
var parameters ;
this . settings = EPUBJS . core . defaults ( _options || { } , {
bookPath : bookPath ,
restore : false ,
reload : false ,
bookmarks : undefined ,
annotations : undefined ,
contained : undefined ,
bookKey : undefined ,
styles : undefined ,
sidebarReflow : false ,
generatePagination : false ,
history : true
} ) ;
// Overide options with search parameters
if ( search ) {
parameters = search . slice ( 1 ) . split ( "&" ) ;
parameters . forEach ( function ( p ) {
var split = p . split ( "=" ) ;
var name = split [ 0 ] ;
var value = split [ 1 ] || '' ;
reader . settings [ name ] = decodeURIComponent ( value ) ;
} ) ;
}
this . setBookKey ( this . settings . bookPath ) ; //-- This could be username + path or any unique string
if ( this . settings . restore && this . isSaved ( ) ) {
this . applySavedSettings ( ) ;
}
this . settings . styles = this . settings . styles || {
fontSize : "100%"
} ;
this . book = book = new ePub ( this . settings . bookPath , this . settings ) ;
this . offline = false ;
this . sidebarOpen = false ;
if ( ! this . settings . bookmarks ) {
this . settings . bookmarks = [ ] ;
}
if ( ! this . settings . annotations ) {
this . settings . annotations = [ ] ;
}
if ( this . settings . generatePagination ) {
book . generatePagination ( $viewer . width ( ) , $viewer . height ( ) ) ;
}
this . rendition = book . renderTo ( "viewer" , {
ignoreClass : "annotator-hl" ,
width : "100%" ,
height : "100%"
} ) ;
if ( this . settings . previousLocationCfi ) {
this . displayed = this . rendition . display ( this . settings . previousLocationCfi ) ;
} else {
this . displayed = this . rendition . display ( ) ;
}
book . ready . then ( function ( ) {
reader . ReaderController = EPUBJS . reader . ReaderController . call ( reader , book ) ;
reader . SettingsController = EPUBJS . reader . SettingsController . call ( reader , book ) ;
reader . ControlsController = EPUBJS . reader . ControlsController . call ( reader , book ) ;
reader . SidebarController = EPUBJS . reader . SidebarController . call ( reader , book ) ;
reader . BookmarksController = EPUBJS . reader . BookmarksController . call ( reader , book ) ;
reader . NotesController = EPUBJS . reader . NotesController . call ( reader , book ) ;
window . addEventListener ( "hashchange" , this . hashChanged . bind ( this ) , false ) ;
document . addEventListener ( 'keydown' , this . adjustFontSize . bind ( this ) , false ) ;
this . rendition . on ( "keydown" , this . adjustFontSize . bind ( this ) ) ;
this . rendition . on ( "keydown" , reader . ReaderController . arrowKeys . bind ( this ) ) ;
this . rendition . on ( "selected" , this . selectedRange . bind ( this ) ) ;
} . bind ( this ) ) . then ( function ( ) {
reader . ReaderController . hideLoader ( ) ;
} . bind ( this ) ) ;
// Call Plugins
for ( plugin in EPUBJS . reader . plugins ) {
if ( EPUBJS . reader . plugins . hasOwnProperty ( plugin ) ) {
reader [ plugin ] = EPUBJS . reader . plugins [ plugin ] . call ( reader , book ) ;
}
}
book . loaded . metadata . then ( function ( meta ) {
reader . MetaController = EPUBJS . reader . MetaController . call ( reader , meta ) ;
} ) ;
book . loaded . navigation . then ( function ( navigation ) {
reader . TocController = EPUBJS . reader . TocController . call ( reader , navigation ) ;
} ) ;
window . addEventListener ( "beforeunload" , this . unload . bind ( this ) , false ) ;
return this ;
} ;
EPUBJS . Reader . prototype . adjustFontSize = function ( e ) {
var fontSize ;
var interval = 2 ;
var PLUS = 187 ;
var MINUS = 189 ;
var ZERO = 48 ;
var MOD = ( e . ctrlKey || e . metaKey ) ;
if ( ! this . settings . styles ) return ;
2019-01-22 15:12:12 +00:00
/ *
2019-01-21 11:39:49 +00:00
if ( ! this . settings . styles . fontSize ) {
this . settings . styles . fontSize = "100%" ;
}
fontSize = parseInt ( this . settings . styles . fontSize . slice ( 0 , - 1 ) ) ;
if ( MOD && e . keyCode == PLUS ) {
e . preventDefault ( ) ;
this . book . setStyle ( "fontSize" , ( fontSize + interval ) + "%" ) ;
}
if ( MOD && e . keyCode == MINUS ) {
e . preventDefault ( ) ;
this . book . setStyle ( "fontSize" , ( fontSize - interval ) + "%" ) ;
}
if ( MOD && e . keyCode == ZERO ) {
e . preventDefault ( ) ;
this . book . setStyle ( "fontSize" , "100%" ) ;
}
2019-01-22 15:12:12 +00:00
* /
2019-01-21 11:39:49 +00:00
} ;
EPUBJS . Reader . prototype . addBookmark = function ( cfi ) {
var present = this . isBookmarked ( cfi ) ;
if ( present > - 1 ) return ;
this . settings . bookmarks . push ( cfi ) ;
this . trigger ( "reader:bookmarked" , cfi ) ;
} ;
EPUBJS . Reader . prototype . removeBookmark = function ( cfi ) {
var bookmark = this . isBookmarked ( cfi ) ;
if ( bookmark === - 1 ) return ;
this . settings . bookmarks . splice ( bookmark , 1 ) ;
this . trigger ( "reader:unbookmarked" , bookmark ) ;
} ;
EPUBJS . Reader . prototype . isBookmarked = function ( cfi ) {
var bookmarks = this . settings . bookmarks ;
return bookmarks . indexOf ( cfi ) ;
} ;
/ *
EPUBJS . Reader . prototype . searchBookmarked = function ( cfi ) {
var bookmarks = this . settings . bookmarks ,
len = bookmarks . length ,
i ;
for ( i = 0 ; i < len ; i ++ ) {
if ( bookmarks [ i ] [ 'cfi' ] === cfi ) return i ;
}
return - 1 ;
} ;
* /
EPUBJS . Reader . prototype . clearBookmarks = function ( ) {
this . settings . bookmarks = [ ] ;
} ;
//-- Notes
EPUBJS . Reader . prototype . addNote = function ( note ) {
this . settings . annotations . push ( note ) ;
} ;
EPUBJS . Reader . prototype . removeNote = function ( note ) {
var index = this . settings . annotations . indexOf ( note ) ;
if ( index === - 1 ) return ;
delete this . settings . annotations [ index ] ;
} ;
EPUBJS . Reader . prototype . clearNotes = function ( ) {
this . settings . annotations = [ ] ;
} ;
//-- Settings
EPUBJS . Reader . prototype . setBookKey = function ( identifier ) {
if ( ! this . settings . bookKey ) {
this . settings . bookKey = "epubjsreader:" + EPUBJS . VERSION + ":" + window . location . host + ":" + identifier ;
}
return this . settings . bookKey ;
} ;
//-- Checks if the book setting can be retrieved from localStorage
EPUBJS . Reader . prototype . isSaved = function ( bookPath ) {
var storedSettings ;
if ( ! localStorage ) {
return false ;
}
storedSettings = localStorage . getItem ( this . settings . bookKey ) ;
if ( storedSettings === null ) {
return false ;
} else {
return true ;
}
} ;
EPUBJS . Reader . prototype . removeSavedSettings = function ( ) {
if ( ! localStorage ) {
return false ;
}
localStorage . removeItem ( this . settings . bookKey ) ;
} ;
EPUBJS . Reader . prototype . applySavedSettings = function ( ) {
var stored ;
if ( ! localStorage ) {
return false ;
}
try {
stored = JSON . parse ( localStorage . getItem ( this . settings . bookKey ) ) ;
} catch ( e ) { // parsing error of localStorage
return false ;
}
if ( stored ) {
// Merge styles
if ( stored . styles ) {
this . settings . styles = EPUBJS . core . defaults ( this . settings . styles || { } , stored . styles ) ;
}
// Merge the rest
this . settings = EPUBJS . core . defaults ( this . settings , stored ) ;
return true ;
} else {
return false ;
}
} ;
EPUBJS . Reader . prototype . saveSettings = function ( ) {
if ( this . book ) {
this . settings . previousLocationCfi = this . rendition . currentLocation ( ) . start . cfi ;
}
if ( ! localStorage ) {
return false ;
}
localStorage . setItem ( this . settings . bookKey , JSON . stringify ( this . settings ) ) ;
} ;
EPUBJS . Reader . prototype . unload = function ( ) {
if ( this . settings . restore && localStorage ) {
this . saveSettings ( ) ;
}
} ;
EPUBJS . Reader . prototype . hashChanged = function ( ) {
var hash = window . location . hash . slice ( 1 ) ;
this . rendition . display ( hash ) ;
} ;
EPUBJS . Reader . prototype . selectedRange = function ( cfiRange ) {
var cfiFragment = "#" + cfiRange ;
// Update the History Location
if ( this . settings . history &&
window . location . hash != cfiFragment ) {
// Add CFI fragment to the history
history . pushState ( { } , '' , cfiFragment ) ;
this . currentLocationCfi = cfiRange ;
}
} ;
//-- Enable binding events to reader
RSVP . EventTarget . mixin ( EPUBJS . Reader . prototype ) ;
EPUBJS . reader . BookmarksController = function ( ) {
var reader = this ;
var book = this . book ;
var rendition = this . rendition ;
var $bookmarks = $ ( "#bookmarksView" ) ,
$list = $bookmarks . find ( "#bookmarks" ) ;
var docfrag = document . createDocumentFragment ( ) ;
var show = function ( ) {
$bookmarks . show ( ) ;
} ;
var hide = function ( ) {
$bookmarks . hide ( ) ;
} ;
var counter = 0 ;
var createBookmarkItem = function ( cfi ) {
var listitem = document . createElement ( "li" ) ,
link = document . createElement ( "a" ) ;
listitem . id = "bookmark-" + counter ;
listitem . classList . add ( 'list_item' ) ;
var spineItem = book . spine . get ( cfi ) ;
var tocItem ;
if ( spineItem . index in book . navigation . toc ) {
tocItem = book . navigation . toc [ spineItem . index ] ;
link . textContent = tocItem . label ;
} else {
link . textContent = cfi ;
}
link . href = cfi ;
link . classList . add ( 'bookmark_link' ) ;
link . addEventListener ( "click" , function ( event ) {
var cfi = this . getAttribute ( 'href' ) ;
rendition . display ( cfi ) ;
event . preventDefault ( ) ;
} , false ) ;
listitem . appendChild ( link ) ;
counter ++ ;
return listitem ;
} ;
this . settings . bookmarks . forEach ( function ( cfi ) {
var bookmark = createBookmarkItem ( cfi ) ;
docfrag . appendChild ( bookmark ) ;
} ) ;
$list . append ( docfrag ) ;
this . on ( "reader:bookmarked" , function ( cfi ) {
var item = createBookmarkItem ( cfi ) ;
$list . append ( item ) ;
} ) ;
this . on ( "reader:unbookmarked" , function ( index ) {
var $item = $ ( "#bookmark-" + index ) ;
$item . remove ( ) ;
} ) ;
return {
"show" : show ,
"hide" : hide
} ;
} ;
EPUBJS . reader . ControlsController = function ( book ) {
var reader = this ;
var rendition = this . rendition ;
var $store = $ ( "#store" ) ,
$fullscreen = $ ( "#fullscreen" ) ,
$fullscreenicon = $ ( "#fullscreenicon" ) ,
$cancelfullscreenicon = $ ( "#cancelfullscreenicon" ) ,
$slider = $ ( "#slider" ) ,
$main = $ ( "#main" ) ,
$sidebar = $ ( "#sidebar" ) ,
$settings = $ ( "#setting" ) ,
$bookmark = $ ( "#bookmark" ) ;
/ *
var goOnline = function ( ) {
reader . offline = false ;
// $store.attr("src", $icon.data("save"));
} ;
var goOffline = function ( ) {
reader . offline = true ;
// $store.attr("src", $icon.data("saved"));
} ;
var fullscreen = false ;
book . on ( "book:online" , goOnline ) ;
book . on ( "book:offline" , goOffline ) ;
* /
$slider . on ( "click" , function ( ) {
if ( reader . sidebarOpen ) {
reader . SidebarController . hide ( ) ;
$slider . addClass ( "icon-menu" ) ;
$slider . removeClass ( "icon-right" ) ;
} else {
reader . SidebarController . show ( ) ;
$slider . addClass ( "icon-right" ) ;
$slider . removeClass ( "icon-menu" ) ;
}
} ) ;
if ( typeof screenfull !== 'undefined' ) {
$fullscreen . on ( "click" , function ( ) {
screenfull . toggle ( $ ( '#container' ) [ 0 ] ) ;
} ) ;
if ( screenfull . raw ) {
document . addEventListener ( screenfull . raw . fullscreenchange , function ( ) {
fullscreen = screenfull . isFullscreen ;
if ( fullscreen ) {
$fullscreen
. addClass ( "icon-resize-small" )
. removeClass ( "icon-resize-full" ) ;
} else {
$fullscreen
. addClass ( "icon-resize-full" )
. removeClass ( "icon-resize-small" ) ;
}
} ) ;
}
}
$settings . on ( "click" , function ( ) {
reader . SettingsController . show ( ) ;
} ) ;
$bookmark . on ( "click" , function ( ) {
var cfi = reader . rendition . currentLocation ( ) . start . cfi ;
var bookmarked = reader . isBookmarked ( cfi ) ;
if ( bookmarked === - 1 ) { //-- Add bookmark
reader . addBookmark ( cfi ) ;
$bookmark
. addClass ( "icon-bookmark" )
. removeClass ( "icon-bookmark-empty" ) ;
} else { //-- Remove Bookmark
reader . removeBookmark ( cfi ) ;
$bookmark
. removeClass ( "icon-bookmark" )
. addClass ( "icon-bookmark-empty" ) ;
}
} ) ;
rendition . on ( 'relocated' , function ( location ) {
var cfi = location . start . cfi ;
var cfiFragment = "#" + cfi ;
//-- Check if bookmarked
var bookmarked = reader . isBookmarked ( cfi ) ;
if ( bookmarked === - 1 ) { //-- Not bookmarked
$bookmark
. removeClass ( "icon-bookmark" )
. addClass ( "icon-bookmark-empty" ) ;
} else { //-- Bookmarked
$bookmark
. addClass ( "icon-bookmark" )
. removeClass ( "icon-bookmark-empty" ) ;
}
reader . currentLocationCfi = cfi ;
// Update the History Location
if ( reader . settings . history &&
window . location . hash != cfiFragment ) {
// Add CFI fragment to the history
history . pushState ( { } , '' , cfiFragment ) ;
}
} ) ;
return {
} ;
} ;
EPUBJS . reader . MetaController = function ( meta ) {
var title = meta . title ,
author = meta . creator ;
var $title = $ ( "#book-title" ) ,
$author = $ ( "#chapter-title" ) ,
$dash = $ ( "#title-seperator" ) ;
document . title = title + " – " + author ;
$title . html ( title ) ;
$author . html ( author ) ;
$dash . show ( ) ;
} ;
EPUBJS . reader . NotesController = function ( ) {
var book = this . book ;
var rendition = this . rendition ;
var reader = this ;
var $notesView = $ ( "#notesView" ) ;
var $notes = $ ( "#notes" ) ;
var $text = $ ( "#note-text" ) ;
var $anchor = $ ( "#note-anchor" ) ;
var annotations = reader . settings . annotations ;
var renderer = book . renderer ;
var popups = [ ] ;
var epubcfi = new ePub . CFI ( ) ;
var show = function ( ) {
$notesView . show ( ) ;
} ;
var hide = function ( ) {
$notesView . hide ( ) ;
}
var insertAtPoint = function ( e ) {
var range ;
var textNode ;
var offset ;
var doc = book . renderer . doc ;
var cfi ;
var annotation ;
// standard
if ( doc . caretPositionFromPoint ) {
range = doc . caretPositionFromPoint ( e . clientX , e . clientY ) ;
textNode = range . offsetNode ;
offset = range . offset ;
// WebKit
} else if ( doc . caretRangeFromPoint ) {
range = doc . caretRangeFromPoint ( e . clientX , e . clientY ) ;
textNode = range . startContainer ;
offset = range . startOffset ;
}
if ( textNode . nodeType !== 3 ) {
for ( var i = 0 ; i < textNode . childNodes . length ; i ++ ) {
if ( textNode . childNodes [ i ] . nodeType == 3 ) {
textNode = textNode . childNodes [ i ] ;
break ;
}
}
}
// Find the end of the sentance
offset = textNode . textContent . indexOf ( "." , offset ) ;
if ( offset === - 1 ) {
offset = textNode . length ; // Last item
} else {
offset += 1 ; // After the period
}
cfi = epubcfi . generateCfiFromTextNode ( textNode , offset , book . renderer . currentChapter . cfiBase ) ;
annotation = {
annotatedAt : new Date ( ) ,
anchor : cfi ,
body : $text . val ( )
}
// add to list
reader . addNote ( annotation ) ;
// attach
addAnnotation ( annotation ) ;
placeMarker ( annotation ) ;
// clear
$text . val ( '' ) ;
$anchor . text ( "Attach" ) ;
$text . prop ( "disabled" , false ) ;
rendition . off ( "click" , insertAtPoint ) ;
} ;
var addAnnotation = function ( annotation ) {
var note = document . createElement ( "li" ) ;
var link = document . createElement ( "a" ) ;
note . innerHTML = annotation . body ;
// note.setAttribute("ref", annotation.anchor);
link . innerHTML = " context »" ;
link . href = "#" + annotation . anchor ;
link . onclick = function ( ) {
rendition . display ( annotation . anchor ) ;
return false ;
} ;
note . appendChild ( link ) ;
$notes . append ( note ) ;
} ;
var placeMarker = function ( annotation ) {
var doc = book . renderer . doc ;
var marker = document . createElement ( "span" ) ;
var mark = document . createElement ( "a" ) ;
marker . classList . add ( "footnotesuperscript" , "reader_generated" ) ;
marker . style . verticalAlign = "super" ;
marker . style . fontSize = ".75em" ;
// marker.style.position = "relative";
marker . style . lineHeight = "1em" ;
// mark.style.display = "inline-block";
mark . style . padding = "2px" ;
mark . style . backgroundColor = "#fffa96" ;
mark . style . borderRadius = "5px" ;
mark . style . cursor = "pointer" ;
marker . id = "note-" + EPUBJS . core . uuid ( ) ;
mark . innerHTML = annotations . indexOf ( annotation ) + 1 + "[Reader]" ;
marker . appendChild ( mark ) ;
epubcfi . addMarker ( annotation . anchor , doc , marker ) ;
markerEvents ( marker , annotation . body ) ;
}
var markerEvents = function ( item , txt ) {
var id = item . id ;
var showPop = function ( ) {
var poppos ,
iheight = renderer . height ,
iwidth = renderer . width ,
tip ,
pop ,
maxHeight = 225 ,
itemRect ,
left ,
top ,
pos ;
//-- create a popup with endnote inside of it
if ( ! popups [ id ] ) {
popups [ id ] = document . createElement ( "div" ) ;
popups [ id ] . setAttribute ( "class" , "popup" ) ;
pop _content = document . createElement ( "div" ) ;
popups [ id ] . appendChild ( pop _content ) ;
pop _content . innerHTML = txt ;
pop _content . setAttribute ( "class" , "pop_content" ) ;
renderer . render . document . body . appendChild ( popups [ id ] ) ;
//-- TODO: will these leak memory? - Fred
popups [ id ] . addEventListener ( "mouseover" , onPop , false ) ;
popups [ id ] . addEventListener ( "mouseout" , offPop , false ) ;
//-- Add hide on page change
rendition . on ( "locationChanged" , hidePop , this ) ;
rendition . on ( "locationChanged" , offPop , this ) ;
// chapter.book.on("renderer:chapterDestroy", hidePop, this);
}
pop = popups [ id ] ;
//-- get location of item
itemRect = item . getBoundingClientRect ( ) ;
left = itemRect . left ;
top = itemRect . top ;
//-- show the popup
pop . classList . add ( "show" ) ;
//-- locations of popup
popRect = pop . getBoundingClientRect ( ) ;
//-- position the popup
pop . style . left = left - popRect . width / 2 + "px" ;
pop . style . top = top + "px" ;
//-- Adjust max height
if ( maxHeight > iheight / 2.5 ) {
maxHeight = iheight / 2.5 ;
pop _content . style . maxHeight = maxHeight + "px" ;
}
//-- switch above / below
if ( popRect . height + top >= iheight - 25 ) {
pop . style . top = top - popRect . height + "px" ;
pop . classList . add ( "above" ) ;
} else {
pop . classList . remove ( "above" ) ;
}
//-- switch left
if ( left - popRect . width <= 0 ) {
pop . style . left = left + "px" ;
pop . classList . add ( "left" ) ;
} else {
pop . classList . remove ( "left" ) ;
}
//-- switch right
if ( left + popRect . width / 2 >= iwidth ) {
//-- TEMP MOVE: 300
pop . style . left = left - 300 + "px" ;
popRect = pop . getBoundingClientRect ( ) ;
pop . style . left = left - popRect . width + "px" ;
//-- switch above / below again
if ( popRect . height + top >= iheight - 25 ) {
pop . style . top = top - popRect . height + "px" ;
pop . classList . add ( "above" ) ;
} else {
pop . classList . remove ( "above" ) ;
}
pop . classList . add ( "right" ) ;
} else {
pop . classList . remove ( "right" ) ;
}
}
var onPop = function ( ) {
popups [ id ] . classList . add ( "on" ) ;
}
var offPop = function ( ) {
popups [ id ] . classList . remove ( "on" ) ;
}
var hidePop = function ( ) {
setTimeout ( function ( ) {
popups [ id ] . classList . remove ( "show" ) ;
} , 100 ) ;
}
var openSidebar = function ( ) {
reader . ReaderController . slideOut ( ) ;
show ( ) ;
} ;
item . addEventListener ( "mouseover" , showPop , false ) ;
item . addEventListener ( "mouseout" , hidePop , false ) ;
item . addEventListener ( "click" , openSidebar , false ) ;
}
$anchor . on ( "click" , function ( e ) {
$anchor . text ( "Cancel" ) ;
$text . prop ( "disabled" , "true" ) ;
// listen for selection
rendition . on ( "click" , insertAtPoint ) ;
} ) ;
annotations . forEach ( function ( note ) {
addAnnotation ( note ) ;
} ) ;
/ *
renderer . registerHook ( "beforeChapterDisplay" , function ( callback , renderer ) {
var chapter = renderer . currentChapter ;
annotations . forEach ( function ( note ) {
var cfi = epubcfi . parse ( note . anchor ) ;
if ( cfi . spinePos === chapter . spinePos ) {
try {
placeMarker ( note ) ;
} catch ( e ) {
console . log ( "anchoring failed" , note . anchor ) ;
}
}
} ) ;
callback ( ) ;
} , true ) ;
* /
return {
"show" : show ,
"hide" : hide
} ;
} ;
EPUBJS . reader . ReaderController = function ( book ) {
var $main = $ ( "#main" ) ,
$divider = $ ( "#divider" ) ,
$loader = $ ( "#loader" ) ,
$next = $ ( "#next" ) ,
$prev = $ ( "#prev" ) ;
var reader = this ;
var book = this . book ;
var rendition = this . rendition ;
var slideIn = function ( ) {
var currentPosition = rendition . currentLocation ( ) . start . cfi ;
if ( reader . settings . sidebarReflow ) {
$main . removeClass ( 'single' ) ;
$main . one ( "transitionend" , function ( ) {
rendition . resize ( ) ;
} ) ;
} else {
$main . removeClass ( "closed" ) ;
}
} ;
var slideOut = function ( ) {
var location = rendition . currentLocation ( ) ;
if ( ! location ) {
return ;
}
var currentPosition = location . start . cfi ;
if ( reader . settings . sidebarReflow ) {
$main . addClass ( 'single' ) ;
$main . one ( "transitionend" , function ( ) {
rendition . resize ( ) ;
} ) ;
} else {
$main . addClass ( "closed" ) ;
}
} ;
var showLoader = function ( ) {
$loader . show ( ) ;
hideDivider ( ) ;
} ;
var hideLoader = function ( ) {
$loader . hide ( ) ;
//-- If the book is using spreads, show the divider
// if(book.settings.spreads) {
// showDivider();
// }
} ;
var showDivider = function ( ) {
$divider . addClass ( "show" ) ;
} ;
var hideDivider = function ( ) {
$divider . removeClass ( "show" ) ;
} ;
var keylock = false ;
var arrowKeys = function ( e ) {
if ( e . keyCode == 37 ) {
if ( book . package . metadata . direction === "rtl" ) {
rendition . next ( ) ;
} else {
rendition . prev ( ) ;
}
$prev . addClass ( "active" ) ;
keylock = true ;
setTimeout ( function ( ) {
keylock = false ;
$prev . removeClass ( "active" ) ;
} , 100 ) ;
e . preventDefault ( ) ;
}
if ( e . keyCode == 39 ) {
if ( book . package . metadata . direction === "rtl" ) {
rendition . prev ( ) ;
} else {
rendition . next ( ) ;
}
$next . addClass ( "active" ) ;
keylock = true ;
setTimeout ( function ( ) {
keylock = false ;
$next . removeClass ( "active" ) ;
} , 100 ) ;
e . preventDefault ( ) ;
}
}
document . addEventListener ( 'keydown' , arrowKeys , false ) ;
$next . on ( "click" , function ( e ) {
if ( book . package . metadata . direction === "rtl" ) {
rendition . prev ( ) ;
} else {
rendition . next ( ) ;
}
e . preventDefault ( ) ;
} ) ;
$prev . on ( "click" , function ( e ) {
if ( book . package . metadata . direction === "rtl" ) {
rendition . next ( ) ;
} else {
rendition . prev ( ) ;
}
e . preventDefault ( ) ;
} ) ;
rendition . on ( "layout" , function ( props ) {
if ( props . spread === true ) {
showDivider ( ) ;
} else {
hideDivider ( ) ;
}
} ) ;
rendition . on ( 'relocated' , function ( location ) {
if ( location . atStart ) {
$prev . addClass ( "disabled" ) ;
}
if ( location . atEnd ) {
$next . addClass ( "disabled" ) ;
}
} ) ;
return {
"slideOut" : slideOut ,
"slideIn" : slideIn ,
"showLoader" : showLoader ,
"hideLoader" : hideLoader ,
"showDivider" : showDivider ,
"hideDivider" : hideDivider ,
"arrowKeys" : arrowKeys
} ;
} ;
EPUBJS . reader . SettingsController = function ( ) {
var book = this . book ;
var reader = this ;
var $settings = $ ( "#settings-modal" ) ,
$overlay = $ ( ".overlay" ) ;
var show = function ( ) {
$settings . addClass ( "md-show" ) ;
} ;
var hide = function ( ) {
$settings . removeClass ( "md-show" ) ;
} ;
var $sidebarReflowSetting = $ ( '#sidebarReflow' ) ;
$sidebarReflowSetting . on ( 'click' , function ( ) {
reader . settings . sidebarReflow = ! reader . settings . sidebarReflow ;
} ) ;
$settings . find ( ".closer" ) . on ( "click" , function ( ) {
hide ( ) ;
} ) ;
$overlay . on ( "click" , function ( ) {
hide ( ) ;
} ) ;
return {
"show" : show ,
"hide" : hide
} ;
} ;
EPUBJS . reader . SidebarController = function ( book ) {
var reader = this ;
var $sidebar = $ ( "#sidebar" ) ,
$panels = $ ( "#panels" ) ;
var activePanel = "Toc" ;
var changePanelTo = function ( viewName ) {
var controllerName = viewName + "Controller" ;
if ( activePanel == viewName || typeof reader [ controllerName ] === 'undefined' ) return ;
reader [ activePanel + "Controller" ] . hide ( ) ;
reader [ controllerName ] . show ( ) ;
activePanel = viewName ;
$panels . find ( '.active' ) . removeClass ( "active" ) ;
$panels . find ( "#show-" + viewName ) . addClass ( "active" ) ;
} ;
var getActivePanel = function ( ) {
return activePanel ;
} ;
var show = function ( ) {
reader . sidebarOpen = true ;
reader . ReaderController . slideOut ( ) ;
$sidebar . addClass ( "open" ) ;
}
var hide = function ( ) {
reader . sidebarOpen = false ;
reader . ReaderController . slideIn ( ) ;
$sidebar . removeClass ( "open" ) ;
}
$panels . find ( ".show_view" ) . on ( "click" , function ( event ) {
var view = $ ( this ) . data ( "view" ) ;
changePanelTo ( view ) ;
event . preventDefault ( ) ;
} ) ;
return {
'show' : show ,
'hide' : hide ,
'getActivePanel' : getActivePanel ,
'changePanelTo' : changePanelTo
} ;
} ;
EPUBJS . reader . TocController = function ( toc ) {
var book = this . book ;
var rendition = this . rendition ;
var $list = $ ( "#tocView" ) ,
docfrag = document . createDocumentFragment ( ) ;
var currentChapter = false ;
var generateTocItems = function ( toc , level ) {
var container = document . createElement ( "ul" ) ;
if ( ! level ) level = 1 ;
toc . forEach ( function ( chapter ) {
var listitem = document . createElement ( "li" ) ,
link = document . createElement ( "a" ) ;
toggle = document . createElement ( "a" ) ;
var subitems ;
listitem . id = "toc-" + chapter . id ;
listitem . classList . add ( 'list_item' ) ;
link . textContent = chapter . label ;
link . href = chapter . href ;
link . classList . add ( 'toc_link' ) ;
listitem . appendChild ( link ) ;
if ( chapter . subitems && chapter . subitems . length > 0 ) {
level ++ ;
subitems = generateTocItems ( chapter . subitems , level ) ;
toggle . classList . add ( 'toc_toggle' ) ;
listitem . insertBefore ( toggle , link ) ;
listitem . appendChild ( subitems ) ;
}
container . appendChild ( listitem ) ;
} ) ;
return container ;
} ;
var onShow = function ( ) {
$list . show ( ) ;
} ;
var onHide = function ( ) {
$list . hide ( ) ;
} ;
var chapterChange = function ( e ) {
var id = e . id ,
$item = $list . find ( "#toc-" + id ) ,
$current = $list . find ( ".currentChapter" ) ,
$open = $list . find ( '.openChapter' ) ;
if ( $item . length ) {
if ( $item != $current && $item . has ( currentChapter ) . length > 0 ) {
$current . removeClass ( "currentChapter" ) ;
}
$item . addClass ( "currentChapter" ) ;
// $open.removeClass("openChapter");
$item . parents ( 'li' ) . addClass ( "openChapter" ) ;
}
} ;
rendition . on ( 'renderered' , chapterChange ) ;
var tocitems = generateTocItems ( toc ) ;
docfrag . appendChild ( tocitems ) ;
$list . append ( docfrag ) ;
$list . find ( ".toc_link" ) . on ( "click" , function ( event ) {
var url = this . getAttribute ( 'href' ) ;
event . preventDefault ( ) ;
//-- Provide the Book with the url to show
// The Url must be found in the books manifest
rendition . display ( url ) ;
$list . find ( ".currentChapter" )
. addClass ( "openChapter" )
. removeClass ( "currentChapter" ) ;
$ ( this ) . parent ( 'li' ) . addClass ( "currentChapter" ) ;
} ) ;
$list . find ( ".toc_toggle" ) . on ( "click" , function ( event ) {
var $el = $ ( this ) . parent ( 'li' ) ,
open = $el . hasClass ( "openChapter" ) ;
event . preventDefault ( ) ;
if ( open ) {
$el . removeClass ( "openChapter" ) ;
} else {
$el . addClass ( "openChapter" ) ;
}
} ) ;
return {
"show" : onShow ,
"hide" : onHide
} ;
} ;