import loading from 'loading'; import keyboardnavigation from 'keyboardnavigation'; import dialogHelper from 'dialogHelper'; import dom from 'dom'; import events from 'events'; import 'css!./style'; import 'material-icons'; import 'paper-icon-button-light'; import TableOfContents from './tableOfContents'; export class BookPlayer { constructor() { this.name = 'Book Player'; this.type = 'mediaplayer'; this.id = 'bookplayer'; this.priority = 1; this.onDialogClosed = this.onDialogClosed.bind(this); this.openTableOfContents = this.openTableOfContents.bind(this); this.onWindowKeyUp = this.onWindowKeyUp.bind(this); } play(options) { this._progress = 0; this._loaded = false; loading.show(); const elem = this.createMediaElement(); return this.setCurrentSrc(elem, options); } stop() { this.unbindEvents(); const elem = this._mediaElement; const tocElement = this._tocElement; const rendition = this._rendition; if (elem) { dialogHelper.close(elem); this._mediaElement = null; } if (tocElement) { tocElement.destroy(); this._tocElement = null; } if (rendition) { rendition.destroy(); } // Hide loader in case player was not fully loaded yet loading.hide(); this._cancellationToken.shouldCancel = true; } currentItem() { return this._currentItem; } currentTime() { return this._progress * 1000; } duration() { return 1000; } getBufferedRanges() { return [{ start: 0, end: 10000000 }]; } volume() { return 100; } isMuted() { return false; } paused() { return false; } seekable() { return true; } onWindowKeyUp(e) { const key = keyboardnavigation.getKeyName(e); // TODO: depending on the event this can be the document or the rendition itself const rendition = this._rendition || this; const book = rendition.book; if (this._loaded === false) return; switch (key) { case 'l': case 'ArrowRight': case 'Right': book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next(); break; case 'j': case 'ArrowLeft': case 'Left': book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); break; case 'Escape': if (this._tocElement) { // Close table of contents on ESC if it is open this._tocElement.destroy(); } else { // Otherwise stop the entire book player this.stop(); } break; } } onTouchStart(e) { // TODO: depending on the event this can be the document or the rendition itself const rendition = this._rendition || this; const book = rendition.book; // check that the event is from the book or the document if (!book || this._loaded === false) return; // epubjs stores pages off the screen or something for preloading // get the modulus of the touch event to account for the increased width if (!e.touches || e.touches.length === 0) return; const touch = e.touches[0].clientX % dom.getWindowSize().innerWidth; if (touch < dom.getWindowSize().innerWidth / 2) { book.package.metadata.direction === 'rtl' ? rendition.next() : rendition.prev(); } else { book.package.metadata.direction === 'rtl' ? rendition.prev() : rendition.next(); } } onDialogClosed() { this.stop(); } bindMediaElementEvents() { const elem = this._mediaElement; elem.addEventListener('close', this.onDialogClosed, {once: true}); elem.querySelector('.btnBookplayerExit').addEventListener('click', this.onDialogClosed, {once: true}); elem.querySelector('.btnBookplayerToc').addEventListener('click', this.openTableOfContents); } bindEvents() { this.bindMediaElementEvents(); document.addEventListener('keyup', this.onWindowKeyUp); document.addEventListener('touchstart', this.onTouchStart); // FIXME: I don't really get why document keyup event is not triggered when epub is in focus this._rendition.on('keyup', this.onWindowKeyUp); this._rendition.on('touchstart', this.onTouchStart); } unbindMediaElementEvents() { const elem = this._mediaElement; elem.removeEventListener('close', this.onDialogClosed); elem.querySelector('.btnBookplayerExit').removeEventListener('click', this.onDialogClosed); elem.querySelector('.btnBookplayerToc').removeEventListener('click', this.openTableOfContents); } unbindEvents() { if (this._mediaElement) { this.unbindMediaElementEvents(); } document.removeEventListener('keyup', this.onWindowKeyUp); document.removeEventListener('touchstart', this.onTouchStart); if (this._rendition) { this._rendition.off('keyup', this.onWindowKeyUp); this._rendition.off('touchstart', this.onTouchStart); } } openTableOfContents() { if (this._loaded) { this._tocElement = new TableOfContents(this); } } createMediaElement() { let elem = this._mediaElement; if (elem) { return elem; } elem = document.getElementById('bookPlayer'); if (!elem) { elem = dialogHelper.createDialog({ exitAnimationDuration: 400, size: 'fullscreen', autoFocus: false, scrollY: false, exitAnimation: 'fadeout', removeOnClose: true }); elem.id = 'bookPlayer'; let html = ''; html += '