diff --git a/files_reader/CHANGELOG.md b/files_reader/CHANGELOG.md index ec1893c..2ec3aaf 100644 --- a/files_reader/CHANGELOG.md +++ b/files_reader/CHANGELOG.md @@ -1,8 +1,27 @@ -## Unreleased +## UNRELEASED +### + +## 1.0.0 - 2017-03-15 ### Added - Reader now supports CBR/CBZ ('comics') files - - Book position is saved on server and restored on next invocation + - Book position ('cursor') is saved on server and restored on next invocation + - Default settings (independent of fileid) and file-specific settings are saved and restored + - Bookmarks and annotations (notes) are saved and restored (bookmarks are a type of annotation). + - Full-text search implemented. - Framework to support more file format renderers + - hooks added to remove defaults, settings and annotations/bookmarks for deleted files or users + - epubreader + * night mode now works more reliably + * new 'day mode', ie. user-defined colours + * new font settings: font weight + * column width user-configurable + * new mode: maximize reader area, for small-screen devices + * page turn arrows optional, hidden by default + - cbreader + * supports CBR (rar) and CBZ (zip) archives + * single and double page (spread) mode, auto-adjusts to screen geometry + * optional image enhancement filters + * seamless full screen mode (where browser allows user full control of experience, ie. not on apple) ## 0.8.3 - 2017-02-02 ### Fixed diff --git a/files_reader/TODO b/files_reader/TODO new file mode 100644 index 0000000..7de9cf5 --- /dev/null +++ b/files_reader/TODO @@ -0,0 +1,6 @@ + - add hook to delete book info when books are deleted + - do this for files_opds as well + - for maximize page area, disable two-column when in portrait mode + - maybe always disable two-column in portrait mode + - swipe gestures for open/close sidebar, page turn + - disable wide page turn when placing markers diff --git a/files_reader/appinfo/.info.xml.swp b/files_reader/appinfo/.info.xml.swp new file mode 100644 index 0000000..f424b9d Binary files /dev/null and b/files_reader/appinfo/.info.xml.swp differ diff --git a/files_reader/appinfo/app.php b/files_reader/appinfo/app.php index d28cac0..05dcc5c 100644 --- a/files_reader/appinfo/app.php +++ b/files_reader/appinfo/app.php @@ -4,7 +4,7 @@ * ownCloud - Files_Reader App * * @author Frank de Lange - * @copyright 2015 Frank de Lange + * @copyright 2015 - 2017 Frank de Lange * * This file is licensed under the Affero General Public License version 3 or * later. @@ -12,9 +12,11 @@ namespace OCA\Files_Reader\AppInfo; +use OCP\AppFramework\App; use OCP\Util; -Util::addscript( 'files_reader', 'plugin'); +\OCA\Files_Reader\Hooks::register(); +Util::addscript('files_reader', 'plugin'); if(class_exists('\\OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy')) { $manager = \OC::$server->getContentSecurityPolicyManager(); @@ -24,7 +26,7 @@ if(class_exists('\\OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy')) { $csp->addAllowedScriptDomain('\'self\''); $csp->addAllowedFrameDomain('\'self\''); $csp->addAllowedChildSrcDomain('\'self\''); + $csp->addAllowedFontDomain('\'self\''); $csp->addAllowedImageDomain('blob:'); $manager->addDefaultPolicy($csp); } - diff --git a/files_reader/appinfo/database.xml b/files_reader/appinfo/database.xml new file mode 100644 index 0000000..562acb3 --- /dev/null +++ b/files_reader/appinfo/database.xml @@ -0,0 +1,203 @@ + + + *dbname* + true + false + utf8 + + + *dbprefix*reader_bookmarks + + + + id + integer + true + true + true + true + 8 + + + + + user_id + text + + true + 64 + + + + + file_id + integer + true + true + 11 + + + + + type + text + + true + 32 + + + + + name + text + + true + 512 + + + + + value + text + + true + 512 + + + + + content + text + + false + 4096 + + + + + last_modified + integer + 0 + 8 + false + true + + + + reader_bookmarks_file_id_index + + file_id + + + + + reader_bookmarks_user_id_index + + user_id + + + + + reader_bookmarks_name_index + + name + + + + +
+ + + *dbprefix*reader_preferences + + + + id + integer + true + true + true + true + 8 + + + + + user_id + text + + true + 64 + + + + + file_id + integer + true + true + 11 + + + + + scope + text + + true + 32 + + + + + name + text + + true + 128 + + + + + value + text + + true + 4096 + + + + + last_modified + integer + 0 + 8 + false + true + + + + reader_preferences_file_id_index + + file_id + + + + + reader_preferences_user_id_index + + user_id + + + + + reader_preferences_scope_index + + scope + + + + +
+ +
diff --git a/files_reader/appinfo/info.xml b/files_reader/appinfo/info.xml index 19c0571..ffce3cf 100644 --- a/files_reader/appinfo/info.xml +++ b/files_reader/appinfo/info.xml @@ -2,17 +2,126 @@ files_reader Reader (ebook reader) - An epub-compatible ebook reader based on a pure javascript epub renderer + Files_Reader + A multi-format browser-based ebook reader, supports EPUB and CBR/CBZ - Reader is an ebook reader based on a pure javascript epub renderer. It only works for books formatted according to the epub standard. + - 0.8.4 + 1.0.0 AGPL Frank de Lange @@ -21,14 +130,21 @@ https://github.com/Yetangitu/owncloud-apps/issues https://github.com/Yetangitu/owncloud-apps/files_reader https://raw.githubusercontent.com/Yetangitu/owncloud-apps/master/screenshots/files_reader-1.png - https://raw.githubusercontent.com/Yetangitu/owncloud-apps/master/screenshots/files_reader-2.png https://raw.githubusercontent.com/Yetangitu/owncloud-apps/master/screenshots/files_reader-3.png + https://raw.githubusercontent.com/Yetangitu/owncloud-apps/master/screenshots/photo_2017-03-15_17-22-00.jpg + https://raw.githubusercontent.com/Yetangitu/owncloud-apps/master/screenshots/photo_2017-03-15_17-22-02.jpg files multimedia office + + + - + + pgsql + sqlite + mysql 167127 diff --git a/files_reader/appinfo/routes.php b/files_reader/appinfo/routes.php index bf5901f..cda0696 100644 --- a/files_reader/appinfo/routes.php +++ b/files_reader/appinfo/routes.php @@ -10,9 +10,29 @@ * later. */ -namespace OCA\Files_Reader\AppInfo; - return ['routes' => [ - ['name' => 'display#showReader', 'url' => '/', 'verb' => 'GET'], + // Page + ['name' => 'page#showReader', 'url' => '/', 'verb' => 'GET'], + + // Bookmarks + ['name' => 'bookmark#get_cursor', 'url' => '/bookmark/cursor/{fileId}', 'verb' => 'GET'], + ['name' => 'bookmark#set_cursor', 'url' => '/bookmark/cursor', 'verb' => 'POST'], + ['name' => 'bookmark#delete_cursor', 'url' => '/bookmark/cursor/{fileId}', 'verb' => 'DELETE'], + ['name' => 'bookmark#get', 'url' => '/bookmark/{fileId}/{name}', 'verb' => 'GET', 'defaults' => ['name' => '']], + ['name' => 'bookmark#get', 'url' => '/bookmark/{fileId}/{type}/{name}', 'verb' => 'GET', 'defaults' => ['name' => '']], + ['name' => 'bookmark#set', 'url' => '/bookmark', 'verb' => 'POST'], + ['name' => 'bookmark#delete', 'url' => '/bookmark/{fileId}/{name}', 'verb' => 'DELETE'], + + // Metadata + ['name' => 'metadata#get', 'url' => '/metadata/{fileId}/{name}', 'verb' => 'GET', 'defaults' => ['name' => '']], + ['name' => 'metadata#set', 'url' => '/metadata/{fileId}/{name}/{value}', 'verb' => 'POST'], + + // Preferences + ['name' => 'preference#get_default', 'url' => '/preference/default/{scope}/{name}', 'verb' => 'GET', 'defaults' => ['name' => '']], + ['name' => 'preference#set_default', 'url' => '/preference/default', 'verb' => 'POST'], + ['name' => 'preference#delete_default', 'url' => '/preference/default/{scope}/{name}', 'verb' => 'DELETE'], + ['name' => 'preference#get', 'url' => '/preference/{fileId}/{scope}/{name}', 'verb' => 'GET', 'defaults' => ['name' => '']], + ['name' => 'preference#set', 'url' => '/preference', 'verb' => 'POST'], + ['name' => 'preference#delete', 'url' => '/preference/{fileId}/{scope}/{name}', 'verb' => 'DELETE'], ]]; diff --git a/files_reader/controller/displaycontroller.php b/files_reader/controller/displaycontroller.php deleted file mode 100644 index a600c1f..0000000 --- a/files_reader/controller/displaycontroller.php +++ /dev/null @@ -1,58 +0,0 @@ -urlGenerator = $urlGenerator; - } - - /** - * @PublicPage - * @NoCSRFRequired - * - * @return TemplateResponse - */ - public function showReader() { - $params = [ - 'urlGenerator' => $this->urlGenerator - ]; - - $response = new TemplateResponse($this->appName, 'reader', $params, 'blank'); - - $csp = new ContentSecurityPolicy(); - $csp->addAllowedChildSrcDomain('\'self\''); - $csp->addAllowedScriptDomain('\'self\''); - $csp->addAllowedFrameDomain('\'self\''); - $csp->addAllowedStyleDomain('blob:'); - $csp->addAllowedImageDomain('blob:'); - $response->setContentSecurityPolicy($csp); - - return $response; - } - -} diff --git a/files_reader/css/idevice.css b/files_reader/css/idevice.css deleted file mode 100644 index 888c060..0000000 --- a/files_reader/css/idevice.css +++ /dev/null @@ -1,33 +0,0 @@ -/* For iPad portrait layouts only */ -@media only screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation: portrait) { - #viewer iframe { - width: 460px; - height: 740px; - } -} - /*For iPad landscape layouts only */ -@media only screen and (min-device-width: 481px) and (max-device-width: 1024px) and (orientation: landscape) { - #viewer iframe { - width: 460px; - height: 415px; - } -} -/* For iPhone portrait layouts only */ -@media only screen and (max-device-width: 480px) and (orientation: portrait) { - #viewer { - width: 256px; - height: 432px; - } - #viewer iframe { - width: 256px; - height: 432px; - } -} -/* For iPhone landscape layouts only */ -@media only screen and (max-device-width: 480px) and (orientation: landscape) { - #viewer iframe { - width: 256px; - height: 124px; - } -} - diff --git a/files_reader/css/tooltip.css b/files_reader/css/tooltip.css deleted file mode 100644 index cd538ab..0000000 --- a/files_reader/css/tooltip.css +++ /dev/null @@ -1,31 +0,0 @@ -.tooltip { - position: relative; - top: 0; - left: 0; -} - -*.tooltip:hover span { - opacity: 1; - visibility: visible; -} - -*.tooltip span { - z-index: 10; - padding: 1em; - bottom: 2em; - right: -10em; - width: 50%; - background-color: #222222; - color: #FFFFFF; - height: auto; - border-radius: 0.5em; - opacity: 0; - position:absolute; - visibility: hidden; - word-wrap: break-word; - -webkit-transition: all 0.5s; - -moz-transition: all 0.5s; - -ms-transition: all 0.5s; - -o-transition: all 0.5s; - transition: all 0.5s; -} diff --git a/files_reader/js/plugin.js b/files_reader/js/plugin.js index c92e835..59946aa 100644 --- a/files_reader/js/plugin.js +++ b/files_reader/js/plugin.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Frank de Lange + * Copyright (c) 2015-2017 Frank de Lange * Copyright (c) 2013-2014 Lukas Reschke * * This file is licensed under the Affero General Public License version 3 @@ -9,17 +9,33 @@ * */ + (function(OCA) { - OCA.FilesReader = OCA.FilesReader || {}; + OCA.Files_Reader = OCA.Files_Reader || {}; var isMobile = navigator.userAgent.match(/Mobi/i); var hasTouch = 'ontouchstart' in document.documentElement; + function actionHandler(fileName, mime, context) { + var downloadUrl = ''; + if($('#isPublic').val()) { + var sharingToken = $('#sharingToken').val(); + downloadUrl = OC.generateUrl('/s/{token}/download?files={files}&path={path}', { + token: sharingToken, + files: fileName, + path: context.dir + }); + } else { + downloadUrl = Files.getDownloadUrl(fileName, context.dir); + } + OCA.Files_Reader.Plugin.show(downloadUrl, mime, true); + } + /** - * @namespace OCA.FilesReader.Plugin + * @namespace OCA.Files_Reader.Plugin */ - OCA.FilesReader.Plugin = { + OCA.Files_Reader.Plugin = { /** * @param fileList @@ -51,10 +67,10 @@ * @param downloadUrl * @param isFileList */ - show: function(downloadUrl, isFileList) { + show: function(downloadUrl, mimeType, isFileList) { var self = this; var $iframe; - var viewer = OC.generateUrl('/apps/files_reader/?file={file}', {file: downloadUrl}); + var viewer = OC.generateUrl('/apps/files_reader/?file={file}&type={type}', {file: downloadUrl, type: mimeType}); // launch in new window on mobile and touch devices... if (isMobile || hasTouch) { window.open(viewer, downloadUrl); @@ -86,39 +102,41 @@ _extendFileActions: function(fileActions) { var self = this; fileActions.registerAction({ - name: 'view', - displayName: 'Favorite', + name: 'view-epub', + displayName: 'View', mime: 'application/epub+zip', permissions: OC.PERMISSION_READ, - actionHandler: function(fileName, context) { - var downloadUrl = ''; - if($('#isPublic').val()) { - var sharingToken = $('#sharingToken').val(); - downloadUrl = OC.generateUrl('/s/{token}/download?files={files}&path={path}', { - token: sharingToken, - files: fileName, - path: context.dir - }); - } else { - downloadUrl = Files.getDownloadUrl(fileName, context.dir); - } - self.show(downloadUrl, true); + actionHandler: function(fileName, context){ + return actionHandler(fileName, 'application/epub+zip', context); } }); - fileActions.setDefault('application/epub+zip', 'view'); + fileActions.registerAction({ + name: 'view-cbr', + displayName: 'View', + mime: 'application/x-cbr', + permissions: OC.PERMISSION_READ, + actionHandler: function(fileName, context) { + return actionHandler(fileName, 'application/x-cbr', context); + } + }); + + + fileActions.setDefault('application/epub+zip', 'view-epub'); + fileActions.setDefault('application/x-cbr', 'view-cbr'); } }; })(OCA); -OC.Plugins.register('OCA.Files.FileList', OCA.FilesReader.Plugin); +OC.Plugins.register('OCA.Files.FileList', OCA.Files_Reader.Plugin); // FIXME: Hack for single public file view since it is not attached to the fileslist $(document).ready(function(){ - if ($('#isPublic').val() && $('#mimetype').val() === 'application/epub+zip') { + if ($('#isPublic').val() && ($('#mimetype').val() === 'application/epub+zip'|| $('#mimetype').val() === 'application/x-cbr)')) { var sharingToken = $('#sharingToken').val(); var downloadUrl = OC.generateUrl('/s/{token}/download', {token: sharingToken}); - var viewer = OCA.FilesReader.Plugin; - viewer.show(downloadUrl, false); + var viewer = OCA.Files_Reader.Plugin; + var mime = $('#mimetype').val(); + viewer.show(downloadUrl, mime, false); } }); diff --git a/files_reader/js/ready.js b/files_reader/js/ready.js index cb78e50..868315f 100644 --- a/files_reader/js/ready.js +++ b/files_reader/js/ready.js @@ -1,169 +1,139 @@ -function disableStyles(doc, disable) { - for ( var i=0; i').attr('href', document.getElementsByTagName("base").item(0).href + 'css/idevice.css')); - } + options.session = {}; + options.session.filename = decodeURI($session.data('filename')); + options.session.format = $session.data('filetype'); + options.session.fileId = $session.data('fileid'); + options.session.title = options.session.filename; + options.session.nonce = $session.data('nonce') || ""; + options.session.version = $session.data('version') || ""; + options.session.metadata = $session.data('metadata') || {}; + options.session.annotations = $session.data('annotations') || {}; + options.session.fileId = $session.data('fileid') || ""; + options.session.scope = $session.data('scope') || ""; + options.session.cursor = $session.data('cursor') || {}; + options.session.defaults = $session.data('defaults') || {}; + options.session.preferences = $session.data('preferences') || {}; + options.session.defaults = $session.data('defaults') || {}; + options.session.basePath = $session.data('basepath'); + options.session.downloadLink = $session.data('downloadlink'); - // IE < 11 - if (navigator.userAgent.indexOf("MSIE") != -1) { - EPUBJS.Hooks.register("beforeChapterDisplay").wgxpath = function(callback, renderer){ - wgxpath.install(renderer.render.window); - if(callback) - callback(); - }; - wgxpath.install(window); - } - function nightModeConfig() { - delCSSRule(EPUBJS.nightSheet, EPUBJS.nightSelector); - addCSSRule(EPUBJS.nightSheet, EPUBJS.nightSelector, 'color: ' + EPUBJS.nightModeColor + ' !important; background: ' + EPUBJS.nightModeBackground + ' !important;'); - } + /* functions return jquery promises */ + options.session.getPreference = function(name) { + return $.get(options.session.basePath + "preference/" + options.session.fileId + "/" + options.session.scope + "/" + name); + }; + options.session.setPreference = function(name, value) { + return $.post(options.session.basePath + "preference", + { + "fileId": options.session.fileId, + "scope": options.session.scope, + "name": name, + "value": JSON.stringify(value) + }); + }; + options.session.deletePreference = function(name) { + return $.delete(options.session.basePath + "preference/" + options.session.fileId + "/" + options.session.scope + "/" + name); + }; + options.session.getDefault = function(name) { + return $.get(options.session.basePath + "preference/default/" + options.session.scope + "/" + name); + }; + options.session.setDefault = function(name, value) { + return $.post(options.session.basePath + "preference/default", + { + "scope": options.session.scope, + "name": name, + "value": JSON.stringify(value) + }); + }; + options.session.deleteDefault = function(name) { + return $.delete(options.session.basePath + "preference/default/" + options.session.scope + "/" + name); + }; + options.session.getBookmark = function(name, type) { + return $.get(options.session.basePath + "bookmark/" + options.session.fileId + "/" + type + "/" + name); + }; + options.session.setBookmark = function(name, value, type, content) { + return $.post(options.session.basePath + "bookmark", + { + "fileId": options.session.fileId, + "name": name, + "value": JSON.stringify(value), + "type": type, + "content": JSON.stringify(content) + }); + }; + options.session.deleteBookmark = function(name) { + return $.delete(options.session.basePath + "bookmark/" + options.session.fileId + "/" + name); + }; + options.session.getCursor = function() { + return $.get(options.session.basePath + "bookmark/cursor/" + options.session.fileId); + }; + options.session.setCursor = function(value) { + return $.post(options.session.basePath + "bookmark/cursor", + { + "fileId": options.session.fileId, + "value": JSON.stringify(value) + }); + }; + options.session.deleteCursor = function() { + return $.delete(options.session.basePath + "bookmark/cursor/" + options.session.fileId); + }; - // nightMode - EPUBJS.nightMode = false; - EPUBJS.nightSheet = addStyleSheet(); - EPUBJS.nightSelector = '.night *'; - EPUBJS.nightModeBackground = $('#nightModeBackground').val(); - EPUBJS.nightModeColor = $('#nightModeColor').val(); - addCSSRule(EPUBJS.nightSheet, '.nonight', 'background: initial !important;'); - nightModeConfig(); + switch (type) { + case 'application/epub+zip': + options['contained'] = true; + renderEpub(file, options); + break; + case 'application/x-cbr': + renderCbr(file, options); + break; + default: + console.log(type + ' is not supported by Reader'); + } + + // why is there no standard library function for this? + function getUrlParameter (param) { + var pattern = new RegExp('[?&]'+param+'((=([^&]*))|(?=(&|$)))','i'); + var m = window.location.search.match(pattern); + return m && ( typeof(m[3])==='undefined' ? '' : m[3] ); + } - $('#nightModeBackground').on('change', function() { - EPUBJS.nightModeBackground = $('#nightModeBackground').val(); - nightModeConfig(); - }); + // start epub.js renderer + function renderEpub(file, options) { - $('#nightModeColor').on('change', function() { - EPUBJS.nightModeColor = $('#nightModeColor').val(); - nightModeConfig(); - }); + // some parameters... + EPUBJS.filePath = "vendor/epubjs/"; + EPUBJS.cssPath = "vendor/epubjs/css/"; + EPUBJS.basePath = $('.session').data('basepath'); - console.log(document.getElementById("dllink").value); - var reader = ePubReader(document.getElementById("dllink").value, { contained: true }); + /* device-specific boilerplate */ - // enable night/day mode switch by clicking on the book title/author - // just switching in the "night" class works on some browsers but not on others, hence the trickery with - // setStyle/removeStyle... - $('#metainfo').on('click', function() { - if(EPUBJS.nightMode) { - reader.book.removeStyle("background"); - reader.book.removeStyle("color"); - $("#outerContainer").removeClass("night"); - EPUBJS.nightMode = false; - } else { - reader.book.setStyle("background", EPUBJS.nightModeBackground); - reader.book.setStyle("color", EPUBJS.nightModeColor); - $("#outerContainer").addClass("night"); - EPUBJS.nightMode = true; - } - }); + // IE < 11 + if (navigator.userAgent.indexOf("MSIE") != -1) { + EPUBJS.Hooks.register("beforeChapterDisplay").wgxpath = function(callback, renderer){ + wgxpath.install(renderer.render.window); + if(callback) + callback(); + }; + wgxpath.install(window); + } - // extra-wide page turn area? - $('#touch_nav').on('click', function() { - if ($('#touch_nav').prop('checked')) { - $("#prev").removeClass("touch_nav"); - $("#next").removeClass("touch_nav"); - } else { - $("#prev").addClass("touch_nav"); - $("#next").addClass("touch_nav"); - } - }); - - // user-defined font - EPUBJS.ignore_css = false; - EPUBJS.bookFrame = null; - EPUBJS.user_fontFamily = $('#fontFamily').val(); - EPUBJS.user_fontSize = $('#fontSize').val() + '%'; + var reader = ePubReader(file, options); + } - $('#ignore_css').on('click', function() { - EPUBJS.bookFrame = document.getElementsByTagName('iframe')[0].contentDocument; - if ($('#ignore_css').prop('checked')) { - $('#fontFamily').prop('disabled',false); - $('#fontSize').prop('disabled',false); - EPUBJS.ignore_css = true; - reader.book.setStyle('font-size', EPUBJS.user_fontSize); - reader.book.setStyle('font-family', EPUBJS.user_fontFamily); - } else { - $('#fontFamily').prop('disabled',true); - $('#fontSize').prop('disabled',true); - EPUBJS.ignore_css = false; - reader.book.removeStyle('font-size'); - reader.book.removeStyle('font-family'); - } - disableStyles(EPUBJS.bookFrame, EPUBJS.ignore_css); -; - }); + // start cbr.js renderer + function renderCbr(file, options) { + CBRJS.filePath = "vendor/cbrjs/"; - $('#fontSize').on('change', function() { - EPUBJS.user_fontSize = $('#fontSize').val() + '%'; - $('#font_example').css('font-size', EPUBJS.user_fontSize); - reader.book.setStyle('font-size', EPUBJS.user_fontSize); - }); - $('#fontFamily').on('change', function() { - EPUBJS.user_fontFamily = $('#fontFamily').val(); - $('#font_example').css('font-family', EPUBJS.user_fontFamily); - reader.book.setStyle('font-family', EPUBJS.user_fontFamily); - }); - } + var reader = cbReader(file, options); + } + } }; diff --git a/files_reader/lib/Controller/BookmarkController.php b/files_reader/lib/Controller/BookmarkController.php new file mode 100644 index 0000000..1c6851c --- /dev/null +++ b/files_reader/lib/Controller/BookmarkController.php @@ -0,0 +1,124 @@ +bookmarkService = $bookmarkService; + } + + /** + * @brief return bookmark + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * @param string $name + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function get($fileId, $name, $type=null) { + return $this->bookmarkService->get($fileId, $name, $type); + } + + /** + * @brief write bookmark + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * @param string $name + * @param string $value + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function set($fileId, $name, $value, $type=null, $content=null) { + return $this->bookmarkService->set($fileId, $name, $value, $type, $content); + } + + + /** + * @brief return cursor for $fileId + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function getCursor($fileId) { + return $this->bookmarkService->getCursor($fileId); + } + + /** + * @brief write cursor for $fileId + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * @param string $value + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function setCursor($fileId, $value) { + return $this->bookmarkService->setCursor($fileId, $value); + } + + /** + * @brief delete bookmark + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * @param string name + * + */ + public function delete($fileId, $name) { + return $this->bookmarkService->delete($fileId, $name); + } + + /** + * @brief delete cursor + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * + */ + public function deleteCursor($fileId) { + return $this->bookmarkService->deleteCursor($fileId); + } +} diff --git a/files_reader/lib/Controller/MetadataController.php b/files_reader/lib/Controller/MetadataController.php new file mode 100644 index 0000000..15eafad --- /dev/null +++ b/files_reader/lib/Controller/MetadataController.php @@ -0,0 +1,80 @@ +metadataService = $metadataService; + } + + + /** + * @brief write metadata + * + * @NoAdminRequired + * + * @param int $fileId + * @param string $value + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function setAll($fileId, $value) { + return $this->metadataService->setAll($fileId, $value); + } + + /** + * @brief return metadata item + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param int $fileId + * @param string $name + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function get($fileId, $name) { + return $this->metadataService->get($fileId, $name); + } + + /** + * @brief write metadata item + * + * @NoAdminRequired + * + * @param int $fileId + * @param string $name + * @param string $value + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function set($fileId, $name, $value) { + return $this->metadataService->set($fileId, $name, $value); + } + +} diff --git a/files_reader/lib/Controller/PageController.php b/files_reader/lib/Controller/PageController.php new file mode 100644 index 0000000..c9cb52b --- /dev/null +++ b/files_reader/lib/Controller/PageController.php @@ -0,0 +1,147 @@ +urlGenerator = $urlGenerator; + $this->rootFolder = $rootFolder; + $this->shareManager = $shareManager; + $this->userId = $UserId; + $this->bookmarkService = $bookmarkService; + $this->metadataService = $metadataService; + $this->preferenceService = $preferenceService; + } + + /** + * @PublicPage + * @NoCSRFRequired + * + * @return TemplateResponse + */ + public function showReader() { + $templates= [ + 'application/epub+zip' => 'epubreader', + 'application/x-cbr' => 'cbreader' + ]; + + /** + * $fileInfo = [ + * fileId => null, + * fileName => null, + * fileType => null + * ]; + */ + $fileInfo = $this->getFileInfo($this->request->get['file']); + $fileId = $fileInfo['fileId']; + $type = $this->request->get["type"]; + $scope = $template = $templates[$type]; + + $params = [ + 'urlGenerator' => $this->urlGenerator, + 'downloadLink' => $this->request->get['file'], + 'scope' => $scope, + 'fileId' => $fileInfo['fileId'], + 'fileName' => $fileInfo['fileName'], + 'fileType' => $fileInfo['fileType'], + 'cursor' => $this->toJson($this->bookmarkService->getCursor($fileId)), + 'defaults' => $this->toJson($this->preferenceService->getDefault($scope)), + 'preferences' => $this->toJson($this->preferenceService->get($scope, $fileId)), + 'defaults' => $this->toJson($this->preferenceService->getDefault($scope)), + 'metadata' => $this->toJson($this->metadataService->get($fileId)), + 'annotations' => $this->toJson($this->bookmarkService->get($fileId)) + ]; + + + + $response = new TemplateResponse($this->appName, $template, $params, 'blank'); + + return $response; + } + + /** + * @brief sharing-aware file info retriever + * + * Work around the differences between normal and shared file access + * + * @param string $path path-fragment from url + * @return array + */ + private function getFileInfo($path) { + $count = 0; + $shareToken = preg_replace("/\/index\.php\/s\/([A-Za-z0-9]{15})\/download/", "$1", $path, 1,$count); + if ($count === 1) { + $node = $this->shareManager->getShareByToken($shareToken)->getNode(); + $filePath = $node->getPath(); + $fileId = $node->getId(); + } else { + $filePath = $path; + $fileId = $this->rootFolder->getUserFolder($this->userId) + ->get(explode("/", rawurldecode($this->request->get['file']),4)[3]) + ->getFileInfo() + ->getId(); + } + + return [ + fileName => pathInfo($filePath, PATHINFO_FILENAME), + fileType => strtolower(pathInfo($filePath, PATHINFO_EXTENSION)), + fileId => $fileId + ]; + } + + private function toJson($value) { + return htmlspecialchars(json_encode($value), ENT_QUOTES, 'UTF-8'); + } +} diff --git a/files_reader/lib/Controller/PreferenceController.php b/files_reader/lib/Controller/PreferenceController.php new file mode 100644 index 0000000..e7d6d14 --- /dev/null +++ b/files_reader/lib/Controller/PreferenceController.php @@ -0,0 +1,128 @@ +urlGenerator = $urlGenerator; + $this->preferenceService = $preferenceService; + } + + /** + * @brief return preference for $fileId + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $scope + * @param int $fileId + * @param string $name if null, return all preferences for $scope + $fileId + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function get($scope, $fileId, $name) { + return $this->preferenceService->get($scope, $fileId, $name); + } + + /** + * @brief write preference for $fileId + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $scope + * @param int $fileId + * @param string $name + * @param string $value + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function set($scope, $fileId, $name, $value) { + return $this->preferenceService->set($scope, $fileId, $name, $value); + } + + + /** + * @brief return default preference + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $scope + * @param string $name if null, return all default preferences for scope + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function getDefault($scope, $name) { + return $this->preferenceService->getDefault($scope, $name); + } + + /** + * @brief write default preference + * + * @NoAdminRequired + * @NoCSRFRequired + * + * @param string $scope + * @param string $name + * @param string $value + * + * @return array|\OCP\AppFramework\Http\JSONResponse + */ + public function setDefault($scope, $name, $value) { + return $this->preferenceService->setDefault($scope, $name, $value); + } + + /** + * @brief delete preference + * + * @param string $scope + * @param int $fileId + * @param string $name + * + */ + public function delete($scope, $fileId, $name) { + return $this->preferenceService->delete($scope, $fileId, $name); + } + + /** + * @brief delete default preference + * + * @param $scope + * @param $name + * + */ + public function deleteDefault($scope, $name) { + return $this->preferenceService->deleteDefault($scope, $name); + } +} diff --git a/files_reader/lib/Db/Bookmark.php b/files_reader/lib/Db/Bookmark.php new file mode 100644 index 0000000..0390ab8 --- /dev/null +++ b/files_reader/lib/Db/Bookmark.php @@ -0,0 +1,48 @@ + $this->getId(), + 'userId' => $this->getUserId(), + 'fileId' => $this->getFileId(), + 'type' => $this->getType(), + 'name' => $this->getName(), + 'value' => static::conditional_json_decode($this->getValue()), + 'content' => static::conditional_json_decode($this->getContent()), + 'lastModified' => $this->getLastModified() + ]; + } + + public function toService() { + return [ + 'name' => $this->getName(), + 'type' => $this->getType(), + 'value' => $this->conditional_json_decode($this->getValue()), + 'content' => $this->conditional_json_decode($this->getContent()), + 'lastModified' => $this->getLastModified(), + ]; + } +} + diff --git a/files_reader/lib/Db/BookmarkMapper.php b/files_reader/lib/Db/BookmarkMapper.php new file mode 100644 index 0000000..292db93 --- /dev/null +++ b/files_reader/lib/Db/BookmarkMapper.php @@ -0,0 +1,121 @@ +userId = $UserId; + } + + /** + * @brief get bookmarks for $fileId+$userId(+$name) + * @param $fileId + * @param string $name + * @return array + */ + public function get($fileId, $name, $type=null) { + $sql = "SELECT * FROM `*PREFIX*reader_bookmarks` WHERE file_id=? AND `user_id`=?"; + $args = [ $fileId, $this->userId ]; + if (!(null === $type)) { + $sql .= " AND `type`=?"; + $args[] = $type; + } + if (!(null === $name)) { + $sql .= " AND `name`=?"; + $args[] = $name; + } + + return $this->findEntities($sql, $args); + } + + /** + * @brief write bookmark to database + * + * @param int $fileId + * @param string $name + * @param string $value + * + * @return Bookmark the newly created or updated bookmark + */ + public function set($fileId, $name, $value, $type, $content=null) { + + $result = $this->get($fileId, $name); + + if(empty($result)) { + + // anonymous bookmarks are named after their contents + if (null === $name) { + $name = $value; + } + + // default type is "bookmark" + if (null === $type) { + $type = "bookmark"; + } + + $bookmark = new Bookmark(); + $bookmark->setFileId($fileId); + $bookmark->setUserId($this->userId); + $bookmark->setType($type); + $bookmark->setName($name); + $bookmark->setValue($value); + $bookmark->setContent($content); + + $this->insert($bookmark); + } else { + $bookmark = $result[0]; + $bookmark->setValue($value); + $bookmark->setContent($content); + + $this->update($bookmark); + } + + return $bookmark; + } + + /* currently not used */ + public function deleteForFileId($fileId) { + $sql = "SELECT * FROM `*PREFIX*reader_bookmarks` WHERE file_id=?"; + $args = [ $fileId ]; + array_map( + function($entity) { + $this->delete($entity); + }, $this->findEntities($sql, $args) + ); + } + + /* currently not used */ + public function deleteForUserId($userId) { + $sql = "SELECT * FROM `*PREFIX*reader_bookmarks` WHERE user_id=?"; + $args = [ $userId ]; + array_map( + function($entity) { + $this->delete($entity); + }, $this->findEntities($sql, $args) + ); + } +} + diff --git a/files_reader/lib/Db/Preference.php b/files_reader/lib/Db/Preference.php new file mode 100644 index 0000000..1baf1f6 --- /dev/null +++ b/files_reader/lib/Db/Preference.php @@ -0,0 +1,42 @@ + $this->getId(), + 'scope' => $this->getScope(), + 'fileId' => $this->getFileId(), + 'name' => $this->getName(), + 'value' => $this->conditional_json_decode($this->getValue()), + 'lastModified' => $this->getLastModified(), + ]; + } + + public function toService() { + return [ + 'name' => $this->getName(), + 'value' => $this->conditional_json_decode($this->getValue()), + ]; + } +} + diff --git a/files_reader/lib/Db/PreferenceMapper.php b/files_reader/lib/Db/PreferenceMapper.php new file mode 100644 index 0000000..350ea00 --- /dev/null +++ b/files_reader/lib/Db/PreferenceMapper.php @@ -0,0 +1,105 @@ +userId = $UserId; + } + + /** + * @brief get preferences for $scope+$fileId+$userId(+$name) + * + * @param string $scope + * @param int $fileId + * @param string $name + * @return array + */ + public function get($scope, $fileId, $name=null) { + if(!empty($name)) { + $sql = "SELECT * FROM `*PREFIX*reader_preferences` WHERE `scope`=? AND `file_id`=? AND `user_id`=? AND `name`=?"; + $args = array( + $scope, + $fileId, + $this->userId, + $name); + } else { + $sql = "SELECT * FROM `*PREFIX*reader_preferences` WHERE `scope`=? AND `file_id`=? AND `user_id`=?"; + $args = array( + $scope, + $fileId, + $this->userId); + } + + return $this->findEntities($sql, $args); + } + + /** + * @brief write preference to database + * + * @param string $scope + * @param int $fileId + * @param string $name + * @param string $value + * + * @return Preference the newly created or updated preference + */ + public function set($scope, $fileId, $name, $value) { + + $result = $this->get($scope, $fileId, $name); + + if(empty($result)) { + + $preference = new Preference(); + $preference->setScope($scope); + $preference->setFileId($fileId); + $preference->setUserId($this->userId); + $preference->setName($name); + $preference->setValue($value); + + $this->insert($preference); + } else { + $preference = $result[0]; + $preference->setValue($value); + + $this->update($preference); + } + + return $preference; + } + + /* currently not used*/ + public function deleteForFileId($fileId) { + $sql = "SELECT * FROM `*PREFIX*reader_preferences` WHERE file_id=?"; + $args = [ $fileId ]; + array_map( + function($entity) { + $this->delete($entity); + }, $this->findEntities($sql, $args) + ); + } + + /* currently not used*/ + public function deleteForUserId($userId) { + $sql = "SELECT * FROM `*PREFIX*reader_preferences` WHERE user_id=?"; + $args = [ $userId ]; + array_map( + function($entity) { + $this->delete($entity); + }, $this->findEntities($sql, $args) + ); + } +} diff --git a/files_reader/lib/Db/ReaderEntity.php b/files_reader/lib/Db/ReaderEntity.php new file mode 100644 index 0000000..c6f3a58 --- /dev/null +++ b/files_reader/lib/Db/ReaderEntity.php @@ -0,0 +1,39 @@ + $this->getName(), + 'type' => $this->getType(), + 'value' => $this->conditional_json_decode($this->getValue()), + 'content' => $this->conditional_json_decode($this->getContent()), + 'lastModified' => $this->getLastModified(), + ]; + } +*/ +} + diff --git a/files_reader/lib/Db/ReaderMapper.php b/files_reader/lib/Db/ReaderMapper.php new file mode 100644 index 0000000..efe7bf1 --- /dev/null +++ b/files_reader/lib/Db/ReaderMapper.php @@ -0,0 +1,40 @@ +time = $time; + } + + public function update(Entity $entity) { + $entity->setLastModified($this->time->getMicroTime()); + return parent::update($entity); + } + + public function insert(Entity $entity) { + $entity->setLastModified($this->time->getMicroTime()); + return parent::insert($entity); + } +} diff --git a/files_reader/lib/Hooks.php b/files_reader/lib/Hooks.php new file mode 100644 index 0000000..fe28f96 --- /dev/null +++ b/files_reader/lib/Hooks.php @@ -0,0 +1,53 @@ +getRootFolder()->listen('\OC\Files', 'preDelete', function (Node $node) { + $fileId = $node->getId(); + $connection = \OC::$server->getDatabaseConnection(); + self::deleteFile($connection, $fileId); + }); + \OC::$server->getUserManager()->listen('\OC\User', 'preDelete', function (User $user) { + $userId = $user->getUID(); + $connection = \OC::$server->getDatabaseConnection(); + self::deleteUser($connection, $userId); + }); + } + + protected static function deleteFile(IDBConnection $connection, $fileId) { + $queryBuilder = $connection->getQueryBuilder(); + $queryBuilder->delete('reader_bookmarks','b')->where('b.file_id = :file_id')->setParameter(':file_id', $fileId); + $queryBuilder->execute(); + + $queryBuilder = $connection->getQueryBuilder(); + $queryBuilder->delete('reader_preferences','p')->where('p.file_id = :file_id')->setParameter(':file_id', $fileId); + $queryBuilder->execute(); + } + + protected static function deleteUser(IDBConnection $connection, $userId) { + $queryBuilder = $connection->getQueryBuilder(); + $queryBuilder->delete('reader_bookmarks','b')->where('b.user_id = :user_id')->setParameter(':user_id', $userId); + $queryBuilder->execute(); + + $queryBuilder = $connection->getQueryBuilder(); + $queryBuilder->delete('reader_preferences','p')->where('p.user_id = :user_id')->setParameter(':user_id', $userId); + $queryBuilder->execute(); + } + +} diff --git a/files_reader/lib/Service/BookmarkService.php b/files_reader/lib/Service/BookmarkService.php new file mode 100644 index 0000000..9f147b3 --- /dev/null +++ b/files_reader/lib/Service/BookmarkService.php @@ -0,0 +1,113 @@ +bookmarkMapper = $bookmarkMapper; + $this->userId = $UserId; + } + + /** + * @brief get bookmark + * + * bookmark type is format-dependent, eg CFI for epub, page number for CBR/CBZ, etc + * + * @param int $fileId + * @param string $name + * + * @return array + */ + public function get($fileId, $name=null, $type=null) { + $result = $this->bookmarkMapper->get($fileId, $name, $type); + return array_map( + function($entity) { + return $entity->toService(); + }, $result); + } + + /** + * @brief write bookmark + * + * position type is format-dependent, eg CFI for epub, page number for CBR/CBZ, etc + * + * @param int $fileId + * @param string $name + * @param string $value + * + * @return array + */ + public function set($fileId, $name, $value, $type=null, $content=null) { + return $this->bookmarkMapper->set($fileId, $name, $value, $type, $content); + } + + /** + * @brief get cursor (current position in book) + * + * @param int $fileId + * + * @return array + */ + public function getCursor($fileId) { + $result = $this->get($fileId, static::CURSOR); + if (count($result) === 1) { + return $result[0]; + } + } + + /** + * @brief set cursor (current position in book) + * + * @param int $fileId + * @param string $value + * + * @return array + */ + public function setCursor($fileId, $value) { + return $this->bookmarkMapper->set($fileId, static::CURSOR, $value, static::bookmark_type); + } + + /** + * @brief delete bookmark + * + * @param int $fileId + * @param string $name + * + */ + public function delete($fileId, $name, $type=null) { + foreach ($this->bookmarkMapper->get($fileId, $name, $type) as $bookmark) { + $this->bookmarkMapper->delete($bookmark); + } + } + + /** + * @brief delete cursor + * + * @param int $fileId + * + */ + public function deleteCursor($fileId) { + $this->delete($fileId, static::CURSOR, static::bookmark_type); + } +} + diff --git a/files_reader/lib/Service/MetadataService.php b/files_reader/lib/Service/MetadataService.php new file mode 100644 index 0000000..7bcf930 --- /dev/null +++ b/files_reader/lib/Service/MetadataService.php @@ -0,0 +1,64 @@ + $meta[$name]]; + } else { + return $meta; + } + } + } + + return []; + } + + /** + * @brief write metadata to database + * + * @param int $fileId + * @param array $value + * + * @return array + */ + public function setAll($fileId, $value) { + // no-op for now + return []; + } + + /** + * @brief write metadata item to database + * + * @param int $fileId + * @param string $name + * @param array $value + * + * @return array + */ + public function set($fileId, $name, $value) { + // no-op for now + return []; + } +} + diff --git a/files_reader/lib/Service/PreferenceService.php b/files_reader/lib/Service/PreferenceService.php new file mode 100644 index 0000000..8a86fb2 --- /dev/null +++ b/files_reader/lib/Service/PreferenceService.php @@ -0,0 +1,117 @@ +preferenceMapper = $preferenceMapper; + } + + /** + * @brief get preference + * + * scope identifies preference source, i.e. which renderer the preference applies to + * preference type is format-dependent, eg CFI for epub, page number for CBR/CBZ, etc + * + * @param string $scope + * @param int $fileId + * @param string $name + * + * @return array + */ + public function get($scope, $fileId, $name=null) { + $result = $this->preferenceMapper->get($scope, $fileId, $name); + return array_map( + function($entity) { + return $entity->toService(); + }, $result); + } + + /** + * @brief write preference + * + * scope identifies preference source, i.e. which renderer the preference applies to + * position type is format-dependent, eg CFI for epub, page number for CBR/CBZ, etc + * + * @param string $scope + * @param int $fileId + * @param string $name + * @param string $value + * + * @return array + */ + public function set($scope, $fileId, $name, $value) { + return $this->preferenceMapper->set($scope, $fileId, $name, $value); + } + + /** + * @brief get default preference + * + * @param string $scope + * @param string $name + * + * @return array + */ + public function getDefault($scope, $name=null) { + return $this->get($scope, static::DEFAULTS, $name); + } + + /** + * @brief set default preference + * + * @param string $scope + * @param string $name + * @param string $value + * + * @return array + */ + public function setDefault($scope, $name, $value) { + return $this->preferenceMapper->set($scope, static::DEFAULTS, $name, $value); + } + + /** + * @brief delete preference + * + * @param string $scope + * @param int $fileId + * @param string $name + * + */ + public function delete($scope, $fileId, $name) { + foreach($this->preferenceMapper->get($scope, $fileId, $name) as $preference) { + $this->preferenceMapper->delete($preference); + } + } + + /** + * @brief delete default + * + * @param string $scope + * @param string $name + * + */ + public function deleteDefault($scope, $name) { + $this->delete($scope, static::DEFAULTS, $name); + } +} diff --git a/files_reader/lib/Service/Service.php b/files_reader/lib/Service/Service.php new file mode 100644 index 0000000..42c6caf --- /dev/null +++ b/files_reader/lib/Service/Service.php @@ -0,0 +1,25 @@ +mapper = $mapper; + } +} + + + diff --git a/files_reader/lib/Utility/Time.php b/files_reader/lib/Utility/Time.php new file mode 100644 index 0000000..71fe633 --- /dev/null +++ b/files_reader/lib/Utility/Time.php @@ -0,0 +1,27 @@ +getContentSecurityPolicyNonceManager()->getNonce() + : 'nonce_not_implemented'; +?> + + +' data-scope='' data-cursor='' data-defaults='' data-preferences='' data-metadata='' data-annotations=''> + + + + + + <?php p($_['title']);?> + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+ +
+   / +
+ +
+
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + +
+
+ + +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/files_reader/templates/epubreader.php b/files_reader/templates/epubreader.php new file mode 100644 index 0000000..15f92de --- /dev/null +++ b/files_reader/templates/epubreader.php @@ -0,0 +1,299 @@ +getContentSecurityPolicyNonceManager()->getNonce() + : 'nonce_not_implemented'; +?> + + +' data-scope='' data-cursor='' data-defaults='' data-preferences='' data-metadata='' data-annotations=''> + + + + + + <?php p($_['title']);?> + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + + +
+ +
+ + + +   –   + + + +
+ +
+ + + + + +
+
+ + + + + + +
+
+ +
+ +
+ + + +
+ + + +
+
+
+ + diff --git a/files_reader/templates/reader.php b/files_reader/templates/reader.php deleted file mode 100644 index 0b900ee..0000000 --- a/files_reader/templates/reader.php +++ /dev/null @@ -1,197 +0,0 @@ -getContentSecurityPolicyNonceManager()->getNonce() - : 'nonce_not_implemented'; -?> - - - - - - - - - <?php p($title);?> - - - - - - - - - - - - - - - - - - - - - -
- -
- -
-
- -
-
- -
- -
-
- -
-
-
- - diff --git a/files_reader/vendor/bartaz/jquery.highlight.js b/files_reader/vendor/bartaz/jquery.highlight.js new file mode 100644 index 0000000..9dcf3c7 --- /dev/null +++ b/files_reader/vendor/bartaz/jquery.highlight.js @@ -0,0 +1,108 @@ +/* + * jQuery Highlight plugin + * + * Based on highlight v3 by Johann Burkard + * http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html + * + * Code a little bit refactored and cleaned (in my humble opinion). + * Most important changes: + * - has an option to highlight only entire words (wordsOnly - false by default), + * - has an option to be case sensitive (caseSensitive - false by default) + * - highlight element tag and class names can be specified in options + * + * Usage: + * // wrap every occurrance of text 'lorem' in content + * // with (default options) + * $('#content').highlight('lorem'); + * + * // search for and highlight more terms at once + * // so you can save some time on traversing DOM + * $('#content').highlight(['lorem', 'ipsum']); + * $('#content').highlight('lorem ipsum'); + * + * // search only for entire word 'lorem' + * $('#content').highlight('lorem', { wordsOnly: true }); + * + * // don't ignore case during search of term 'lorem' + * $('#content').highlight('lorem', { caseSensitive: true }); + * + * // wrap every occurrance of term 'ipsum' in content + * // with + * $('#content').highlight('ipsum', { element: 'em', className: 'important' }); + * + * // remove default highlight + * $('#content').unhighlight(); + * + * // remove custom highlight + * $('#content').unhighlight({ element: 'em', className: 'important' }); + * + * + * Copyright (c) 2009 Bartek Szopka + * + * Licensed under MIT license. + * + */ + +jQuery.extend({ + highlight: function (node, re, nodeName, className) { + if (node.nodeType === 3) { + var match = node.data.match(re); + if (match) { + var highlight = document.createElement(nodeName || 'span'); + highlight.className = className || 'highlight'; + var wordNode = node.splitText(match.index); + wordNode.splitText(match[0].length); + var wordClone = wordNode.cloneNode(true); + highlight.appendChild(wordClone); + wordNode.parentNode.replaceChild(highlight, wordNode); + return 1; //skip added node in parent + } + } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children + !/(script|style)/i.test(node.tagName) && // ignore script and style nodes + !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted + for (var i = 0; i < node.childNodes.length; i++) { + i += jQuery.highlight(node.childNodes[i], re, nodeName, className); + } + } + return 0; + } +}); + +jQuery.fn.unhighlight = function (options) { + var settings = { className: 'highlight', element: 'span' }; + jQuery.extend(settings, options); + + return this.find(settings.element + "." + settings.className).each(function () { + var parent = this.parentNode; + parent.replaceChild(this.firstChild, this); + parent.normalize(); + }).end(); +}; + +jQuery.fn.highlight = function (words, options) { + var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false }; + jQuery.extend(settings, options); + + if (words.constructor === String) { + words = [words]; + } + words = jQuery.grep(words, function(word, i){ + return word != ''; + }); + words = jQuery.map(words, function(word, i) { + return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"); + }); + if (words.length == 0) { return this; }; + + var flag = settings.caseSensitive ? "" : "i"; + var pattern = "(" + words.join("|") + ")"; + if (settings.wordsOnly) { + pattern = "\\b" + pattern + "\\b"; + } + var re = new RegExp(pattern, flag); + + return this.each(function () { + jQuery.highlight(this, re, settings.element, settings.className); + }); +}; + diff --git a/files_reader/vendor/bgrins/spectrum.css b/files_reader/vendor/bgrins/spectrum.css new file mode 100644 index 0000000..a8ad9e4 --- /dev/null +++ b/files_reader/vendor/bgrins/spectrum.css @@ -0,0 +1,507 @@ +/*** +Spectrum Colorpicker v1.8.0 +https://github.com/bgrins/spectrum +Author: Brian Grinstead +License: MIT +***/ + +.sp-container { + position:absolute; + top:0; + left:0; + display:inline-block; + *display: inline; + *zoom: 1; + /* https://github.com/bgrins/spectrum/issues/40 */ + z-index: 9999994; + overflow: hidden; +} +.sp-container.sp-flat { + position: relative; +} + +/* Fix for * { box-sizing: border-box; } */ +.sp-container, +.sp-container * { + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; +} + +/* http://ansciath.tumblr.com/post/7347495869/css-aspect-ratio */ +.sp-top { + position:relative; + width: 100%; + display:inline-block; +} +.sp-top-inner { + position:absolute; + top:0; + left:0; + bottom:0; + right:0; +} +.sp-color { + position: absolute; + top:0; + left:0; + bottom:0; + right:20%; +} +.sp-hue { + position: absolute; + top:0; + right:0; + bottom:0; + left:84%; + height: 100%; +} + +.sp-clear-enabled .sp-hue { + top:33px; + height: 77.5%; +} + +.sp-fill { + padding-top: 80%; +} +.sp-sat, .sp-val { + position: absolute; + top:0; + left:0; + right:0; + bottom:0; +} + +.sp-alpha-enabled .sp-top { + margin-bottom: 18px; +} +.sp-alpha-enabled .sp-alpha { + display: block; +} +.sp-alpha-handle { + position:absolute; + top:-4px; + bottom: -4px; + width: 6px; + left: 50%; + cursor: pointer; + border: 1px solid black; + background: white; + opacity: .8; +} +.sp-alpha { + display: none; + position: absolute; + bottom: -14px; + right: 0; + left: 0; + height: 8px; +} +.sp-alpha-inner { + border: solid 1px #333; +} + +.sp-clear { + display: none; +} + +.sp-clear.sp-clear-display { + background-position: center; +} + +.sp-clear-enabled .sp-clear { + display: block; + position:absolute; + top:0px; + right:0; + bottom:0; + left:84%; + height: 28px; +} + +/* Don't allow text selection */ +.sp-container, .sp-replacer, .sp-preview, .sp-dragger, .sp-slider, .sp-alpha, .sp-clear, .sp-alpha-handle, .sp-container.sp-dragging .sp-input, .sp-container button { + -webkit-user-select:none; + -moz-user-select: -moz-none; + -o-user-select:none; + user-select: none; +} + +.sp-container.sp-input-disabled .sp-input-container { + display: none; +} +.sp-container.sp-buttons-disabled .sp-button-container { + display: none; +} +.sp-container.sp-palette-buttons-disabled .sp-palette-button-container { + display: none; +} +.sp-palette-only .sp-picker-container { + display: none; +} +.sp-palette-disabled .sp-palette-container { + display: none; +} + +.sp-initial-disabled .sp-initial { + display: none; +} + + +/* Gradients for hue, saturation and value instead of images. Not pretty... but it works */ +.sp-sat { + background-image: -webkit-gradient(linear, 0 0, 100% 0, from(#FFF), to(rgba(204, 154, 129, 0))); + background-image: -webkit-linear-gradient(left, #FFF, rgba(204, 154, 129, 0)); + background-image: -moz-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: -o-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: -ms-linear-gradient(left, #fff, rgba(204, 154, 129, 0)); + background-image: linear-gradient(to right, #fff, rgba(204, 154, 129, 0)); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr=#FFFFFFFF, endColorstr=#00CC9A81)"; + filter : progid:DXImageTransform.Microsoft.gradient(GradientType = 1, startColorstr='#FFFFFFFF', endColorstr='#00CC9A81'); +} +.sp-val { + background-image: -webkit-gradient(linear, 0 100%, 0 0, from(#000000), to(rgba(204, 154, 129, 0))); + background-image: -webkit-linear-gradient(bottom, #000000, rgba(204, 154, 129, 0)); + background-image: -moz-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: -o-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: -ms-linear-gradient(bottom, #000, rgba(204, 154, 129, 0)); + background-image: linear-gradient(to top, #000, rgba(204, 154, 129, 0)); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#00CC9A81, endColorstr=#FF000000)"; + filter : progid:DXImageTransform.Microsoft.gradient(startColorstr='#00CC9A81', endColorstr='#FF000000'); +} + +.sp-hue { + background: -moz-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -ms-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -o-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: -webkit-gradient(linear, left top, left bottom, from(#ff0000), color-stop(0.17, #ffff00), color-stop(0.33, #00ff00), color-stop(0.5, #00ffff), color-stop(0.67, #0000ff), color-stop(0.83, #ff00ff), to(#ff0000)); + background: -webkit-linear-gradient(top, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); + background: linear-gradient(to bottom, #ff0000 0%, #ffff00 17%, #00ff00 33%, #00ffff 50%, #0000ff 67%, #ff00ff 83%, #ff0000 100%); +} + +/* IE filters do not support multiple color stops. + Generate 6 divs, line them up, and do two color gradients for each. + Yes, really. + */ +.sp-1 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0000', endColorstr='#ffff00'); +} +.sp-2 { + height:16%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffff00', endColorstr='#00ff00'); +} +.sp-3 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ff00', endColorstr='#00ffff'); +} +.sp-4 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffff', endColorstr='#0000ff'); +} +.sp-5 { + height:16%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0000ff', endColorstr='#ff00ff'); +} +.sp-6 { + height:17%; + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff00ff', endColorstr='#ff0000'); +} + +.sp-hidden { + display: none !important; +} + +/* Clearfix hack */ +.sp-cf:before, .sp-cf:after { content: ""; display: table; } +.sp-cf:after { clear: both; } +.sp-cf { *zoom: 1; } + +/* Mobile devices, make hue slider bigger so it is easier to slide */ +@media (max-device-width: 480px) { + .sp-color { right: 40%; } + .sp-hue { left: 63%; } + .sp-fill { padding-top: 60%; } +} +.sp-dragger { + border-radius: 5px; + height: 5px; + width: 5px; + border: 1px solid #fff; + background: #000; + cursor: pointer; + position:absolute; + top:0; + left: 0; +} +.sp-slider { + position: absolute; + top:0; + cursor:pointer; + height: 3px; + left: -1px; + right: -1px; + border: 1px solid #000; + background: white; + opacity: .8; +} + +/* +Theme authors: +Here are the basic themeable display options (colors, fonts, global widths). +See http://bgrins.github.io/spectrum/themes/ for instructions. +*/ + +.sp-container { + border-radius: 0; + background-color: #ECECEC; + border: solid 1px #f0c49B; + padding: 0; +} +.sp-container, .sp-container button, .sp-container input, .sp-color, .sp-hue, .sp-clear { + font: normal 12px "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + box-sizing: border-box; +} +.sp-top { + margin-bottom: 3px; +} +.sp-color, .sp-hue, .sp-clear { + border: solid 1px #666; +} + +/* Input */ +.sp-input-container { + float:right; + width: 100px; + margin-bottom: 4px; +} +.sp-initial-disabled .sp-input-container { + width: 100%; +} +.sp-input { + font-size: 12px !important; + border: 1px inset; + padding: 4px 5px; + margin: 0; + width: 100%; + background:transparent; + border-radius: 3px; + color: #222; +} +.sp-input:focus { + border: 1px solid orange; +} +.sp-input.sp-validation-error { + border: 1px solid red; + background: #fdd; +} +.sp-picker-container , .sp-palette-container { + float:left; + position: relative; + padding: 10px; + padding-bottom: 300px; + margin-bottom: -290px; +} +.sp-picker-container { + width: 172px; + border-left: solid 1px #fff; +} + +/* Palettes */ +.sp-palette-container { + border-right: solid 1px #ccc; +} + +.sp-palette-only .sp-palette-container { + border: 0; +} + +.sp-palette .sp-thumb-el { + display: block; + position:relative; + float:left; + width: 24px; + height: 15px; + margin: 3px; + cursor: pointer; + border:solid 2px transparent; +} +.sp-palette .sp-thumb-el:hover, .sp-palette .sp-thumb-el.sp-thumb-active { + border-color: orange; +} +.sp-thumb-el { + position:relative; +} + +/* Initial */ +.sp-initial { + float: left; + border: solid 1px #333; +} +.sp-initial span { + width: 30px; + height: 25px; + border:none; + display:block; + float:left; + margin:0; +} + +.sp-initial .sp-clear-display { + background-position: center; +} + +/* Buttons */ +.sp-palette-button-container, +.sp-button-container { + float: right; +} + +/* Replacer (the little preview div that shows up instead of the ) */ +.sp-replacer { + margin:0; + overflow:hidden; + cursor:pointer; + padding: 4px; + display:inline-block; + *zoom: 1; + *display: inline; + border: solid 1px #91765d; + background: #eee; + color: #333; + vertical-align: middle; +} +.sp-replacer:hover, .sp-replacer.sp-active { + border-color: #F0C49B; + color: #111; +} +.sp-replacer.sp-disabled { + cursor:default; + border-color: silver; + color: silver; +} +.sp-dd { + padding: 2px 0; + height: 16px; + line-height: 16px; + float:left; + font-size:10px; +} +.sp-preview { + position:relative; + width:25px; + height: 20px; + border: solid 1px #222; + margin-right: 5px; + float:left; + z-index: 0; +} + +.sp-palette { + *width: 220px; + max-width: 220px; +} +.sp-palette .sp-thumb-el { + width:16px; + height: 16px; + margin:2px 1px; + border: solid 1px #d0d0d0; +} + +.sp-container { + padding-bottom:0; +} + + +/* Buttons: http://hellohappy.org/css3-buttons/ */ +.sp-container button { + background-color: #eeeeee; + background-image: -webkit-linear-gradient(top, #eeeeee, #cccccc); + background-image: -moz-linear-gradient(top, #eeeeee, #cccccc); + background-image: -ms-linear-gradient(top, #eeeeee, #cccccc); + background-image: -o-linear-gradient(top, #eeeeee, #cccccc); + background-image: linear-gradient(to bottom, #eeeeee, #cccccc); + border: 1px solid #ccc; + border-bottom: 1px solid #bbb; + border-radius: 3px; + color: #333; + font-size: 14px; + line-height: 1; + padding: 5px 4px; + text-align: center; + text-shadow: 0 1px 0 #eee; + vertical-align: middle; +} +.sp-container button:hover { + background-color: #dddddd; + background-image: -webkit-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -moz-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -ms-linear-gradient(top, #dddddd, #bbbbbb); + background-image: -o-linear-gradient(top, #dddddd, #bbbbbb); + background-image: linear-gradient(to bottom, #dddddd, #bbbbbb); + border: 1px solid #bbb; + border-bottom: 1px solid #999; + cursor: pointer; + text-shadow: 0 1px 0 #ddd; +} +.sp-container button:active { + border: 1px solid #aaa; + border-bottom: 1px solid #888; + -webkit-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -moz-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -ms-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + -o-box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; + box-shadow: inset 0 0 5px 2px #aaaaaa, 0 1px 0 0 #eeeeee; +} +.sp-cancel { + font-size: 11px; + color: #d93f3f !important; + margin:0; + padding:2px; + margin-right: 5px; + vertical-align: middle; + text-decoration:none; + +} +.sp-cancel:hover { + color: #d93f3f !important; + text-decoration: underline; +} + + +.sp-palette span:hover, .sp-palette span.sp-thumb-active { + border-color: #000; +} + +.sp-preview, .sp-alpha, .sp-thumb-el { + position:relative; + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAIAAADZF8uwAAAAGUlEQVQYV2M4gwH+YwCGIasIUwhT25BVBADtzYNYrHvv4gAAAABJRU5ErkJggg==); +} +.sp-preview-inner, .sp-alpha-inner, .sp-thumb-inner { + display:block; + position:absolute; + top:0;left:0;bottom:0;right:0; +} + +.sp-palette .sp-thumb-inner { + background-position: 50% 50%; + background-repeat: no-repeat; +} + +.sp-palette .sp-thumb-light.sp-thumb-active .sp-thumb-inner { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAIVJREFUeNpiYBhsgJFMffxAXABlN5JruT4Q3wfi/0DsT64h8UD8HmpIPCWG/KemIfOJCUB+Aoacx6EGBZyHBqI+WsDCwuQ9mhxeg2A210Ntfo8klk9sOMijaURm7yc1UP2RNCMbKE9ODK1HM6iegYLkfx8pligC9lCD7KmRof0ZhjQACDAAceovrtpVBRkAAAAASUVORK5CYII=); +} + +.sp-palette .sp-thumb-dark.sp-thumb-active .sp-thumb-inner { + background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAAadEVYdFNvZnR3YXJlAFBhaW50Lk5FVCB2My41LjEwMPRyoQAAAMdJREFUOE+tkgsNwzAMRMugEAahEAahEAZhEAqlEAZhEAohEAYh81X2dIm8fKpEspLGvudPOsUYpxE2BIJCroJmEW9qJ+MKaBFhEMNabSy9oIcIPwrB+afvAUFoK4H0tMaQ3XtlrggDhOVVMuT4E5MMG0FBbCEYzjYT7OxLEvIHQLY2zWwQ3D+9luyOQTfKDiFD3iUIfPk8VqrKjgAiSfGFPecrg6HN6m/iBcwiDAo7WiBeawa+Kwh7tZoSCGLMqwlSAzVDhoK+6vH4G0P5wdkAAAAASUVORK5CYII=); +} + +.sp-clear-display { + background-repeat:no-repeat; + background-position: center; + background-image: url(data:image/gif;base64,R0lGODlhFAAUAPcAAAAAAJmZmZ2dnZ6enqKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq/Hx8fLy8vT09PX19ff39/j4+Pn5+fr6+vv7+wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAP8ALAAAAAAUABQAAAihAP9FoPCvoMGDBy08+EdhQAIJCCMybCDAAYUEARBAlFiQQoMABQhKUJBxY0SPICEYHBnggEmDKAuoPMjS5cGYMxHW3IiT478JJA8M/CjTZ0GgLRekNGpwAsYABHIypcAgQMsITDtWJYBR6NSqMico9cqR6tKfY7GeBCuVwlipDNmefAtTrkSzB1RaIAoXodsABiZAEFB06gIBWC1mLVgBa0AAOw==); +} diff --git a/files_reader/vendor/bgrins/spectrum.js b/files_reader/vendor/bgrins/spectrum.js new file mode 100644 index 0000000..7200978 --- /dev/null +++ b/files_reader/vendor/bgrins/spectrum.js @@ -0,0 +1,2323 @@ +// Spectrum Colorpicker v1.8.0 +// https://github.com/bgrins/spectrum +// Author: Brian Grinstead +// License: MIT + +(function (factory) { + "use strict"; + + if (typeof define === 'function' && define.amd) { // AMD + define(['jquery'], factory); + } + else if (typeof exports == "object" && typeof module == "object") { // CommonJS + module.exports = factory(require('jquery')); + } + else { // Browser + factory(jQuery); + } +})(function($, undefined) { + "use strict"; + + var defaultOpts = { + + // Callbacks + beforeShow: noop, + move: noop, + change: noop, + show: noop, + hide: noop, + + // Options + color: false, + flat: false, + showInput: false, + allowEmpty: false, + showButtons: true, + clickoutFiresChange: true, + showInitial: false, + showPalette: false, + showPaletteOnly: false, + hideAfterPaletteSelect: false, + togglePaletteOnly: false, + showSelectionPalette: true, + localStorageKey: false, + appendTo: "body", + maxSelectionSize: 7, + cancelText: "cancel", + chooseText: "choose", + togglePaletteMoreText: "more", + togglePaletteLessText: "less", + clearText: "Clear Color Selection", + noColorSelectedText: "No Color Selected", + preferredFormat: false, + className: "", // Deprecated - use containerClassName and replacerClassName instead. + containerClassName: "", + replacerClassName: "", + showAlpha: false, + theme: "sp-light", + palette: [["#ffffff", "#000000", "#ff0000", "#ff8000", "#ffff00", "#008000", "#0000ff", "#4b0082", "#9400d3"]], + selectionPalette: [], + disabled: false, + offset: null + }, + spectrums = [], + IE = !!/msie/i.exec( window.navigator.userAgent ), + rgbaSupport = (function() { + function contains( str, substr ) { + return !!~('' + str).indexOf(substr); + } + + var elem = document.createElement('div'); + var style = elem.style; + style.cssText = 'background-color:rgba(0,0,0,.5)'; + return contains(style.backgroundColor, 'rgba') || contains(style.backgroundColor, 'hsla'); + })(), + replaceInput = [ + "
", + "
", + "
", + "
" + ].join(''), + markup = (function () { + + // IE does not support gradients with multiple stops, so we need to simulate + // that for the rainbow slider with 8 divs that each have a single gradient + var gradientFix = ""; + if (IE) { + for (var i = 1; i <= 6; i++) { + gradientFix += "
"; + } + } + + return [ + "
", + "
", + "
", + "
", + "", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + "
", + gradientFix, + "
", + "
", + "
", + "
", + "
", + "", + "
", + "
", + "
", + "", + "", + "
", + "
", + "
" + ].join(""); + })(); + + function paletteTemplate (p, color, className, opts) { + var html = []; + for (var i = 0; i < p.length; i++) { + var current = p[i]; + if(current) { + var tiny = tinycolor(current); + var c = tiny.toHsl().l < 0.5 ? "sp-thumb-el sp-thumb-dark" : "sp-thumb-el sp-thumb-light"; + c += (tinycolor.equals(color, current)) ? " sp-thumb-active" : ""; + var formattedString = tiny.toString(opts.preferredFormat || "rgb"); + var swatchStyle = rgbaSupport ? ("background-color:" + tiny.toRgbString()) : "filter:" + tiny.toFilter(); + html.push(''); + } else { + var cls = 'sp-clear-display'; + html.push($('
') + .append($('') + .attr('title', opts.noColorSelectedText) + ) + .html() + ); + } + } + return "
" + html.join('') + "
"; + } + + function hideAll() { + for (var i = 0; i < spectrums.length; i++) { + if (spectrums[i]) { + spectrums[i].hide(); + } + } + } + + function instanceOptions(o, callbackContext) { + var opts = $.extend({}, defaultOpts, o); + opts.callbacks = { + 'move': bind(opts.move, callbackContext), + 'change': bind(opts.change, callbackContext), + 'show': bind(opts.show, callbackContext), + 'hide': bind(opts.hide, callbackContext), + 'beforeShow': bind(opts.beforeShow, callbackContext) + }; + + return opts; + } + + function spectrum(element, o) { + + var opts = instanceOptions(o, element), + flat = opts.flat, + showSelectionPalette = opts.showSelectionPalette, + localStorageKey = opts.localStorageKey, + theme = opts.theme, + callbacks = opts.callbacks, + resize = throttle(reflow, 10), + visible = false, + isDragging = false, + dragWidth = 0, + dragHeight = 0, + dragHelperHeight = 0, + slideHeight = 0, + slideWidth = 0, + alphaWidth = 0, + alphaSlideHelperWidth = 0, + slideHelperHeight = 0, + currentHue = 0, + currentSaturation = 0, + currentValue = 0, + currentAlpha = 1, + palette = [], + paletteArray = [], + paletteLookup = {}, + selectionPalette = opts.selectionPalette.slice(0), + maxSelectionSize = opts.maxSelectionSize, + draggingClass = "sp-dragging", + shiftMovementDirection = null; + + var doc = element.ownerDocument, + body = doc.body, + boundElement = $(element), + disabled = false, + container = $(markup, doc).addClass(theme), + pickerContainer = container.find(".sp-picker-container"), + dragger = container.find(".sp-color"), + dragHelper = container.find(".sp-dragger"), + slider = container.find(".sp-hue"), + slideHelper = container.find(".sp-slider"), + alphaSliderInner = container.find(".sp-alpha-inner"), + alphaSlider = container.find(".sp-alpha"), + alphaSlideHelper = container.find(".sp-alpha-handle"), + textInput = container.find(".sp-input"), + paletteContainer = container.find(".sp-palette"), + initialColorContainer = container.find(".sp-initial"), + cancelButton = container.find(".sp-cancel"), + clearButton = container.find(".sp-clear"), + chooseButton = container.find(".sp-choose"), + toggleButton = container.find(".sp-palette-toggle"), + isInput = boundElement.is("input"), + isInputTypeColor = isInput && boundElement.attr("type") === "color" && inputTypeColorSupport(), + shouldReplace = isInput && !flat, + replacer = (shouldReplace) ? $(replaceInput).addClass(theme).addClass(opts.className).addClass(opts.replacerClassName) : $([]), + offsetElement = (shouldReplace) ? replacer : boundElement, + previewElement = replacer.find(".sp-preview-inner"), + initialColor = opts.color || (isInput && boundElement.val()), + colorOnShow = false, + currentPreferredFormat = opts.preferredFormat, + clickoutFiresChange = !opts.showButtons || opts.clickoutFiresChange, + isEmpty = !initialColor, + allowEmpty = opts.allowEmpty && !isInputTypeColor; + + function applyOptions() { + + if (opts.showPaletteOnly) { + opts.showPalette = true; + } + + toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText); + + if (opts.palette) { + palette = opts.palette.slice(0); + paletteArray = $.isArray(palette[0]) ? palette : [palette]; + paletteLookup = {}; + for (var i = 0; i < paletteArray.length; i++) { + for (var j = 0; j < paletteArray[i].length; j++) { + var rgb = tinycolor(paletteArray[i][j]).toRgbString(); + paletteLookup[rgb] = true; + } + } + } + + container.toggleClass("sp-flat", flat); + container.toggleClass("sp-input-disabled", !opts.showInput); + container.toggleClass("sp-alpha-enabled", opts.showAlpha); + container.toggleClass("sp-clear-enabled", allowEmpty); + container.toggleClass("sp-buttons-disabled", !opts.showButtons); + container.toggleClass("sp-palette-buttons-disabled", !opts.togglePaletteOnly); + container.toggleClass("sp-palette-disabled", !opts.showPalette); + container.toggleClass("sp-palette-only", opts.showPaletteOnly); + container.toggleClass("sp-initial-disabled", !opts.showInitial); + container.addClass(opts.className).addClass(opts.containerClassName); + + reflow(); + } + + function initialize() { + + if (IE) { + container.find("*:not(input)").attr("unselectable", "on"); + } + + applyOptions(); + + if (shouldReplace) { + boundElement.after(replacer).hide(); + } + + if (!allowEmpty) { + clearButton.hide(); + } + + if (flat) { + boundElement.after(container).hide(); + } + else { + + var appendTo = opts.appendTo === "parent" ? boundElement.parent() : $(opts.appendTo); + if (appendTo.length !== 1) { + appendTo = $("body"); + } + + appendTo.append(container); + } + + updateSelectionPaletteFromStorage(); + + offsetElement.bind("click.spectrum touchstart.spectrum", function (e) { + if (!disabled) { + toggle(); + } + + e.stopPropagation(); + + if (!$(e.target).is("input")) { + e.preventDefault(); + } + }); + + if(boundElement.is(":disabled") || (opts.disabled === true)) { + disable(); + } + + // Prevent clicks from bubbling up to document. This would cause it to be hidden. + container.click(stopPropagation); + + // Handle user typed input + textInput.change(setFromTextInput); + textInput.bind("paste", function () { + setTimeout(setFromTextInput, 1); + }); + textInput.keydown(function (e) { if (e.keyCode == 13) { setFromTextInput(); } }); + + cancelButton.text(opts.cancelText); + cancelButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + revert(); + hide(); + }); + + clearButton.attr("title", opts.clearText); + clearButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + isEmpty = true; + move(); + + if(flat) { + //for the flat style, this is a change event + updateOriginalInput(true); + } + }); + + chooseButton.text(opts.chooseText); + chooseButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + + if (IE && textInput.is(":focus")) { + textInput.trigger('change'); + } + + if (isValid()) { + updateOriginalInput(true); + hide(); + } + }); + + toggleButton.text(opts.showPaletteOnly ? opts.togglePaletteMoreText : opts.togglePaletteLessText); + toggleButton.bind("click.spectrum", function (e) { + e.stopPropagation(); + e.preventDefault(); + + opts.showPaletteOnly = !opts.showPaletteOnly; + + // To make sure the Picker area is drawn on the right, next to the + // Palette area (and not below the palette), first move the Palette + // to the left to make space for the picker, plus 5px extra. + // The 'applyOptions' function puts the whole container back into place + // and takes care of the button-text and the sp-palette-only CSS class. + if (!opts.showPaletteOnly && !flat) { + container.css('left', '-=' + (pickerContainer.outerWidth(true) + 5)); + } + applyOptions(); + }); + + draggable(alphaSlider, function (dragX, dragY, e) { + currentAlpha = (dragX / alphaWidth); + isEmpty = false; + if (e.shiftKey) { + currentAlpha = Math.round(currentAlpha * 10) / 10; + } + + move(); + }, dragStart, dragStop); + + draggable(slider, function (dragX, dragY) { + currentHue = parseFloat(dragY / slideHeight); + isEmpty = false; + if (!opts.showAlpha) { + currentAlpha = 1; + } + move(); + }, dragStart, dragStop); + + draggable(dragger, function (dragX, dragY, e) { + + // shift+drag should snap the movement to either the x or y axis. + if (!e.shiftKey) { + shiftMovementDirection = null; + } + else if (!shiftMovementDirection) { + var oldDragX = currentSaturation * dragWidth; + var oldDragY = dragHeight - (currentValue * dragHeight); + var furtherFromX = Math.abs(dragX - oldDragX) > Math.abs(dragY - oldDragY); + + shiftMovementDirection = furtherFromX ? "x" : "y"; + } + + var setSaturation = !shiftMovementDirection || shiftMovementDirection === "x"; + var setValue = !shiftMovementDirection || shiftMovementDirection === "y"; + + if (setSaturation) { + currentSaturation = parseFloat(dragX / dragWidth); + } + if (setValue) { + currentValue = parseFloat((dragHeight - dragY) / dragHeight); + } + + isEmpty = false; + if (!opts.showAlpha) { + currentAlpha = 1; + } + + move(); + + }, dragStart, dragStop); + + if (!!initialColor) { + set(initialColor); + + // In case color was black - update the preview UI and set the format + // since the set function will not run (default color is black). + updateUI(); + currentPreferredFormat = opts.preferredFormat || tinycolor(initialColor).format; + + addColorToSelectionPalette(initialColor); + } + else { + updateUI(); + } + + if (flat) { + show(); + } + + function paletteElementClick(e) { + if (e.data && e.data.ignore) { + set($(e.target).closest(".sp-thumb-el").data("color")); + move(); + } + else { + set($(e.target).closest(".sp-thumb-el").data("color")); + move(); + updateOriginalInput(true); + if (opts.hideAfterPaletteSelect) { + hide(); + } + } + + return false; + } + + var paletteEvent = IE ? "mousedown.spectrum" : "click.spectrum touchstart.spectrum"; + paletteContainer.delegate(".sp-thumb-el", paletteEvent, paletteElementClick); + initialColorContainer.delegate(".sp-thumb-el:nth-child(1)", paletteEvent, { ignore: true }, paletteElementClick); + } + + function updateSelectionPaletteFromStorage() { + + if (localStorageKey && window.localStorage) { + + // Migrate old palettes over to new format. May want to remove this eventually. + try { + var oldPalette = window.localStorage[localStorageKey].split(",#"); + if (oldPalette.length > 1) { + delete window.localStorage[localStorageKey]; + $.each(oldPalette, function(i, c) { + addColorToSelectionPalette(c); + }); + } + } + catch(e) { } + + try { + selectionPalette = window.localStorage[localStorageKey].split(";"); + } + catch (e) { } + } + } + + function addColorToSelectionPalette(color) { + if (showSelectionPalette) { + var rgb = tinycolor(color).toRgbString(); + if (!paletteLookup[rgb] && $.inArray(rgb, selectionPalette) === -1) { + selectionPalette.push(rgb); + while(selectionPalette.length > maxSelectionSize) { + selectionPalette.shift(); + } + } + + if (localStorageKey && window.localStorage) { + try { + window.localStorage[localStorageKey] = selectionPalette.join(";"); + } + catch(e) { } + } + } + } + + function getUniqueSelectionPalette() { + var unique = []; + if (opts.showPalette) { + for (var i = 0; i < selectionPalette.length; i++) { + var rgb = tinycolor(selectionPalette[i]).toRgbString(); + + if (!paletteLookup[rgb]) { + unique.push(selectionPalette[i]); + } + } + } + + return unique.reverse().slice(0, opts.maxSelectionSize); + } + + function drawPalette() { + + var currentColor = get(); + + var html = $.map(paletteArray, function (palette, i) { + return paletteTemplate(palette, currentColor, "sp-palette-row sp-palette-row-" + i, opts); + }); + + updateSelectionPaletteFromStorage(); + + if (selectionPalette) { + html.push(paletteTemplate(getUniqueSelectionPalette(), currentColor, "sp-palette-row sp-palette-row-selection", opts)); + } + + paletteContainer.html(html.join("")); + } + + function drawInitial() { + if (opts.showInitial) { + var initial = colorOnShow; + var current = get(); + initialColorContainer.html(paletteTemplate([initial, current], current, "sp-palette-row-initial", opts)); + } + } + + function dragStart() { + if (dragHeight <= 0 || dragWidth <= 0 || slideHeight <= 0) { + reflow(); + } + isDragging = true; + container.addClass(draggingClass); + shiftMovementDirection = null; + boundElement.trigger('dragstart.spectrum', [ get() ]); + } + + function dragStop() { + isDragging = false; + container.removeClass(draggingClass); + boundElement.trigger('dragstop.spectrum', [ get() ]); + } + + function setFromTextInput() { + + var value = textInput.val(); + + if ((value === null || value === "") && allowEmpty) { + set(null); + updateOriginalInput(true); + } + else { + var tiny = tinycolor(value); + if (tiny.isValid()) { + set(tiny); + updateOriginalInput(true); + } + else { + textInput.addClass("sp-validation-error"); + } + } + } + + function toggle() { + if (visible) { + hide(); + } + else { + show(); + } + } + + function show() { + var event = $.Event('beforeShow.spectrum'); + + if (visible) { + reflow(); + return; + } + + boundElement.trigger(event, [ get() ]); + + if (callbacks.beforeShow(get()) === false || event.isDefaultPrevented()) { + return; + } + + hideAll(); + visible = true; + + $(doc).bind("keydown.spectrum", onkeydown); + $(doc).bind("click.spectrum", clickout); + $(window).bind("resize.spectrum", resize); + replacer.addClass("sp-active"); + container.removeClass("sp-hidden"); + + reflow(); + updateUI(); + + colorOnShow = get(); + + drawInitial(); + callbacks.show(colorOnShow); + boundElement.trigger('show.spectrum', [ colorOnShow ]); + } + + function onkeydown(e) { + // Close on ESC + if (e.keyCode === 27) { + hide(); + } + } + + function clickout(e) { + // Return on right click. + if (e.button == 2) { return; } + + // If a drag event was happening during the mouseup, don't hide + // on click. + if (isDragging) { return; } + + if (clickoutFiresChange) { + updateOriginalInput(true); + } + else { + revert(); + } + hide(); + } + + function hide() { + // Return if hiding is unnecessary + if (!visible || flat) { return; } + visible = false; + + $(doc).unbind("keydown.spectrum", onkeydown); + $(doc).unbind("click.spectrum", clickout); + $(window).unbind("resize.spectrum", resize); + + replacer.removeClass("sp-active"); + container.addClass("sp-hidden"); + + callbacks.hide(get()); + boundElement.trigger('hide.spectrum', [ get() ]); + } + + function revert() { + set(colorOnShow, true); + } + + function set(color, ignoreFormatChange) { + if (tinycolor.equals(color, get())) { + // Update UI just in case a validation error needs + // to be cleared. + updateUI(); + return; + } + + var newColor, newHsv; + if (!color && allowEmpty) { + isEmpty = true; + } else { + isEmpty = false; + newColor = tinycolor(color); + newHsv = newColor.toHsv(); + + currentHue = (newHsv.h % 360) / 360; + currentSaturation = newHsv.s; + currentValue = newHsv.v; + currentAlpha = newHsv.a; + } + updateUI(); + + if (newColor && newColor.isValid() && !ignoreFormatChange) { + currentPreferredFormat = opts.preferredFormat || newColor.getFormat(); + } + } + + function get(opts) { + opts = opts || { }; + + if (allowEmpty && isEmpty) { + return null; + } + + return tinycolor.fromRatio({ + h: currentHue, + s: currentSaturation, + v: currentValue, + a: Math.round(currentAlpha * 100) / 100 + }, { format: opts.format || currentPreferredFormat }); + } + + function isValid() { + return !textInput.hasClass("sp-validation-error"); + } + + function move() { + updateUI(); + + callbacks.move(get()); + boundElement.trigger('move.spectrum', [ get() ]); + } + + function updateUI() { + + textInput.removeClass("sp-validation-error"); + + updateHelperLocations(); + + // Update dragger background color (gradients take care of saturation and value). + var flatColor = tinycolor.fromRatio({ h: currentHue, s: 1, v: 1 }); + dragger.css("background-color", flatColor.toHexString()); + + // Get a format that alpha will be included in (hex and names ignore alpha) + var format = currentPreferredFormat; + if (currentAlpha < 1 && !(currentAlpha === 0 && format === "name")) { + if (format === "hex" || format === "hex3" || format === "hex6" || format === "name") { + format = "rgb"; + } + } + + var realColor = get({ format: format }), + displayColor = ''; + + //reset background info for preview element + previewElement.removeClass("sp-clear-display"); + previewElement.css('background-color', 'transparent'); + + if (!realColor && allowEmpty) { + // Update the replaced elements background with icon indicating no color selection + previewElement.addClass("sp-clear-display"); + } + else { + var realHex = realColor.toHexString(), + realRgb = realColor.toRgbString(); + + // Update the replaced elements background color (with actual selected color) + if (rgbaSupport || realColor.alpha === 1) { + previewElement.css("background-color", realRgb); + } + else { + previewElement.css("background-color", "transparent"); + previewElement.css("filter", realColor.toFilter()); + } + + if (opts.showAlpha) { + var rgb = realColor.toRgb(); + rgb.a = 0; + var realAlpha = tinycolor(rgb).toRgbString(); + var gradient = "linear-gradient(left, " + realAlpha + ", " + realHex + ")"; + + if (IE) { + alphaSliderInner.css("filter", tinycolor(realAlpha).toFilter({ gradientType: 1 }, realHex)); + } + else { + alphaSliderInner.css("background", "-webkit-" + gradient); + alphaSliderInner.css("background", "-moz-" + gradient); + alphaSliderInner.css("background", "-ms-" + gradient); + // Use current syntax gradient on unprefixed property. + alphaSliderInner.css("background", + "linear-gradient(to right, " + realAlpha + ", " + realHex + ")"); + } + } + + displayColor = realColor.toString(format); + } + + // Update the text entry input as it changes happen + if (opts.showInput) { + textInput.val(displayColor); + } + + if (opts.showPalette) { + drawPalette(); + } + + drawInitial(); + } + + function updateHelperLocations() { + var s = currentSaturation; + var v = currentValue; + + if(allowEmpty && isEmpty) { + //if selected color is empty, hide the helpers + alphaSlideHelper.hide(); + slideHelper.hide(); + dragHelper.hide(); + } + else { + //make sure helpers are visible + alphaSlideHelper.show(); + slideHelper.show(); + dragHelper.show(); + + // Where to show the little circle in that displays your current selected color + var dragX = s * dragWidth; + var dragY = dragHeight - (v * dragHeight); + dragX = Math.max( + -dragHelperHeight, + Math.min(dragWidth - dragHelperHeight, dragX - dragHelperHeight) + ); + dragY = Math.max( + -dragHelperHeight, + Math.min(dragHeight - dragHelperHeight, dragY - dragHelperHeight) + ); + dragHelper.css({ + "top": dragY + "px", + "left": dragX + "px" + }); + + var alphaX = currentAlpha * alphaWidth; + alphaSlideHelper.css({ + "left": (alphaX - (alphaSlideHelperWidth / 2)) + "px" + }); + + // Where to show the bar that displays your current selected hue + var slideY = (currentHue) * slideHeight; + slideHelper.css({ + "top": (slideY - slideHelperHeight) + "px" + }); + } + } + + function updateOriginalInput(fireCallback) { + var color = get(), + displayColor = '', + hasChanged = !tinycolor.equals(color, colorOnShow); + + if (color) { + displayColor = color.toString(currentPreferredFormat); + // Update the selection palette with the current color + addColorToSelectionPalette(color); + } + + if (isInput) { + boundElement.val(displayColor); + } + + if (fireCallback && hasChanged) { + callbacks.change(color); + boundElement.trigger('change', [ color ]); + } + } + + function reflow() { + if (!visible) { + return; // Calculations would be useless and wouldn't be reliable anyways + } + dragWidth = dragger.width(); + dragHeight = dragger.height(); + dragHelperHeight = dragHelper.height(); + slideWidth = slider.width(); + slideHeight = slider.height(); + slideHelperHeight = slideHelper.height(); + alphaWidth = alphaSlider.width(); + alphaSlideHelperWidth = alphaSlideHelper.width(); + + if (!flat) { + container.css("position", "absolute"); + if (opts.offset) { + container.offset(opts.offset); + } else { + container.offset(getOffset(container, offsetElement)); + } + } + + updateHelperLocations(); + + if (opts.showPalette) { + drawPalette(); + } + + boundElement.trigger('reflow.spectrum'); + } + + function destroy() { + boundElement.show(); + offsetElement.unbind("click.spectrum touchstart.spectrum"); + container.remove(); + replacer.remove(); + spectrums[spect.id] = null; + } + + function option(optionName, optionValue) { + if (optionName === undefined) { + return $.extend({}, opts); + } + if (optionValue === undefined) { + return opts[optionName]; + } + + opts[optionName] = optionValue; + + if (optionName === "preferredFormat") { + currentPreferredFormat = opts.preferredFormat; + } + applyOptions(); + } + + function enable() { + disabled = false; + boundElement.attr("disabled", false); + offsetElement.removeClass("sp-disabled"); + } + + function disable() { + hide(); + disabled = true; + boundElement.attr("disabled", true); + offsetElement.addClass("sp-disabled"); + } + + function setOffset(coord) { + opts.offset = coord; + reflow(); + } + + initialize(); + + var spect = { + show: show, + hide: hide, + toggle: toggle, + reflow: reflow, + option: option, + enable: enable, + disable: disable, + offset: setOffset, + set: function (c) { + set(c); + updateOriginalInput(); + }, + get: get, + destroy: destroy, + container: container + }; + + spect.id = spectrums.push(spect) - 1; + + return spect; + } + + /** + * checkOffset - get the offset below/above and left/right element depending on screen position + * Thanks https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.datepicker.js + */ + function getOffset(picker, input) { + var extraY = 0; + var dpWidth = picker.outerWidth(); + var dpHeight = picker.outerHeight(); + var inputHeight = input.outerHeight(); + var doc = picker[0].ownerDocument; + var docElem = doc.documentElement; + var viewWidth = docElem.clientWidth + $(doc).scrollLeft(); + var viewHeight = docElem.clientHeight + $(doc).scrollTop(); + var offset = input.offset(); + offset.top += inputHeight; + + offset.left -= + Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ? + Math.abs(offset.left + dpWidth - viewWidth) : 0); + + offset.top -= + Math.min(offset.top, ((offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ? + Math.abs(dpHeight + inputHeight - extraY) : extraY)); + + return offset; + } + + /** + * noop - do nothing + */ + function noop() { + + } + + /** + * stopPropagation - makes the code only doing this a little easier to read in line + */ + function stopPropagation(e) { + e.stopPropagation(); + } + + /** + * Create a function bound to a given object + * Thanks to underscore.js + */ + function bind(func, obj) { + var slice = Array.prototype.slice; + var args = slice.call(arguments, 2); + return function () { + return func.apply(obj, args.concat(slice.call(arguments))); + }; + } + + /** + * Lightweight drag helper. Handles containment within the element, so that + * when dragging, the x is within [0,element.width] and y is within [0,element.height] + */ + function draggable(element, onmove, onstart, onstop) { + onmove = onmove || function () { }; + onstart = onstart || function () { }; + onstop = onstop || function () { }; + var doc = document; + var dragging = false; + var offset = {}; + var maxHeight = 0; + var maxWidth = 0; + var hasTouch = ('ontouchstart' in window); + + var duringDragEvents = {}; + duringDragEvents["selectstart"] = prevent; + duringDragEvents["dragstart"] = prevent; + duringDragEvents["touchmove mousemove"] = move; + duringDragEvents["touchend mouseup"] = stop; + + function prevent(e) { + if (e.stopPropagation) { + e.stopPropagation(); + } + if (e.preventDefault) { + e.preventDefault(); + } + e.returnValue = false; + } + + function move(e) { + if (dragging) { + // Mouseup happened outside of window + if (IE && doc.documentMode < 9 && !e.button) { + return stop(); + } + + var t0 = e.originalEvent && e.originalEvent.touches && e.originalEvent.touches[0]; + var pageX = t0 && t0.pageX || e.pageX; + var pageY = t0 && t0.pageY || e.pageY; + + var dragX = Math.max(0, Math.min(pageX - offset.left, maxWidth)); + var dragY = Math.max(0, Math.min(pageY - offset.top, maxHeight)); + + if (hasTouch) { + // Stop scrolling in iOS + prevent(e); + } + + onmove.apply(element, [dragX, dragY, e]); + } + } + + function start(e) { + var rightclick = (e.which) ? (e.which == 3) : (e.button == 2); + + if (!rightclick && !dragging) { + if (onstart.apply(element, arguments) !== false) { + dragging = true; + maxHeight = $(element).height(); + maxWidth = $(element).width(); + offset = $(element).offset(); + + $(doc).bind(duringDragEvents); + $(doc.body).addClass("sp-dragging"); + + move(e); + + prevent(e); + } + } + } + + function stop() { + if (dragging) { + $(doc).unbind(duringDragEvents); + $(doc.body).removeClass("sp-dragging"); + + // Wait a tick before notifying observers to allow the click event + // to fire in Chrome. + setTimeout(function() { + onstop.apply(element, arguments); + }, 0); + } + dragging = false; + } + + $(element).bind("touchstart mousedown", start); + } + + function throttle(func, wait, debounce) { + var timeout; + return function () { + var context = this, args = arguments; + var throttler = function () { + timeout = null; + func.apply(context, args); + }; + if (debounce) clearTimeout(timeout); + if (debounce || !timeout) timeout = setTimeout(throttler, wait); + }; + } + + function inputTypeColorSupport() { + return $.fn.spectrum.inputTypeColorSupport(); + } + + /** + * Define a jQuery plugin + */ + var dataID = "spectrum.id"; + $.fn.spectrum = function (opts, extra) { + + if (typeof opts == "string") { + + var returnValue = this; + var args = Array.prototype.slice.call( arguments, 1 ); + + this.each(function () { + var spect = spectrums[$(this).data(dataID)]; + if (spect) { + var method = spect[opts]; + if (!method) { + throw new Error( "Spectrum: no such method: '" + opts + "'" ); + } + + if (opts == "get") { + returnValue = spect.get(); + } + else if (opts == "container") { + returnValue = spect.container; + } + else if (opts == "option") { + returnValue = spect.option.apply(spect, args); + } + else if (opts == "destroy") { + spect.destroy(); + $(this).removeData(dataID); + } + else { + method.apply(spect, args); + } + } + }); + + return returnValue; + } + + // Initializing a new instance of spectrum + return this.spectrum("destroy").each(function () { + var options = $.extend({}, opts, $(this).data()); + var spect = spectrum(this, options); + $(this).data(dataID, spect.id); + }); + }; + + $.fn.spectrum.load = true; + $.fn.spectrum.loadOpts = {}; + $.fn.spectrum.draggable = draggable; + $.fn.spectrum.defaults = defaultOpts; + $.fn.spectrum.inputTypeColorSupport = function inputTypeColorSupport() { + if (typeof inputTypeColorSupport._cachedResult === "undefined") { + var colorInput = $("")[0]; // if color element is supported, value will default to not null + inputTypeColorSupport._cachedResult = colorInput.type === "color" && colorInput.value !== ""; + } + return inputTypeColorSupport._cachedResult; + }; + + $.spectrum = { }; + $.spectrum.localization = { }; + $.spectrum.palettes = { }; + + $.fn.spectrum.processNativeColorInputs = function () { + var colorInputs = $("input[type=color]"); + if (colorInputs.length && !inputTypeColorSupport()) { + colorInputs.spectrum({ + preferredFormat: "hex6" + }); + } + }; + + // TinyColor v1.1.2 + // https://github.com/bgrins/TinyColor + // Brian Grinstead, MIT License + + (function() { + + var trimLeft = /^[\s,#]+/, + trimRight = /\s+$/, + tinyCounter = 0, + math = Math, + mathRound = math.round, + mathMin = math.min, + mathMax = math.max, + mathRandom = math.random; + + var tinycolor = function(color, opts) { + + color = (color) ? color : ''; + opts = opts || { }; + + // If input is already a tinycolor, return itself + if (color instanceof tinycolor) { + return color; + } + // If we are called as a function, call using new instead + if (!(this instanceof tinycolor)) { + return new tinycolor(color, opts); + } + + var rgb = inputToRGB(color); + this._originalInput = color, + this._r = rgb.r, + this._g = rgb.g, + this._b = rgb.b, + this._a = rgb.a, + this._roundA = mathRound(100*this._a) / 100, + this._format = opts.format || rgb.format; + this._gradientType = opts.gradientType; + + // Don't let the range of [0,255] come back in [0,1]. + // Potentially lose a little bit of precision here, but will fix issues where + // .5 gets interpreted as half of the total, instead of half of 1 + // If it was supposed to be 128, this was already taken care of by `inputToRgb` + if (this._r < 1) { this._r = mathRound(this._r); } + if (this._g < 1) { this._g = mathRound(this._g); } + if (this._b < 1) { this._b = mathRound(this._b); } + + this._ok = rgb.ok; + this._tc_id = tinyCounter++; + }; + + tinycolor.prototype = { + isDark: function() { + return this.getBrightness() < 128; + }, + isLight: function() { + return !this.isDark(); + }, + isValid: function() { + return this._ok; + }, + getOriginalInput: function() { + return this._originalInput; + }, + getFormat: function() { + return this._format; + }, + getAlpha: function() { + return this._a; + }, + getBrightness: function() { + var rgb = this.toRgb(); + return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; + }, + setAlpha: function(value) { + this._a = boundAlpha(value); + this._roundA = mathRound(100*this._a) / 100; + return this; + }, + toHsv: function() { + var hsv = rgbToHsv(this._r, this._g, this._b); + return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; + }, + toHsvString: function() { + var hsv = rgbToHsv(this._r, this._g, this._b); + var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); + return (this._a == 1) ? + "hsv(" + h + ", " + s + "%, " + v + "%)" : + "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; + }, + toHsl: function() { + var hsl = rgbToHsl(this._r, this._g, this._b); + return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; + }, + toHslString: function() { + var hsl = rgbToHsl(this._r, this._g, this._b); + var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); + return (this._a == 1) ? + "hsl(" + h + ", " + s + "%, " + l + "%)" : + "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; + }, + toHex: function(allow3Char) { + return rgbToHex(this._r, this._g, this._b, allow3Char); + }, + toHexString: function(allow3Char) { + return '#' + this.toHex(allow3Char); + }, + toHex8: function() { + return rgbaToHex(this._r, this._g, this._b, this._a); + }, + toHex8String: function() { + return '#' + this.toHex8(); + }, + toRgb: function() { + return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; + }, + toRgbString: function() { + return (this._a == 1) ? + "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : + "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; + }, + toPercentageRgb: function() { + return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a }; + }, + toPercentageRgbString: function() { + return (this._a == 1) ? + "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : + "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; + }, + toName: function() { + if (this._a === 0) { + return "transparent"; + } + + if (this._a < 1) { + return false; + } + + return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; + }, + toFilter: function(secondColor) { + var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a); + var secondHex8String = hex8String; + var gradientType = this._gradientType ? "GradientType = 1, " : ""; + + if (secondColor) { + var s = tinycolor(secondColor); + secondHex8String = s.toHex8String(); + } + + return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; + }, + toString: function(format) { + var formatSet = !!format; + format = format || this._format; + + var formattedString = false; + var hasAlpha = this._a < 1 && this._a >= 0; + var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); + + if (needsAlphaFormat) { + // Special case for "transparent", all other non-alpha formats + // will return rgba when there is transparency. + if (format === "name" && this._a === 0) { + return this.toName(); + } + return this.toRgbString(); + } + if (format === "rgb") { + formattedString = this.toRgbString(); + } + if (format === "prgb") { + formattedString = this.toPercentageRgbString(); + } + if (format === "hex" || format === "hex6") { + formattedString = this.toHexString(); + } + if (format === "hex3") { + formattedString = this.toHexString(true); + } + if (format === "hex8") { + formattedString = this.toHex8String(); + } + if (format === "name") { + formattedString = this.toName(); + } + if (format === "hsl") { + formattedString = this.toHslString(); + } + if (format === "hsv") { + formattedString = this.toHsvString(); + } + + return formattedString || this.toHexString(); + }, + + _applyModification: function(fn, args) { + var color = fn.apply(null, [this].concat([].slice.call(args))); + this._r = color._r; + this._g = color._g; + this._b = color._b; + this.setAlpha(color._a); + return this; + }, + lighten: function() { + return this._applyModification(lighten, arguments); + }, + brighten: function() { + return this._applyModification(brighten, arguments); + }, + darken: function() { + return this._applyModification(darken, arguments); + }, + desaturate: function() { + return this._applyModification(desaturate, arguments); + }, + saturate: function() { + return this._applyModification(saturate, arguments); + }, + greyscale: function() { + return this._applyModification(greyscale, arguments); + }, + spin: function() { + return this._applyModification(spin, arguments); + }, + + _applyCombination: function(fn, args) { + return fn.apply(null, [this].concat([].slice.call(args))); + }, + analogous: function() { + return this._applyCombination(analogous, arguments); + }, + complement: function() { + return this._applyCombination(complement, arguments); + }, + monochromatic: function() { + return this._applyCombination(monochromatic, arguments); + }, + splitcomplement: function() { + return this._applyCombination(splitcomplement, arguments); + }, + triad: function() { + return this._applyCombination(triad, arguments); + }, + tetrad: function() { + return this._applyCombination(tetrad, arguments); + } + }; + + // If input is an object, force 1 into "1.0" to handle ratios properly + // String input requires "1.0" as input, so 1 will be treated as 1 + tinycolor.fromRatio = function(color, opts) { + if (typeof color == "object") { + var newColor = {}; + for (var i in color) { + if (color.hasOwnProperty(i)) { + if (i === "a") { + newColor[i] = color[i]; + } + else { + newColor[i] = convertToPercentage(color[i]); + } + } + } + color = newColor; + } + + return tinycolor(color, opts); + }; + + // Given a string or object, convert that input to RGB + // Possible string inputs: + // + // "red" + // "#f00" or "f00" + // "#ff0000" or "ff0000" + // "#ff000000" or "ff000000" + // "rgb 255 0 0" or "rgb (255, 0, 0)" + // "rgb 1.0 0 0" or "rgb (1, 0, 0)" + // "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" + // "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" + // "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" + // "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" + // "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" + // + function inputToRGB(color) { + + var rgb = { r: 0, g: 0, b: 0 }; + var a = 1; + var ok = false; + var format = false; + + if (typeof color == "string") { + color = stringInputToObject(color); + } + + if (typeof color == "object") { + if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { + rgb = rgbToRgb(color.r, color.g, color.b); + ok = true; + format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { + color.s = convertToPercentage(color.s); + color.v = convertToPercentage(color.v); + rgb = hsvToRgb(color.h, color.s, color.v); + ok = true; + format = "hsv"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { + color.s = convertToPercentage(color.s); + color.l = convertToPercentage(color.l); + rgb = hslToRgb(color.h, color.s, color.l); + ok = true; + format = "hsl"; + } + + if (color.hasOwnProperty("a")) { + a = color.a; + } + } + + a = boundAlpha(a); + + return { + ok: ok, + format: color.format || format, + r: mathMin(255, mathMax(rgb.r, 0)), + g: mathMin(255, mathMax(rgb.g, 0)), + b: mathMin(255, mathMax(rgb.b, 0)), + a: a + }; + } + + + // Conversion Functions + // -------------------- + + // `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: + // + + // `rgbToRgb` + // Handle bounds / percentage checking to conform to CSS color spec + // + // *Assumes:* r, g, b in [0, 255] or [0, 1] + // *Returns:* { r, g, b } in [0, 255] + function rgbToRgb(r, g, b){ + return { + r: bound01(r, 255) * 255, + g: bound01(g, 255) * 255, + b: bound01(b, 255) * 255 + }; + } + + // `rgbToHsl` + // Converts an RGB color value to HSL. + // *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] + // *Returns:* { h, s, l } in [0,1] + function rgbToHsl(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, l = (max + min) / 2; + + if(max == min) { + h = s = 0; // achromatic + } + else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + return { h: h, s: s, l: l }; + } + + // `hslToRgb` + // Converts an HSL color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hslToRgb(h, s, l) { + var r, g, b; + + h = bound01(h, 360); + s = bound01(s, 100); + l = bound01(l, 100); + + function hue2rgb(p, q, t) { + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + if(s === 0) { + r = g = b = l; // achromatic + } + else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHsv` + // Converts an RGB color value to HSV + // *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] + // *Returns:* { h, s, v } in [0,1] + function rgbToHsv(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, v = max; + + var d = max - min; + s = max === 0 ? 0 : d / max; + + if(max == min) { + h = 0; // achromatic + } + else { + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h, s: s, v: v }; + } + + // `hsvToRgb` + // Converts an HSV color value to RGB. + // *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] + // *Returns:* { r, g, b } in the set [0, 255] + function hsvToRgb(h, s, v) { + + h = bound01(h, 360) * 6; + s = bound01(s, 100); + v = bound01(v, 100); + + var i = math.floor(h), + f = h - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + mod = i % 6, + r = [v, q, p, p, t, v][mod], + g = [t, v, v, q, p, p][mod], + b = [p, p, t, v, v, q][mod]; + + return { r: r * 255, g: g * 255, b: b * 255 }; + } + + // `rgbToHex` + // Converts an RGB color to hex + // Assumes r, g, and b are contained in the set [0, 255] + // Returns a 3 or 6 character hex + function rgbToHex(r, g, b, allow3Char) { + + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + // Return a 3 character hex if possible + if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + } + + return hex.join(""); + } + // `rgbaToHex` + // Converts an RGBA color plus alpha transparency to hex + // Assumes r, g, b and a are contained in the set [0, 255] + // Returns an 8 character hex + function rgbaToHex(r, g, b, a) { + + var hex = [ + pad2(convertDecimalToHex(a)), + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + return hex.join(""); + } + + // `equals` + // Can be called with any tinycolor input + tinycolor.equals = function (color1, color2) { + if (!color1 || !color2) { return false; } + return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); + }; + tinycolor.random = function() { + return tinycolor.fromRatio({ + r: mathRandom(), + g: mathRandom(), + b: mathRandom() + }); + }; + + + // Modification Functions + // ---------------------- + // Thanks to less.js for some of the basics here + // + + function desaturate(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s -= amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + } + + function saturate(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s += amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); + } + + function greyscale(color) { + return tinycolor(color).desaturate(100); + } + + function lighten (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l += amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + } + + function brighten(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var rgb = tinycolor(color).toRgb(); + rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); + rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); + rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); + return tinycolor(rgb); + } + + function darken (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l -= amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); + } + + // Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. + // Values outside of this range will be wrapped into this range. + function spin(color, amount) { + var hsl = tinycolor(color).toHsl(); + var hue = (mathRound(hsl.h) + amount) % 360; + hsl.h = hue < 0 ? 360 + hue : hue; + return tinycolor(hsl); + } + + // Combination Functions + // --------------------- + // Thanks to jQuery xColor for some of the ideas behind these + // + + function complement(color) { + var hsl = tinycolor(color).toHsl(); + hsl.h = (hsl.h + 180) % 360; + return tinycolor(hsl); + } + + function triad(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) + ]; + } + + function tetrad(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) + ]; + } + + function splitcomplement(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), + tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) + ]; + } + + function analogous(color, results, slices) { + results = results || 6; + slices = slices || 30; + + var hsl = tinycolor(color).toHsl(); + var part = 360 / slices; + var ret = [tinycolor(color)]; + + for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { + hsl.h = (hsl.h + part) % 360; + ret.push(tinycolor(hsl)); + } + return ret; + } + + function monochromatic(color, results) { + results = results || 6; + var hsv = tinycolor(color).toHsv(); + var h = hsv.h, s = hsv.s, v = hsv.v; + var ret = []; + var modification = 1 / results; + + while (results--) { + ret.push(tinycolor({ h: h, s: s, v: v})); + v = (v + modification) % 1; + } + + return ret; + } + + // Utility Functions + // --------------------- + + tinycolor.mix = function(color1, color2, amount) { + amount = (amount === 0) ? 0 : (amount || 50); + + var rgb1 = tinycolor(color1).toRgb(); + var rgb2 = tinycolor(color2).toRgb(); + + var p = amount / 100; + var w = p * 2 - 1; + var a = rgb2.a - rgb1.a; + + var w1; + + if (w * a == -1) { + w1 = w; + } else { + w1 = (w + a) / (1 + w * a); + } + + w1 = (w1 + 1) / 2; + + var w2 = 1 - w1; + + var rgba = { + r: rgb2.r * w1 + rgb1.r * w2, + g: rgb2.g * w1 + rgb1.g * w2, + b: rgb2.b * w1 + rgb1.b * w2, + a: rgb2.a * p + rgb1.a * (1 - p) + }; + + return tinycolor(rgba); + }; + + + // Readability Functions + // --------------------- + // + + // `readability` + // Analyze the 2 colors and returns an object with the following properties: + // `brightness`: difference in brightness between the two colors + // `color`: difference in color/hue between the two colors + tinycolor.readability = function(color1, color2) { + var c1 = tinycolor(color1); + var c2 = tinycolor(color2); + var rgb1 = c1.toRgb(); + var rgb2 = c2.toRgb(); + var brightnessA = c1.getBrightness(); + var brightnessB = c2.getBrightness(); + var colorDiff = ( + Math.max(rgb1.r, rgb2.r) - Math.min(rgb1.r, rgb2.r) + + Math.max(rgb1.g, rgb2.g) - Math.min(rgb1.g, rgb2.g) + + Math.max(rgb1.b, rgb2.b) - Math.min(rgb1.b, rgb2.b) + ); + + return { + brightness: Math.abs(brightnessA - brightnessB), + color: colorDiff + }; + }; + + // `readable` + // http://www.w3.org/TR/AERT#color-contrast + // Ensure that foreground and background color combinations provide sufficient contrast. + // *Example* + // tinycolor.isReadable("#000", "#111") => false + tinycolor.isReadable = function(color1, color2) { + var readability = tinycolor.readability(color1, color2); + return readability.brightness > 125 && readability.color > 500; + }; + + // `mostReadable` + // Given a base color and a list of possible foreground or background + // colors for that base, returns the most readable color. + // *Example* + // tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000" + tinycolor.mostReadable = function(baseColor, colorList) { + var bestColor = null; + var bestScore = 0; + var bestIsReadable = false; + for (var i=0; i < colorList.length; i++) { + + // We normalize both around the "acceptable" breaking point, + // but rank brightness constrast higher than hue. + + var readability = tinycolor.readability(baseColor, colorList[i]); + var readable = readability.brightness > 125 && readability.color > 500; + var score = 3 * (readability.brightness / 125) + (readability.color / 500); + + if ((readable && ! bestIsReadable) || + (readable && bestIsReadable && score > bestScore) || + ((! readable) && (! bestIsReadable) && score > bestScore)) { + bestIsReadable = readable; + bestScore = score; + bestColor = tinycolor(colorList[i]); + } + } + return bestColor; + }; + + + // Big List of Colors + // ------------------ + // + var names = tinycolor.names = { + aliceblue: "f0f8ff", + antiquewhite: "faebd7", + aqua: "0ff", + aquamarine: "7fffd4", + azure: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "000", + blanchedalmond: "ffebcd", + blue: "00f", + blueviolet: "8a2be2", + brown: "a52a2a", + burlywood: "deb887", + burntsienna: "ea7e5d", + cadetblue: "5f9ea0", + chartreuse: "7fff00", + chocolate: "d2691e", + coral: "ff7f50", + cornflowerblue: "6495ed", + cornsilk: "fff8dc", + crimson: "dc143c", + cyan: "0ff", + darkblue: "00008b", + darkcyan: "008b8b", + darkgoldenrod: "b8860b", + darkgray: "a9a9a9", + darkgreen: "006400", + darkgrey: "a9a9a9", + darkkhaki: "bdb76b", + darkmagenta: "8b008b", + darkolivegreen: "556b2f", + darkorange: "ff8c00", + darkorchid: "9932cc", + darkred: "8b0000", + darksalmon: "e9967a", + darkseagreen: "8fbc8f", + darkslateblue: "483d8b", + darkslategray: "2f4f4f", + darkslategrey: "2f4f4f", + darkturquoise: "00ced1", + darkviolet: "9400d3", + deeppink: "ff1493", + deepskyblue: "00bfff", + dimgray: "696969", + dimgrey: "696969", + dodgerblue: "1e90ff", + firebrick: "b22222", + floralwhite: "fffaf0", + forestgreen: "228b22", + fuchsia: "f0f", + gainsboro: "dcdcdc", + ghostwhite: "f8f8ff", + gold: "ffd700", + goldenrod: "daa520", + gray: "808080", + green: "008000", + greenyellow: "adff2f", + grey: "808080", + honeydew: "f0fff0", + hotpink: "ff69b4", + indianred: "cd5c5c", + indigo: "4b0082", + ivory: "fffff0", + khaki: "f0e68c", + lavender: "e6e6fa", + lavenderblush: "fff0f5", + lawngreen: "7cfc00", + lemonchiffon: "fffacd", + lightblue: "add8e6", + lightcoral: "f08080", + lightcyan: "e0ffff", + lightgoldenrodyellow: "fafad2", + lightgray: "d3d3d3", + lightgreen: "90ee90", + lightgrey: "d3d3d3", + lightpink: "ffb6c1", + lightsalmon: "ffa07a", + lightseagreen: "20b2aa", + lightskyblue: "87cefa", + lightslategray: "789", + lightslategrey: "789", + lightsteelblue: "b0c4de", + lightyellow: "ffffe0", + lime: "0f0", + limegreen: "32cd32", + linen: "faf0e6", + magenta: "f0f", + maroon: "800000", + mediumaquamarine: "66cdaa", + mediumblue: "0000cd", + mediumorchid: "ba55d3", + mediumpurple: "9370db", + mediumseagreen: "3cb371", + mediumslateblue: "7b68ee", + mediumspringgreen: "00fa9a", + mediumturquoise: "48d1cc", + mediumvioletred: "c71585", + midnightblue: "191970", + mintcream: "f5fffa", + mistyrose: "ffe4e1", + moccasin: "ffe4b5", + navajowhite: "ffdead", + navy: "000080", + oldlace: "fdf5e6", + olive: "808000", + olivedrab: "6b8e23", + orange: "ffa500", + orangered: "ff4500", + orchid: "da70d6", + palegoldenrod: "eee8aa", + palegreen: "98fb98", + paleturquoise: "afeeee", + palevioletred: "db7093", + papayawhip: "ffefd5", + peachpuff: "ffdab9", + peru: "cd853f", + pink: "ffc0cb", + plum: "dda0dd", + powderblue: "b0e0e6", + purple: "800080", + rebeccapurple: "663399", + red: "f00", + rosybrown: "bc8f8f", + royalblue: "4169e1", + saddlebrown: "8b4513", + salmon: "fa8072", + sandybrown: "f4a460", + seagreen: "2e8b57", + seashell: "fff5ee", + sienna: "a0522d", + silver: "c0c0c0", + skyblue: "87ceeb", + slateblue: "6a5acd", + slategray: "708090", + slategrey: "708090", + snow: "fffafa", + springgreen: "00ff7f", + steelblue: "4682b4", + tan: "d2b48c", + teal: "008080", + thistle: "d8bfd8", + tomato: "ff6347", + turquoise: "40e0d0", + violet: "ee82ee", + wheat: "f5deb3", + white: "fff", + whitesmoke: "f5f5f5", + yellow: "ff0", + yellowgreen: "9acd32" + }; + + // Make it easy to access colors via `hexNames[hex]` + var hexNames = tinycolor.hexNames = flip(names); + + + // Utilities + // --------- + + // `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` + function flip(o) { + var flipped = { }; + for (var i in o) { + if (o.hasOwnProperty(i)) { + flipped[o[i]] = i; + } + } + return flipped; + } + + // Return a valid alpha value [0,1] with all invalid values being set to 1 + function boundAlpha(a) { + a = parseFloat(a); + + if (isNaN(a) || a < 0 || a > 1) { + a = 1; + } + + return a; + } + + // Take input from [0, n] and return it as [0, 1] + function bound01(n, max) { + if (isOnePointZero(n)) { n = "100%"; } + + var processPercent = isPercentage(n); + n = mathMin(max, mathMax(0, parseFloat(n))); + + // Automatically convert percentage into number + if (processPercent) { + n = parseInt(n * max, 10) / 100; + } + + // Handle floating point rounding errors + if ((math.abs(n - max) < 0.000001)) { + return 1; + } + + // Convert into [0, 1] range if it isn't already + return (n % max) / parseFloat(max); + } + + // Force a number between 0 and 1 + function clamp01(val) { + return mathMin(1, mathMax(0, val)); + } + + // Parse a base-16 hex value into a base-10 integer + function parseIntFromHex(val) { + return parseInt(val, 16); + } + + // Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 + // + function isOnePointZero(n) { + return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; + } + + // Check to see if string passed in is a percentage + function isPercentage(n) { + return typeof n === "string" && n.indexOf('%') != -1; + } + + // Force a hex value to have 2 characters + function pad2(c) { + return c.length == 1 ? '0' + c : '' + c; + } + + // Replace a decimal with it's percentage value + function convertToPercentage(n) { + if (n <= 1) { + n = (n * 100) + "%"; + } + + return n; + } + + // Converts a decimal to a hex value + function convertDecimalToHex(d) { + return Math.round(parseFloat(d) * 255).toString(16); + } + // Converts a hex value to a decimal + function convertHexToDecimal(h) { + return (parseIntFromHex(h) / 255); + } + + var matchers = (function() { + + // + var CSS_INTEGER = "[-\\+]?\\d+%?"; + + // + var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; + + // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. + var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; + + // Actual matching. + // Parentheses and commas are optional, but not required. + // Whitespace can take the place of commas or opening paren + var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + + return { + rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), + rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), + hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), + hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), + hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), + hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), + hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, + hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ + }; + })(); + + // `stringInputToObject` + // Permissive string parsing. Take in a number of formats, and output an object + // based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` + function stringInputToObject(color) { + + color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); + var named = false; + if (names[color]) { + color = names[color]; + named = true; + } + else if (color == 'transparent') { + return { r: 0, g: 0, b: 0, a: 0, format: "name" }; + } + + // Try to match string input using regular expressions. + // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] + // Just return an object and let the conversion functions handle that. + // This way the result will be the same whether the tinycolor is initialized with string or object. + var match; + if ((match = matchers.rgb.exec(color))) { + return { r: match[1], g: match[2], b: match[3] }; + } + if ((match = matchers.rgba.exec(color))) { + return { r: match[1], g: match[2], b: match[3], a: match[4] }; + } + if ((match = matchers.hsl.exec(color))) { + return { h: match[1], s: match[2], l: match[3] }; + } + if ((match = matchers.hsla.exec(color))) { + return { h: match[1], s: match[2], l: match[3], a: match[4] }; + } + if ((match = matchers.hsv.exec(color))) { + return { h: match[1], s: match[2], v: match[3] }; + } + if ((match = matchers.hsva.exec(color))) { + return { h: match[1], s: match[2], v: match[3], a: match[4] }; + } + if ((match = matchers.hex8.exec(color))) { + return { + a: convertHexToDecimal(match[1]), + r: parseIntFromHex(match[2]), + g: parseIntFromHex(match[3]), + b: parseIntFromHex(match[4]), + format: named ? "name" : "hex8" + }; + } + if ((match = matchers.hex6.exec(color))) { + return { + r: parseIntFromHex(match[1]), + g: parseIntFromHex(match[2]), + b: parseIntFromHex(match[3]), + format: named ? "name" : "hex" + }; + } + if ((match = matchers.hex3.exec(color))) { + return { + r: parseIntFromHex(match[1] + '' + match[1]), + g: parseIntFromHex(match[2] + '' + match[2]), + b: parseIntFromHex(match[3] + '' + match[3]), + format: named ? "name" : "hex" + }; + } + + return false; + } + + window.tinycolor = tinycolor; + })(); + + $(function () { + if ($.fn.spectrum.load) { + $.fn.spectrum.processNativeColorInputs(); + } + }); + +}); diff --git a/files_reader/vendor/bitjs b/files_reader/vendor/bitjs new file mode 160000 index 0000000..1f76d76 --- /dev/null +++ b/files_reader/vendor/bitjs @@ -0,0 +1 @@ +Subproject commit 1f76d7611598b88fe736ec77eb4626bcf1e9ee06 diff --git a/files_reader/vendor/cbrjs/cbr.js b/files_reader/vendor/cbrjs/cbr.js new file mode 100644 index 0000000..503f2fa --- /dev/null +++ b/files_reader/vendor/cbrjs/cbr.js @@ -0,0 +1,1478 @@ +var CBRJS = CBRJS || {}; +CBRJS.VERSION = "0.0.1"; + +CBRJS.basePath = CBRJS.basePath || ""; +CBRJS.session = CBRJS.session || {}; + +CBRJS.Reader = function(bookPath, _options) { + + var reader = this, + $progressbar = $('.bar'), + search = window.location.search, + parameters, + options, + found; + + this.options = options = $.extend(true, _options || {}, { + bookPath: bookPath, + session: {} + }); + + // 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.options[name] = decodeURIComponent(value); + }); + } + + function extractImages(url, opts) { + + var images = [], + xhr = new XMLHttpRequest(), + filename = decodeURIComponent(url.split('/').pop()), + re_file_ext = new RegExp(/\.([a-z]+)$/), + format = filename.toLowerCase().match(re_file_ext)[1], + archive_class = ({ cbz: 'Unzipper', cbr: 'Unrarrer' })[format], + options = $.extend({ + start: function () {}, + extract: function (page_url) {}, + progress: function (percent_complete) {}, + finish: function (images) {} + }, opts); + + if (!archive_class) { + alert('invalid file type, only cbz and cbr are supported.'); + return false; + } + + xhr.open('GET',url, true); + + options.start(filename); + + xhr.responseType = "arraybuffer"; + + xhr.onprogress = function (e) { + if (e.lengthComputable) { + $progressbar.css('width', Math.floor((e.loaded / e.total) * 100) + '%'); + } + }; + + xhr.onloadstart = function (e) { + $progressbar.css('width', '0%'); + }; + + xhr.onloadend = function (e) { + $('.icon-cloud_download').addClass('ok'); + reader.options.session.size = e.total; + }; + + xhr.onload = function () { + if ((this.status === 200) && this.response) { + var done = false; + var ua = new bitjs.archive[archive_class](this.response, 'vendor/bitjs/'); + + ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.START, function (e) { + $progressbar.css('width', '0%'); + $('.icon-unarchive').addClass('active'); + }); + + ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.EXTRACT, function (e) { + + var mimetype, blob, url; + var file_extension = e.unarchivedFile.filename.toLowerCase().match(re_file_ext)[1]; + + switch (file_extension) { + case 'jpg': + case 'jpeg': + mimetype = 'image/jpeg'; + break; + case 'png': + mimetype = 'image/png'; + break; + case 'gif': + mimetype = 'image/gif'; + break; + default: + return false; + } + + blob = new Blob([e.unarchivedFile.fileData], { type: mimetype }); + url = window.URL.createObjectURL(blob); + + images.push(url); + + options.extract(url, blob); + }); + + ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.PROGRESS, function (e) { + options.progress(Math.floor(e.currentBytesUnarchived / e.totalUncompressedBytesInArchive * 100)); + }); + + ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.FINISH, function (e) { + options.finish(images); + }); + + ua.addEventListener(bitjs.archive.UnarchiveEvent.Type.ERROR, function (e) { + $('.icon-unarchive').removeClass('active'); + $('.icon-unarchive').addClass('error'); + $('#message').text('Failed to extract images from archive, file corrupted?'); + + }); + } + + ua.start(); + }; + + xhr.send(); + } + + function openComicArchive(url, options) { + + var title, page = 0; + + extractImages(url, { + start: function (filename) { + this.filename = filename; + $('.toolbar').addClass('hide'); + $('.navigation').addClass('hide'); + $('.icon-cloud_download').addClass('active'); + $('.message-text').text(filename); + $('#progressbar').show(); + }, + extract: function (url, blob) { + $('.message-text').text('extracting page #' + ++page); + }, + progress: function (percent_complete) { + $progressbar.css('width', percent_complete + '%'); + }, + finish: function (pages) { + + $('.icon-unarchive').addClass('ok'); + var name = this.filename.replace(/\.[a-z]+$/, ''); + var id = encodeURIComponent(name.toLowerCase()); + var book = new ComicBook('viewer', pages, options); + + document.title = name; + + $('.toolbar').removeClass('hide'); + $('.navigation').removeClass('hide'); + $('#progressbar').hide(); + $('#viewer').show(); + + book.draw(); + + $(window).on('resize', function () { + book.draw(); + }); + $(window).on('beforeunload', function(e) { + book.destroy(); + }); + } + }); + + } + + + function getPref (arr, name) { + if ((arr.constructor === Array) && (found = arr.filter(function(e) { return e.name === name; }))) { + if (found.hasOwnProperty("value")) { + return found.value; + } + } + }; + + openComicArchive(bookPath, { + currentPage: parseInt(options.session.cursor.value) || 0, + enhance: getPref(options.session.preferences, "enhance") || {}, + manga: getPref(options.session.preferences, "manga") || false, + thumbnails: getPref(options.session.defaults, "thumbnails"), + thumbnailWidth: parseInt(getPref(options.session.defaults, "thumbnailWidth")) || 200, + session: options.session + }); + + return this; +}; + +var ComicBook; +ComicBook = (function ($) { + + + 'use strict'; + + /** + * Merge two arrays. Any properties in b will replace the same properties in + * a. New properties from b will be added to a. + * + * @param a {Object} + * @param b {Object} + */ + function merge(a, b) { + + var prop; + + if (typeof b === 'undefined') { + b = {}; + } + + for (prop in a) { + if (a.hasOwnProperty(prop)) { + if (prop in b) { + continue; + } + b[prop] = a[prop]; + } + } + + return b; + } + + /** + * Exception class. Always throw an instance of this when throwing exceptions. + * + * @param {String} type + * @param {Object} object + * @returns {ComicBookException} + */ + var ComicBookException = { + INVALID_ACTION: 'invalid action', + INVALID_PAGE: 'invalid page', + INVALID_PAGE_TYPE: 'invalid page type', + UNDEFINED_CONTROL: 'undefined control', + INVALID_ZOOM_MODE: 'invalid zoom mode', + INVALID_NAVIGATION_EVENT: 'invalid navigation event' + }; + + function ComicBook(id, srcs, opts) { + + var self = this; + var canvas_id = id; // canvas element id + this.srcs = srcs; // array of image srcs for pages + + var defaults = { + displayMode: (window.innerWidth > window.innerHeight) ? 'double' : 'single', // single / double + zoomMode: 'fitWindow', // manual / fitWidth / fitWindow + manga: false, // true / false + fullscreen: false, // true / false + enhance: {}, // image filters to use + thumbnails: true, // true / false (use thumbnails in index) + thumbnailWidth: 200, // width of thumbnail + sidebarWide: false, // use wide sidbar + currentPage: 0, // current page + keyboard: { + 32: 'next', // space + 34: 'next', // page-down + 39: 'next', // cursor-right + 33: 'previous', // page-up + 37: 'previous', // cursor-left + 36: 'first', // home + 35: 'last', // end + 83: 'sidebar', // s + 84: 'toolbar', // t + 76: 'toggleLayout', // l + 70: 'toggleFullscreen', // f + 27: 'closeSidebar' // esc + }, + vendorPath: 'vendor/', + forward_buffer: 3, + session: { + getCursor: function() {}, + setCursor: function(value) {}, + getBookmark: function(name, type) {}, + setBookmark: function(name, value, type, content) {}, + getDefault: function(name) {}, + setDefault: function(name, value) {}, + getPreference: function(name) {}, + setPreference: function(name, value) {} + } + }; + + var options = {}; + + this.isMobile = false; + + // mobile enhancements + if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini|Mobile/i.test(navigator.userAgent)) { + this.isMobile = true; + document.body.classList.add('mobile'); + + window.addEventListener('load', function () { + setTimeout(function () { + window.scrollTo(0, 1); + }, 0); + }); + } + + window.addEventListener('resize', function () { + self.setLayout((window.innerWidth > window.innerHeight) ? 'double' : 'single'); + }); + + $.extend(true, options, defaults, opts); // options array for internal use + + var no_pages = srcs.length; + var pages = []; // array of preloaded Image objects + var canvas; // the HTML5 canvas object + var context; // the 2d drawing context + var tcv = document.createElement("canvas"); // canvas used for thumbnailer + var tctx = tcv.getContext('2d'); // context used for thumbnailer + var toc = document.getElementById('toc'); // context used for thumbnailer + var loaded = []; // the images that have been loaded so far + var scale = 1; // page zoom scale, 1 = 100% + var is_double_page_spread = false; + var controlsRendered = false; // have the user controls been inserted into the dom yet? + var page_requested = false; // used to request non preloaded pages + var shiv = false; + + /** + * Gets the window.innerWidth - scrollbars + */ + function windowWidth() { + + var height = window.innerHeight + 1; + + if (shiv === false) { + shiv = $(document.createElement('div')) + .attr('id', 'cbr-width-shiv') + .css({ + width: '100%', + position: 'absolute', + top: 0, + zIndex: '-1000' + }); + + $('body').append(shiv); + } + + shiv.height(height); + + return shiv.innerWidth(); + } + + /** + * enables the back button + */ + function checkHash() { + + var hash = getHash(); + + if (hash !== options.currentPage && loaded.indexOf(hash) > -1) { + options.currentPage = hash; + self.draw(); + } + } + + function getHash() { + var hash = parseInt(location.hash.substring(1), 10) - 1 || 0; + if (hash < 0) { + setHash(0); + hash = 0; + } + return hash; + } + + function setHash(pageNo) { + location.hash = pageNo; + } + + // page hash on first load + var hash = getHash(); + + /** + * Setup the canvas element for use throughout the class. + */ + function init() { + + // setup canvas + canvas = document.getElementById(canvas_id); + context = canvas.getContext('2d'); + + // render user controls + if (controlsRendered === false) { + self.renderControls(); + self.tocCreate(no_pages); + controlsRendered = true; + } + + // add page controls + window.addEventListener('keydown', self.navigation, false); + window.addEventListener('hashchange', checkHash, false); + + // fill in metadata + options.session.pagecount = srcs.length; + $('.book-title').text(options.session.title); + $('.book-format').text(options.session.format); + $('.book-pagecount').text(options.session.pagecount); + $('.book-size').text(options.session.size); + } + + window.addEventListener('touchstart', function (e) { + var $el = $(e.target); + if ($el.attr('id') === 'viewer') { + self.toggleToolbar(); + } + }, false); + + /** + * Connect controls to events + */ + ComicBook.prototype.renderControls = function () { + + var controls = {}, $toolbar; + + // set values from preferences or defaults + // do this before connecting listeners to avoid triggering callbacks + for (var prop in options.enhance) { + if(options.enhance.hasOwnProperty(prop)) { + switch (prop) { + case 'brightness': + document.getElementById('brightness').value = options.enhance.brightness['brightness']; + document.getElementById('contrast').value = options.enhance.brightness['contrast']; + break; + case 'sharpen': + document.getElementById('sharpen').value = options.enhance.sharpen['strength']; + break; + case 'desaturate': + $('#image-desaturate').prop('checked', true); + break; + case 'removenoise': + $('#image-removenoise').prop('checked', true); + break; + default: + console.log("unknown enhancement: " + JSON.stringify(prop)); + } + } + }; + + // thumbnail controls + $('#thumbnail-generate').prop('checked', options.thumbnails); + $('#thumbnail-width').val(options.thumbnailWidth); + if (!options.thumbnails) { + $('#toc-populate').addClass('open'); + $('#thumbnail-width').prop('disabled', true); + } + + // connect callbacks + $('.control').each(function () { + + controls[$(this).attr('name')] = $(this); + + // add event listeners to controls that specify callbacks + $(this).find('*').andSelf().filter('[data-action][data-trigger]').each(function () { + + var $this = $(this); + var trigger = $this.data('trigger'); + var action = $this.data('action'); + + // trigger a direct method if exists + if (typeof self[$this.data('action')] === 'function') { + $this.on(trigger, self[action]); + } + + // throw an event to be caught outside if the app code + $this.on(trigger, function (e) { + $(self).trigger(trigger, e); + }); + }); + }); + + this.controls = controls; + + $toolbar = this.getControl('toolbar'); + $toolbar + .find('.manga-' + options.manga).show().end() + .find('.manga-' + !options.manga).hide().end() + .find('.layout').hide().end().find('.layout-' + options.displayMode).show().end() + .find('.fullscreen-' + options.fullscreen).show().end() + .find('.fullscreen-' + !options.fullscreen).hide(); + + if (parent !== window) { + $('.close').removeClass('hide'); + $('.close').on('click', function() { parent.OCA.Files_Reader.Plugin.hide(); }); + } + }; + + ComicBook.prototype.getControl = function (control) { + if (typeof this.controls[control] !== 'object') { + throw ComicBookException.UNDEFINED_CONTROL + ' ' + control; + } + return this.controls[control]; + }; + + ComicBook.prototype.showControl = function (control) { + this.getControl(control).show().addClass('open'); + }; + + ComicBook.prototype.hideControl = function (control) { + this.getControl(control).removeClass('open').hide(); + }; + + ComicBook.prototype.toggleControl = function (control) { + this.getControl(control).toggle().toggleClass('open'); + }; + + ComicBook.prototype.toggleLayout = function () { + self.setLayout((options.displayMode === 'single') ? 'double' : 'single'); + }; + + ComicBook.prototype.setLayout = function (layout) { + var $toolbar = self.getControl('toolbar'); + options.displayMode = (layout === 'single') ? 'single' : 'double'; + + $toolbar.find('.layout').hide().end().find('.layout-' + options.displayMode).show(); + + self.drawPage(); + }; + + + /** + * Create thumbnail for image + * + * @return Image + */ + ComicBook.prototype.getThumb = function (image) { + var thumb = new Image(); + var scale = image.width / options.thumbnailWidth; + tcv.width = options.thumbnailWidth; + tcv.height = Math.floor(image.height / scale); + tctx.drawImage(image, 0, 0, tcv.width, tcv.height); + thumb.src = tcv.toDataURL(); + tctx.clearRect(0, 0, tcv.width, tcv.height); + + return thumb; + }; + + /** + * Create empty TOC with placeholder images + */ + ComicBook.prototype.tocCreate = function (no_pages) { + // use small image with reasonable aspect ratio + tcv.width = 5; + tcv.height = 7; + // transparent, style with .placeholder in CSS + tctx.fillStyle = "rgba(200, 200, 200, 0)"; + tctx.fillRect(0, 0, tcv.width, tcv.height); + var imgsrc = tcv.toDataURL(); + + for(var i = 0; i < no_pages; i++) { + var item = document.createElement('li'); + item.setAttribute("id", "page-" + parseInt(i + 1)); + var placeholder = new Image(); + placeholder.src = imgsrc; + var label = document.createElement('span'); + label.innerHTML = i + 1; + item.appendChild(placeholder); + item.appendChild(label); + toc.appendChild(item); + } + }; + + /** + * Insert thumbnail into TOC + */ + ComicBook.prototype.tocInsert = function (image, page, replace) { + var placeholder = toc.children[page].firstChild; + if (replace === true) { + placeholder.parentNode.replaceChild( + self.getThumb(image), + placeholder + ); + } + + toc.children[page].addEventListener('click', function (e) { + self.drawPage(page + 1, true); + }); + }; + + /** + * Populate TOC on demand + */ + ComicBook.prototype.tocPopulate = function () { + var i = 0; + while (i < srcs.length) { + self.tocInsert(pages[i], i, true); + i++; + } + + // set, but don't save for future sessions + options.thumbnails = true; + $('#toc-populate').removeClass('open'); + }; + + /** + * Get the image for a given page. + * + * @return Image + */ + ComicBook.prototype.getPage = function (i) { + + if (i < 0 || i > srcs.length - 1) { + throw ComicBookException.INVALID_PAGE + ' ' + i; + } + + if (typeof pages[i] === 'object') { + return pages[i]; + } else { + page_requested = i; + this.showControl('loadingOverlay'); + } + }; + + /** + * @see #preload + */ + ComicBook.prototype.draw = function () { + + init(); + + // resize navigation controls + $('.navigate').outerHeight(window.innerHeight); + $('.overlay').outerWidth(windowWidth()).height(window.innerHeight); + + // preload images if needed + if (pages.length !== no_pages) { + this.preload(); + } else { + this.drawPage(); + } + }; + + /** + * Zoom the canvas + * + * @param new_scale {Number} Scale the canvas to this ratio + */ + ComicBook.prototype.zoom = function (new_scale) { + options.zoomMode = 'manual'; + scale = new_scale; + if (typeof this.getPage(options.currentPage) === 'object') { + this.drawPage(); + } + }; + + ComicBook.prototype.zoomIn = function () { + self.zoom(scale + 0.1); + }; + + ComicBook.prototype.zoomOut = function () { + self.zoom(scale - 0.1); + }; + + ComicBook.prototype.fitWidth = function () { + options.zoomMode = 'fitWidth'; + self.drawPage(); + }; + + ComicBook.prototype.fitWindow = function () { + options.zoomMode = 'fitWindow'; + self.drawPage(); + }; + + /** + * Preload all images, draw the page only after a given number have been loaded. + * + * @see #drawPage + */ + ComicBook.prototype.preload = function () { + + var i = options.currentPage; // the current page counter for this method + var rendered = false; + var queue = []; + + this.showControl('loadingOverlay'); + + function loadImage(i) { + + var page = new Image(); + page.src = srcs[i]; + + page.onload = function () { + + pages[i] = this; + + self.tocInsert(this, i, options.thumbnails); + + loaded.push(i); + + $('#cbr-progress-bar .progressbar-value').css('width', Math.floor((loaded.length / no_pages) * 100) + '%'); + + // double page mode needs an extra page added + var buffer = (options.displayMode === 'double' && options.currentPage < srcs.length - 1) ? 1 : 0; + + // start rendering the comic when the requested page is ready + if ((rendered === false && ($.inArray(options.currentPage + buffer, loaded) !== -1) || + (typeof page_requested === 'number' && $.inArray(page_requested, loaded) !== -1))) { + // if the user is waiting for a page to be loaded, render that one instead of the default options.currentPage + if (typeof page_requested === 'number') { + options.currentPage = page_requested - 1; + page_requested = false; + } + + self.drawPage(); + self.hideControl('loadingOverlay'); + rendered = true; + } + + if (queue.length) { + loadImage(queue[0]); + queue.splice(0, 1); + } else { + $('#cbr-status').delay(500).fadeOut(); + } + }; + } + + // loads pages in both directions so you don't have to wait for all pages + // to be loaded before you can scroll backwards + function preload(start, stop) { + + var j = 0; + var count = 1; + var forward = start; + var backward = start - 1; + + while (forward <= stop) { + + if (count > options.forward_buffer && backward > -1) { + queue.push(backward); + backward--; + count = 0; + } else { + queue.push(forward); + forward++; + } + count++; + } + + while (backward > -1) { + queue.push(backward); + backward--; + } + + loadImage(queue[j]); + } + + preload(i, srcs.length - 1); + }; + + ComicBook.prototype.pageLoaded = function (page_no) { + return (typeof loaded[page_no - 1] !== 'undefined'); + }; + + /** + * Draw the current page in the canvas + */ + ComicBook.prototype.drawPage = function (page_no, reset_scroll) { + + var scrollY; + + reset_scroll = (typeof reset_scroll !== 'undefined') ? reset_scroll : true; + scrollY = reset_scroll ? 0 : window.scrollY; + + // if a specific page is given try to render it, if not bail and wait for preload() to render it + if (typeof page_no === 'number' && page_no < srcs.length && page_no > 0) { + options.currentPage = page_no - 1; + if (!this.pageLoaded(page_no)) { + this.showControl('loadingOverlay'); + return; + } + } + + if (options.currentPage < 0) { + options.currentPage = 0; + } + + var zoom_scale; + var offsetW = 0, + offsetH = 0; + + var page = self.getPage(options.currentPage); + var page2 = false; + + if (options.displayMode === 'double' && options.currentPage < srcs.length - 1) { + page2 = self.getPage(options.currentPage + 1); + } + + if (typeof page !== 'object') { + throw ComicBookException.INVALID_PAGE_TYPE + ' ' + typeof page; + } + + var width = page.width, + height = page.height; + + // reset the canvas to stop duplicate pages showing + canvas.width = 0; + canvas.height = 0; + + // show double page spreads on a single page + is_double_page_spread = ( + typeof page2 === 'object' && + (page.width > page.height || page2.width > page2.height) && + options.displayMode === 'double' + ); + if (is_double_page_spread) { + options.displayMode = 'single'; + } + + if (options.displayMode === 'double') { + + // for double page spreads, factor in the width of both pages + if (typeof page2 === 'object') { + width += page2.width; + } + + // if this is the last page and there is no page2, still keep the canvas wide + else { + width += width; + } + } + + // update the page scale if a non manual mode has been chosen + switch (options.zoomMode) { + + case 'manual': + document.body.style.overflowX = 'auto'; + zoom_scale = (options.displayMode === 'double') ? scale * 2 : scale; + break; + + case 'fitWidth': + document.body.style.overflowX = 'hidden'; + + // scale up if the window is wider than the page, scale down if the window + // is narrower than the page + zoom_scale = (windowWidth() > width) ? ((windowWidth() - width) / windowWidth()) + 1 : windowWidth() / width; + + // update the interal scale var so switching zoomModes while zooming will be smooth + scale = zoom_scale; + break; + + case 'fitWindow': + document.body.style.overflowX = 'hidden'; + + var width_scale = (windowWidth() > width) ? + ((windowWidth() - width) / windowWidth()) + 1 // scale up if the window is wider than the page + : + windowWidth() / width; // scale down if the window is narrower than the page + var windowHeight = window.innerHeight; + var height_scale = (windowHeight > height) ? + ((windowHeight - height) / windowHeight) + 1 // scale up if the window is wider than the page + : + windowHeight / height; // scale down if the window is narrower than the page + + zoom_scale = (width_scale > height_scale) ? height_scale : width_scale; + scale = zoom_scale; + break; + + default: + throw ComicBookException.INVALID_ZOOM_MODE + ' ' + options.zoomMode; + } + + var canvas_width = page.width * zoom_scale; + var canvas_height = page.height * zoom_scale; + + var page_width = (options.zoomMode === 'manual') ? page.width * scale : canvas_width; + var page_height = (options.zoomMode === 'manual') ? page.height * scale : canvas_height; + + canvas_height = page_height; + + // make sure the canvas is always at least full screen, even if the page is more narrow than the screen + canvas.width = (canvas_width < windowWidth()) ? windowWidth() : canvas_width; + canvas.height = (canvas_height < window.innerHeight) ? window.innerHeight : canvas_height; + + // always keep pages centered + if (options.zoomMode === 'manual' || options.zoomMode === 'fitWindow') { + + // work out a horizontal position + if (canvas_width < windowWidth()) { + offsetW = (windowWidth() - page_width) / 2; + if (options.displayMode === 'double') { + offsetW = offsetW - page_width / 2; + } + } + + // work out a vertical position + if (canvas_height < window.innerHeight) { + offsetH = (window.innerHeight - page_height) / 2; + } + } + + // in manga double page mode reverse the page(s) + if (options.manga && options.displayMode === 'double' && typeof page2 === 'object') { + var tmpPage = page; + var tmpPage2 = page2; + page = tmpPage2; + page2 = tmpPage; + } + + // draw the page(s) + context.drawImage(page, offsetW, offsetH, page_width, page_height); + if (options.displayMode === 'double' && typeof page2 === 'object') { + context.drawImage(page2, page_width + offsetW, offsetH, page_width, page_height); + } + + this.pixastic = new Pixastic(context, options.vendorPath + 'pixastic/'); + + // apply any image enhancements previously defined + $.each(options.enhance, function (action, options) { + self.enhance[action](options); + }); + + var current_page = + (options.displayMode === 'double' && + options.currentPage + 2 <= srcs.length) ? (options.currentPage + 1) + '-' + (options.currentPage + 2) : options.currentPage + 1; + + this.getControl('toolbar') + .find('.current-page').text(current_page) + .end() + .find('.page-count').text(srcs.length); + + // revert page mode back to double if it was auto switched for a double page spread + if (is_double_page_spread) { + options.displayMode = 'double'; + } + + // disable the fit width button if needed + $('button.cbr-fit-width').attr('disabled', (options.zoomMode === 'fitWidth')); + $('button.cbr-fit-window').attr('disabled', (options.zoomMode === 'fitWindow')); + + // disable prev/next buttons if not needed + $('.navigate').show(); + if (options.currentPage === 0) { + if (options.manga) { + $('.navigate-left').show(); + $('.navigate-right').hide(); + } else { + $('.navigate-left').hide(); + $('.navigate-right').show(); + } + } + + if (options.currentPage === srcs.length - 1 || (typeof page2 === 'object' && options.currentPage === srcs.length - 2)) { + if (options.manga) { + $('.navigate-left').hide(); + $('.navigate-right').show(); + } else { + $('.navigate-left').show(); + $('.navigate-right').hide(); + } + } + + if (options.currentPage !== getHash()) { + $(this).trigger('navigate'); + } + + // update hash location + if (getHash() !== options.currentPage) { + setHash(options.currentPage + 1); + } + + options.session.setCursor(options.currentPage); + }; + + /** + * Increment the counter and draw the page in the canvas + * + * @see #drawPage + */ + ComicBook.prototype.drawNextPage = function () { + + var page; + + try { + page = self.getPage(options.currentPage + 1); + } catch (e) { + } + + if (!page) { + return false; + } + + if (options.currentPage + 1 < pages.length) { + options.currentPage += (options.displayMode === 'single' || is_double_page_spread) ? 1 : 2; + try { + self.drawPage(); + } catch (e) { + } + } + + // make sure the top of the page is in view + window.scroll(0, 0); + }; + + /** + * Decrement the counter and draw the page in the canvas + * + * @see #drawPage + */ + ComicBook.prototype.drawPrevPage = function () { + + var page; + + try { + page = self.getPage(options.currentPage - 1); + } catch (e) { + } + + if (!page) { + return false; + } + + is_double_page_spread = (page.width > page.height); // need to run double page check again here as we are going backwards + + if (options.currentPage > 0) { + options.currentPage -= (options.displayMode === 'single' || is_double_page_spread) ? 1 : 2; + self.drawPage(); + } + + // make sure the top of the page is in view + window.scroll(0, 0); + }; + + /* default settings */ + + ComicBook.prototype.thumbnails = function() { + if ($(this).is(':checked')) { + options.thumbnails = true; + document.getElementById('thumbnail-width').disabled = false; + } else { + options.thumbnails = false; + document.getElementById('thumbnail-width').disabled = true; + } + + options.session.setDefault("thumbnails", options.thumbnails); + }; + + ComicBook.prototype.thumbnailWidth = function() { + options.thumbnailWidth = $(this).val(); + options.session.setDefault("thumbnailWidth", options.thumbnailWidth); + }; + + ComicBook.prototype.sidebarWide = function (wide) { + if (typeof(wide) !== "boolean") { + wide = ($(this).is(':checked') === true); + } + + if (wide) { + options.sidebarWide = true; + document.getElementById('sidebar').classList.add('wide'); + } else { + options.sidebarWide = false; + document.getElementById('sidebar').classList.remove('wide'); + self.sidebarWidth(0); + } + + options.session.setDefault("sidebarWide", options.sidebarWide); + }; + + ComicBook.prototype.sidebarWidth = function(width) { + if (typeof(width) !== "number") { + width = $(this).val(); + } + options.sidebarWidth = width; + + // width === 0 is interpreted as 'use value from CSS' + if (options.sidebarWidth > 0) { + document.getElementById('sidebar').style.width = options.sidebarWidth + "%"; + } else { + document.getElementById('sidebar').style.width = ""; + } + + options.session.setDefault("sidebarWidth", options.sidebarWidth); + }; + + ComicBook.prototype.resetSidebar = function () { + self.sidebarWide(false); + self.sidebarWidth(0); + }; + + /* book-specific settings */ + + ComicBook.prototype.brightness = function () { + var $brightness = { + brightness: $('#brightness').val(), + contrast: $('#contrast').val() + }; + + self.enhance.brightness($brightness); + options.enhance.brightness = $brightness; + options.session.setPreference("enhance",options.enhance); + }; + + ComicBook.prototype.sharpen = function () { + options.enhance.sharpen = $(this).val(); + self.enhance.sharpen({ + strength: options.enhance.sharpen + }); + + options.session.setPreference("enhance",options.enhance); + }; + + ComicBook.prototype.desaturate = function () { + if ($(this).is(':checked')) { + options.enhance.desaturate = {}; + self.enhance.desaturate(); + } else { + delete options.enhance.desaturate; + self.enhance.resaturate(); + } + + options.session.setPreference("enhance",options.enhance); + }; + + ComicBook.prototype.removenoise = function () { + if ($(this).is(':checked')) { + options.enhance.removenoise = {}; + self.enhance.removenoise(); + } else { + delete options.enhance.removenoise; + self.enhance.unremovenoise(); + } + + options.session.setPreference("enhance",options.enhance); + }; + + ComicBook.prototype.resetEnhancements = function () { + self.enhance.reset(); + options.session.setPreference("enhance",options.enhance); + }; + + /** + * Apply image enhancements to the canvas. + */ + ComicBook.prototype.enhance = { + + /** + * Reset enhancements. + * This can reset a specific enhancement if the method name is passed, or + * it will reset all. + * + * @param method {string} the specific enhancement to reset + */ + reset: function (method) { + if (!method) { + options.enhance = {}; + } else { + delete options.enhance[method]; + } + self.drawPage(null, false); + }, + + /** + * Pixastic progress callback + * @param {float} progress + */ + // progress: function (progress) { + progress: function () { + // console.info(Math.floor(progress * 100)); + }, + + /** + * Pixastic on complete callback + */ + done: function () { + + }, + + /** + * Adjust brightness / contrast + * + * params + * brightness (int) -150 to 150 + * contrast: (float) -1 to infinity + * + * @param {Object} params Brightness & contrast levels + * @param {Boolean} reset Reset before applying more enhancements? + */ + brightness: function (params, reset) { + + if (reset !== false) { + this.reset('brightness'); + } + + // merge user options with defaults + var opts = merge({ + brightness: 0, + contrast: 0 + }, params); + + options.enhance.brightness = opts; + + // run the enhancement + self.pixastic.brightness({ + brightness: opts.brightness, + contrast: opts.contrast + }).done(this.done, this.progress); + }, + + /** + * Force black and white + */ + desaturate: function () { + options.enhance.desaturate = {}; + self.pixastic.desaturate().done(this.done, this.progress); + }, + + /** + * Undo desaturate + */ + resaturate: function () { + delete options.enhance.desaturate; + self.drawPage(null, false); + }, + + /** + * Sharpen + * + * options: + * strength: number (-1 to infinity) + * + * @param {Object} options + */ + sharpen: function (params) { + + this.desharpen(); + + var opts = merge({ + strength: 0 + }, params); + + options.enhance.sharpen = opts; + + self.pixastic.sharpen3x3({ + strength: opts.strength + }).done(this.done, this.progress); + }, + + desharpen: function () { + delete options.enhance.sharpen; + self.drawPage(null, false); + }, + + /** + * Remove noise + */ + removenoise: function () { + options.enhance.removenoise = {}; + self.pixastic.removenoise().done(this.done, this.progress); + }, + + unremovenoise: function () { + delete options.enhance.removenoise; + self.drawPage(null, false); + } + }; + + ComicBook.prototype.navigation = function (e) { + + // disable navigation when the overlay is showing + if ($('#cbr-loading-overlay').is(':visible')) { + return false; + } + + var side = false, page_no = false; + + switch (e.type) { + + case 'click': + side = e.currentTarget.getAttribute('data-navigate-side'); + break; + + case 'keydown': + + // console.log("keydown: " + e.keyCode); + + switch (options.keyboard[e.keyCode]) { + case 'previous': + side = 'left'; + break; + case 'next': + side = 'right'; + break; + case 'first': + page_no = 1; + break; + case 'last': + page_no = srcs.length - 1; + break; + case 'sidebar': + self.toggleSidebar(); + break; + case 'toolbar': + self.toggleToolbar(); + break; + case 'toggleLayout': + self.toggleLayout(); + break; + case 'toggleFullscreen': + self.toggleFullscreen(); + break; + case 'closeSidebar': + self.closeSidebar(); + break; + default: + /* + throw ComicBookException.INVALID_NAVIGATION_EVENT + ' ' + e.type; + */ + } + break; + + default: + throw ComicBookException.INVALID_NAVIGATION_EVENT + ' ' + e.type; + } + + if (side) { + + e.stopPropagation(); + + // western style (left to right) + if (!options.manga) { + if (side === 'left') { + self.drawPrevPage(); + } + if (side === 'right') { + self.drawNextPage(); + } + } + // manga style (right to left) + else { + if (side === 'left') { + self.drawNextPage(); + } + if (side === 'right') { + self.drawPrevPage(); + } + } + + return false; + } + + if (page_no) { + self.drawPage(page_no, true); + return false; + } + }; + + ComicBook.prototype.toggleReadingMode = function () { + options.manga = !options.manga; + self.getControl('toolbar') + .find('.manga-' + options.manga).show().end() + .find('.manga-' + !options.manga).hide(); + options.session.setPreference("manga",options.manga); + }; + + ComicBook.prototype.toggleToolbar = function () { + self.toggleControl('toolbar'); + }; + + ComicBook.prototype.openSidebar = function () { + $('.sidebar').addClass('open'); + $('.toolbar').addClass('open'); + self.showControl('busyOverlay'); + self.scrollToc(); + }; + + ComicBook.prototype.closeSidebar = function () { + $('.sidebar').removeClass('open'); + $('.toolbar').removeClass('open'); + self.toggleToolbar(); + self.hideControl('busyOverlay'); + }; + + ComicBook.prototype.toggleSidebar = function () { + $('.sidebar').hasClass('open') + ? self.closeSidebar() + : self.openSidebar(); + }; + + ComicBook.prototype.toggleFullscreen = function () { + options.fullscreen = !options.fullscreen; + self.getControl('toolbar') + .find('.fullscreen-' + options.fullscreen).show().end() + .find('.fullscreen-' + !options.fullscreen).hide(); + if (options.fullscreen) { + screenfull.request($('#container')[0]); + } else { + screenfull.exit($('#container')[0]); + } + }; + + + /* + * Scroll TOC to page (default: current page) + */ + ComicBook.prototype.scrollToc = function (page) { + if (page === undefined) { + page = options.currentPage; + } + + document.getElementById('toc').parentNode.scrollTop = + document.getElementById('page-' + String(page + 1)).offsetTop + - Math.floor($('.panels').height() * 1.5); + }; + + ComicBook.prototype.showToc = function () { + self.getControl('sidebar') + .find('.open').removeClass('open').end() + .find('.toc-view').addClass('open'); + if (!options.thumbnails) { + $('#toc-populate').addClass('open'); + } + }; + + ComicBook.prototype.showBookSettings = function () { + self.getControl('sidebar') + .find('.open').removeClass('open').end() + .find('.book-settings-view').addClass('open'); + }; + + ComicBook.prototype.showSettings = function () { + self.getControl('sidebar') + .find('.open').removeClass('open').end() + .find('.settings-view').addClass('open'); + }; + + ComicBook.prototype.destroy = function () { + + $.each(this.controls, function (name, $control) { + $control.remove(); + }); + + canvas.width = 0; + canvas.height = 0; + + window.removeEventListener('keydown', this.navigation, false); + window.removeEventListener('hashchange', checkHash, false); + + setHash(''); + + // $(this).trigger('destroy'); + }; + + } + + return ComicBook; + +})(jQuery); + +(function(root, $) { + + var previousReader = root.cbReader || {}; + + var cbReader = root.cbReader = function(path, options) { + return new CBRJS.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 = cbReader; + } + +})(window, jQuery); + + diff --git a/files_reader/vendor/cbrjs/css/cbr.css b/files_reader/vendor/cbrjs/css/cbr.css new file mode 100644 index 0000000..ffdbfd1 --- /dev/null +++ b/files_reader/vendor/cbrjs/css/cbr.css @@ -0,0 +1,448 @@ +/* reader */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} + +.cbr-control { + font-family: helvetica, arial, sans-serif; + font-size: 12px; +} + +.cbr-control { + color: #fff; + background-color: #111; + padding: 10px; + position: fixed !important; + box-shadow: 0 0 4px #000; +} + +.navigate { + top: 0; + margin: 0; + cursor: pointer; + width: 20%; + opacity: 0; + background: center no-repeat; + box-shadow: none; + padding: 0 3em; +} + +.navigate > span { + color: #000; + font-size: 10em; + background-color: rgba(255, 255, 255, 0.8); + border-radius: 1em; + top: 45%; + position: relative; +} + +body:not(.mobile) .navigate:hover { + opacity: 1; +} + +.navigate-left { + left: 0; +} + +.navigate-left > span { + float: left; +} + +.navigate-right { + right: 0; +} + +.navigate-right > span { + float: right; +} + +.toggle-controls { + cursor: pointer; + width: 20%; + height: 20%; + left: 40%; + top: 40%; + border: none; + position: fixed; +} + +#cbr-loading-overlay { + z-index: 100; + background: #000 url("img/loading.gif") no-repeat center; +} + +.overlay { + opacity: 0.7; + box-shadow: none; +} + +#cbr-status { + z-index: 101; + font-size: 12px; + right: 0; + bottom: 0; + margin: 8px; + border-radius: 4px; +} + +#cbr-progress-bar { + width: 200px; +} + +#cbr-progress-bar, +#cbr-progress-bar .progressbar-value { + height: 3px; +} + +#cbr-progress-bar .progressbar-value { + width: 0; + background: #86C441; + border-color: #3E7600; +} + +* { + -webkit-user-select: none; + -webkit-touch-callout: none; + -webkit-tap-highlight-color: rgba(0,0,0,0); +} + +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 12px; + line-height: 20px; + color: #333; +} + +button, +input, +label { + cursor: pointer; +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} + +.toolbar, +.panels { + position: absolute; + color: white; + /* overflow: visible; */ + background: #4e4e4e; + /* left: 0; */ + /* right: 0; */ + width: 100%; +} + +.toolbar { + /* position: fixed; */ + z-index: 99; + /* margin-bottom: 0; */ + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4); + opacity: 0; + transition: opacity 0.1s ease-in-out; + text-align: center; +} + +.toolbar .metainfo { + font-size: 1.2em; + top: 0.5em; +} + +.metainfo span { + margin: 0 0.5em; +} + +.mobile .metainfo .book-title, +.mobile .metainfo .title-separator { + display: none; +} + +body:not(.mobile) .toolbar:hover, +.mobile .toolbar, +.toolbar.open { + opacity: 1; +} + +.toolbar div { + display: inline-block; + position: relative; +} + +.toolbar .separator { + /* border: solid 1px; */ + height: 1em; + opacity: 0.5; +} + +.toolbar button, .sidebar button { + color: white; + border: none; + background-color: transparent; + padding: 0; +} + +.toolbar div > button, +.sidebar div > button { + font-size: 1.5em; + padding: 0.5em; + margin: 0; +} + +.mobile .toolbar div > button, +.mobile .sidebar div > button { + padding: 0.5em; + margin: 0; +} + +body:not(.mobile) .toolbar div > button:hover, +body:not(.mobile) .sidebar div > button:hover { + color: #8CC746; +} + +body:not(.mobile) .toolbar button[data-action=close]:hover { + color: #FF6464; +} + +.hide { + display: none !important; +} + +/* sidebar */ +.sidebar.open { + box-shadow: 3px 0px 3px 0px rgba(0, 0, 0, 0.4); + display: block; +} + +.sidebar { + background: #6b6b6b; + position: fixed; + top: 0; + min-width: 25em; + height: 100%; + overflow: hidden; + display: none; + z-index: 100; +} + +.sidebar.wide { + width: 20%; +} + +.panels { + overflow: hidden; +} + +.panels .open { + background-color: #6B6B6B; +} + +.view.open { + display: block !important; +} + +#toc-populate.open { + display: inline-block !important; + background-color: #4e4e4e; +} + +.view { + overflow-y: scroll; + display: none !important; + width: 100%; + position: absolute; + top: 3em; + bottom: 0; + text-align: center; +} + +.toc-view li { + margin: 1em; + font-family: Georgia, "Times New Roman", Times, serif; +} + +.toc-view img, .placeholder { + width: 100%; + position: relative; + background-color: #999; +} + +.toc-view span { + position: absolute; + transform: translate3d(-3em, 3em, 0); + font-size: 5em; + font-weight: bold; + color: #F8F8F8; + text-shadow: 0.05em 0.05em 0.02em rgba(70, 70, 70, 0.8); + -webkit-text-stroke: 2px black; + background-color: rgba(255,255,255,0.7); + border-radius: 1em; + box-shadow: 0 0 0.3em rgba(255,255,255,1); + padding: 0.5em; +} + +.settings-container { + text-align: left; + display: inline-block; + width: 95%; + font-size: 1em; + background: #F8F8F8; + color: #111; + padding-top: 1em; + padding-bottom: 1em; + margin-top: 1em; + border-radius: 4px; + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4); +} + +.settings-container label { + margin-right: 1em; +} + +.settings-container > label { + font-weight: bold; + width: 100%; + display: inline-block; + margin-bottom: 1em; + text-align: center; +} + +.view .control-group input[type=range] { + width: 80%; + float: right; + margin: 0; +} + +.view .control-group { + padding: 1em; +} + +.view .sliders { + font-size: 1.5em; +} + +.view .control-group span { + float: left; + margin: 0 2px; + clear: both; +} + +.view .control-group input[type=reset] { + float: right; +} + +.metadata { + padding: 1em; + margin: 1em; +} + +.metadata table { + font-size: 1.2em; + color: #F8F8F8; +} + +.metadata td:nth-child(1) { + font-weight: bold; + padding-right: 1em; +} + +/* END sidebar */ + +/* progressbar (loading/unarchiving) */ + +.progress, .bar { + width: 100%; + height: 0.3em; + margin: 0; + padding: 0; + display: inline-block; +} +.progress { + border: none; +} +.bar { + width: 0; + background-color: red; + box-shadow: 0px 1px 3px rgba(0,0,0,.6); +} + +.message { + margin: 3em; +} + +.message-icons { + font-size: 3em; + color: lightgrey; + vertical-align: middle; +} + +.message-text { + font-size: 1.5em; + color: black; +} + +.active { + color:black; +} + +.ok { + color:green; +} + +.error { + color:red; +} + +/* END progressbar */ diff --git a/files_reader/vendor/cbrjs/css/idevice.css b/files_reader/vendor/cbrjs/css/idevice.css new file mode 100644 index 0000000..e8da954 --- /dev/null +++ b/files_reader/vendor/cbrjs/css/idevice.css @@ -0,0 +1,4 @@ +.view { + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} diff --git a/files_reader/vendor/cbrjs/img/loading.gif b/files_reader/vendor/cbrjs/img/loading.gif new file mode 100644 index 0000000..95350aa Binary files /dev/null and b/files_reader/vendor/cbrjs/img/loading.gif differ diff --git a/files_reader/css/annotations.css b/files_reader/vendor/epubjs/css/annotations.css similarity index 100% rename from files_reader/css/annotations.css rename to files_reader/vendor/epubjs/css/annotations.css diff --git a/files_reader/css/font/fontello.eot b/files_reader/vendor/epubjs/css/font/fontello.eot similarity index 100% rename from files_reader/css/font/fontello.eot rename to files_reader/vendor/epubjs/css/font/fontello.eot diff --git a/files_reader/css/font/fontello.svg b/files_reader/vendor/epubjs/css/font/fontello.svg similarity index 100% rename from files_reader/css/font/fontello.svg rename to files_reader/vendor/epubjs/css/font/fontello.svg diff --git a/files_reader/css/font/fontello.ttf b/files_reader/vendor/epubjs/css/font/fontello.ttf similarity index 100% rename from files_reader/css/font/fontello.ttf rename to files_reader/vendor/epubjs/css/font/fontello.ttf diff --git a/files_reader/css/font/fontello.woff b/files_reader/vendor/epubjs/css/font/fontello.woff similarity index 100% rename from files_reader/css/font/fontello.woff rename to files_reader/vendor/epubjs/css/font/fontello.woff diff --git a/files_reader/vendor/epubjs/css/idevice.css b/files_reader/vendor/epubjs/css/idevice.css new file mode 100644 index 0000000..680c7f8 --- /dev/null +++ b/files_reader/vendor/epubjs/css/idevice.css @@ -0,0 +1,14 @@ +.notes, +.search-results, +.view { + overflow-y: scroll; + -webkit-overflow-scrolling: touch; +} + +.search-results { + height: calc(100% - 5em); +} + +.notes { + height: calc(100% - 9em); +} diff --git a/files_reader/vendor/epubjs/css/main.css b/files_reader/vendor/epubjs/css/main.css new file mode 100644 index 0000000..8dd25de --- /dev/null +++ b/files_reader/vendor/epubjs/css/main.css @@ -0,0 +1,329 @@ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +fieldset { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} + +body { + background: #4e4e4e; + overflow: hidden; + font-style: +} + +#main { + position: absolute; + width: 100%; + height: 100%; + right: 0; + border-radius: 5px; + background: #fff; + overflow: hidden; + -webkit-transition: -webkit-transform .4s, width .2s; + -moz-transition: -webkit-transform .4s, width .2s; + + -moz-box-shadow: inset 0 0 50px rgba(0,0,0,.1); + -webkit-box-shadow: inset 0 0 50px rgba(0,0,0,.1); + box-shadow: inset 0 0 50px rgba(0,0,0,.1); +} + +#titlebar { + padding: 0.5em; + color: #4f4f4f; + font-weight: 100; + font-family: Georgia, "Times New Roman", Times, serif; + opacity: .5; + text-align: center; + -webkit-transition: opacity .5s; + -moz-transition: opacity .5s; + z-index: 10; + position: fixed; + width: 100%; +} + +#titlebar:hover { + opacity: 1; +} + +#titlebar a { + width: 1em; + height: 1em; + overflow: hidden; + display: inline-block; + opacity: .5; + padding: 0.2em; + border-radius: 0.2em; + border: 1px rgba(0,0,0,0) solid; +} + +#titlebar a::before { + visibility: visible; +} + +#titlebar a:hover { + opacity: .8; + border: 1px rgba(0,0,0,.2) solid; +} + +#titlebar a:active { + opacity: 1; + color: rgba(0,0,0,.6); + box-shadow: inset 0 0 6px rgba(155,155,155,.8); +} + +#book-title { + font-weight: 600; +} + +#title-seperator { + display: none; +} + +#title-controls { + margin-left: 1em; + margin-right: 1em; +} + +#viewer { + width: 80%; + height: 80%; + top: 10%; + margin: auto; + max-width: 72em; + z-index: 2; + position: relative; + overflow: hidden; +} + +#viewer iframe { + border: none; +} + +#prev, #next { + position: absolute; + top: 10%; + height: 80%; + margin: 0; +} + +.touch_nav { + width: 35%; +} + +.arrow div { + display: table-cell; + vertical-align: middle; +} + + +#prev { + left: 0; + padding-left: 2em; +} + +#next { + right: 0; + padding-right: 2em; + text-align: right; +} + +.arrow { + font-size: 64px; + /* color: #E2E2E2; */ + font-family: arial, sans-serif; + font-weight: bold; + cursor: pointer; + display: table; + z-index: 3; + opacity: 0.2; +} + +.arrow:hover { + /* color: #777; */ + opacity: 0.5; +} + +.arrow:active, +.arrow.active { + color: #000; +} + +#main.closed { + transform: translate(25em, 0); +} + +#main.single { + width: calc(100% - 25em); +} + +#metainfo { + display: inline-block; + text-align: center; + max-width: 80%; + min-width: 20%; +} + +#divider { + position: absolute; + width: 1px; + border-right: 1px #000 solid; + height: 80%; + z-index: 100; + left: 50%; + margin-left: -1px; + top: 10%; + opacity: .15; + box-shadow: -2px 0 15px rgba(0, 0, 0, 1); + display: none; +} + +#divider.show { + display: block; +} + +#loader { + position: absolute; + z-index: 10; + left: 50%; + top: 50%; + margin: -33px 0 0 -33px; +} + +.pull-left { + float: left; +} + +.pull-right { + float: right; +} + +.highlight { + background-color: yellow; +} + +.overlay { + position: fixed; + width: 100%; + height: 100%; + visibility: hidden; + top: 0; + left: 0; + z-index: 1000; + opacity: 0; + background: rgba(255,255,255,0.8); + -webkit-transition: all 0.3s; + -moz-transition: all 0.3s; + transition: all 0.3s; +} + +.hide { + display: none !important; +} + +.translucent { + opacity: 0 !important; +} + +@media only screen and (max-width: 1040px) { +/* + #viewer{ + width: 50%; + margin-left: 25%; + } + */ + #divider, + #divider.show { + display: none; + } +} + +@media only screen and (max-width: 900px) { +/* + #viewer{ + width: 60%; + margin-left: 20%; + } +*/ + #prev { + padding-left: 20px; + } + + #next { + padding-right: 20px; + } +} + +@media only screen and (max-width: 550px) { +/* + #viewer{ + width: 80%; + margin-left: 10%; + } + */ + #viewer { + width: 95%; + } + + #prev { + padding-left: 0; + } + + #next { + padding-right: 0; + } + + .arrow div { + text-indent: 100%; + white-space: nowrap; + overflow: hidden; + } + + /* + #main { + -webkit-transform: translate(0, 0); + -moz-transform: translate(0, 0); + -webkit-transition: -webkit-transform .3s; + -moz-transition: -moz-transform .3s; + } + + #main.closed { + -webkit-transform: translate(260px, 0); + -moz-transform: translate(260px, 0); + } + */ + #titlebar { + /* font-size: 16px; */ + /* margin: 0 50px 0 50px; */ + } + + #metainfo { + font-size: 10px; + } + /* + #tocView { + width: 260px; + } + + #tocView li { + font-size: 12px; + } + + #tocView > ul{ + padding-left: 10px; + -webkit-padding-start:; + } + */ +} + diff --git a/files_reader/css/normalize.css b/files_reader/vendor/epubjs/css/normalize.css similarity index 100% rename from files_reader/css/normalize.css rename to files_reader/vendor/epubjs/css/normalize.css diff --git a/files_reader/css/popup.css b/files_reader/vendor/epubjs/css/popup.css similarity index 86% rename from files_reader/css/popup.css rename to files_reader/vendor/epubjs/css/popup.css index c41aac7..65c2f9b 100644 --- a/files_reader/css/popup.css +++ b/files_reader/vendor/epubjs/css/popup.css @@ -93,4 +93,17 @@ .popup.show, .popup.on { display: block; -} \ No newline at end of file +} + +.note-marker, +.EPUBJS-CFI-MARKER, +.EPUBJS-CFI-SPLIT { + vertical-align: super; + font-size: 0.75em; + line-height: 1em; + padding: 0.1em; + background-color: #fffa96; + border-radius: 5px; + cursor: pointer; +} + diff --git a/files_reader/vendor/epubjs/css/sidebar.css b/files_reader/vendor/epubjs/css/sidebar.css new file mode 100644 index 0000000..aa3b09e --- /dev/null +++ b/files_reader/vendor/epubjs/css/sidebar.css @@ -0,0 +1,492 @@ +/* sidebar */ + +.sidebar.open { + box-shadow: 3px 0px 3px 0px rgba(0, 0, 0, 0.4); + display: block; +} + +.sidebar { + background: #6b6b6b; + position: fixed; + top: 0; + min-width: 25em; + height: 100%; + overflow: hidden; + display: none; + z-index: 100; +} + +.sidebar.wide { + width: 20%; +} + +.toolbar, +.panels { + position: absolute; + width: 100%; + height: 2em; + background: #4e4e4e; +} + +.toolbar { + text-align: center; +} + +.toolbar .metainfo { + font-size: 1.2em; + top: 0.5em; +} + +.toolbar div { + display: inline-block; + position: relative; +} + +.toolbar .separator { + /* border: solid 1px; */ + height: 1em; + opacity: 0.5; +} + +.toolbar button, .sidebar button { + color: white; + border: none; + background-color: transparent; + padding: 0; +} + +.sidebar div > button { + /* font-size: 1.5em; */ + /* font-size: 1em; */ + padding: 0.4em; + margin: 0; +} + +.mobile .sidebar div > button { + padding: 0.5em; + margin: 0; +} + +.sidebar div > button:hover { + color: #8CC746; +} + +.panels .open { + background-color: #6B6B6B; +} + +.view.open { + display: block !important; +} + +.view { + overflow-x: hidden; + display: none !important; + width: 100%; + position: absolute; + top: 2em; + bottom: 0; +} + +.list_item a { + color: #AAA; + text-decoration: none; +} + +.list_item a.chapter { + font-size: 1em; +} + +.list_item a.section { + font-size: .8em; +} + +.list_item.currentChapter > a, +.list_item a:hover { + color: #f1f1f1 +} + +.list_item a:hover { + color: #E2E2E2; +} + +.list_item ul { + display: none; +} + +.list_item.currentChapter > ul, +.list_item.openChapter > ul { + display: block; +} + +legend { + margin-left: 1em; + padding: 0.5em; + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4); + } + +.settings-container { + text-align: left; + margin: 1em; + background: #F8F8F8; + color: #111; + border-radius: 4px; + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4); +} + +.settings-container > legend { + position: relative; + top: 1em; + margin-bottom: 1em; + text-align: center; + font-weight: 600; + box-shadow: none; +} + +.settings-container label { + margin-right: 1em; +} + +.center-box { + text-align: center; + margin: 1em; +} + +.font_example { + margin: 1em; + text-align: center; + box-shadow: inset 0 1px 10px rgba(0, 0, 0, 0.4); +} + +.font_example div { + padding: 1em; +} + +.view .control-group input[type=range] { + width: 90%; + float: right; + margin: 0; +} + +.view .control-group { + padding: 1em; +} + +.view .sliders { + font-size: 1.5em; +} + +.view .control-group span { + float: left; + margin: 0 2px; + clear: both; +} + +.view .control-group input[type=reset] { + float: right; +} +.metadata { + padding: 1em; + margin: 1em; +} + +.metadata table { + font-size: 1.2em; + color: #F8F8F8; +} + +.metadata td:nth-child(1) { + font-weight: bold; + padding-right: 1em; +} + +/* panels */ +.panels a { + visibility: hidden; + overflow: hidden; + display: inline-block; + color: #ccc; +} + +.panels a::before { + visibility: visible; +} + +.panels a:hover { + color: #AAA; +} + +.panels a:active { + color: #AAA; + margin: 1px 0 -1px 6px; +} + +.panels a.active, +.panels a.active:hover { + color: #AAA; +} + +/* END panels */ + +/* TOC (and search, and bookmarks) */ +.toc_toggle { + display: inline-block; + width: 14px; + cursor: pointer; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; +} + +.toc_toggle:before { + content: '▸'; + color: #fff; + margin-right: -4px; +} + +.currentChapter > .toc_toggle:before, +.openChapter > .toc_toggle:before { + content: '▾'; +} + +#toc-populate.open { + display: inline-block !important; + background-color: #4e4e4e; +} + +.toc-view li, +.bookmarks-view li, +.search-view li { + margin: 1em; + font-family: Georgia, "Times New Roman", Times, serif; + list-style: none; +} + +.toc-view li { + text-transform: capitalize; +} + +.toc-vew.hidden { + display: none; +} + +.bookmark_link_text { + float: right; + font-size: 75%; + margin: 1em; + color: #AAA; +} + +/* END TOC */ + + +/* search */ + +.search-view, +.notes-view { + overflow: hidden; +} + +.search-results { + overflow-y: scroll; + height: 95%; + height: calc(100vh - 5em); +} + +.search-input { + padding-top: 1em; + padding-bottom: 1em; + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4); +} + +.searchbox { + width: 80%; + float: left; + margin-left: 1em; +} + +.searchbox + span { + position: relative; + left: -1em; + visibility: hidden; + font-weight: bold; + font-family: sans-serif; + cursor: pointer; +} + +.searchbox + span:hover { + color: red; +} + +#clear_search { + padding: 0; + padding-right: 0.5em; +} + +/* END search */ + +/* notes */ + +.notes-input { + padding-top: 1em; + padding-bottom: 2em; + box-shadow: 0 1px 10px rgba(0, 0, 0, 0.4); +} + +.notes { + overflow-y: scroll; + height: 95%; + height: calc(100vh - 11em); +} + +.notes li, +.bookmarks li { + margin: 1em 1em 1em 0.5em; + padding: 0.25em 0 0 0.5em; + font-family: Georgia, "Times New Roman", Times, serif; + /* border-top: 1px #eee dotted; */ + color: #ccc; + padding-bottom: 0.5em; + border-left: 0.5em solid transparent; +} + +.notes li:hover, +.bookmarks li:hover { + border-left: 0.5em solid #eee; +} + +.notes li:hover span, +.notes li:hover a, +.bookmarks li:hover span, +.bookmarks li:hover a { + color: #fff; +} + +.item-date { + text-align: right; + font-size: 0.8em; + padding-top: 0.5em; + color: #bbb; +} + +.item-delete, +.item-edit, +.item-save, +.item-cancel, +.note-link { + color: #999; + display: inline-block; + text-decoration: none; + margin-right: 0.5em; + margin-top: 0.5em; + cursor: pointer; +} + +.item-save, +.item-cancel { + background-color: #ccc; + padding: 0.2em 0.4em; + border: 1px solid #ccc; + border-radius: 0.2em; +} + +.item-save:hover, +.item-cancel:hover { + background-color: #eee; + border: 1px solid white; +} + +.item-save { + color: green !important; +} + +.item-cancel { + color: red !important; +} + +.note-link { + float: right; + margin-right: 0; +} + +.note-delete, +.note-edit { + float: left; +} + +div.editable { + background-color: #EEE; + color: #111; + /* border: 1px dotted white; */ + border-radius: 0.2em; + padding: 0.25em; + box-shadow: inset 0 1px 10px rgba(0, 0, 0, 0.4); +} + +.notes li a:hover, +.bookmarks li a:hover { + text-decoration: underline; +} + +.notes li img { +} + +.note-text { + text-align: left; + display: block; + width: calc(100% - 2em); + height: 6em; + margin-left: 1em; + margin-right: 1em; + margin-bottom: 1em; + border-radius: 4px; +} + +.note-text[disabled], #note-text[disabled="disabled"]{ + opacity: .5; +} + +.note-anchor { + font-size: 1.5em; + padding: 0 !important; + margin-right: 0.5em !important; +} + +.note-marker { + vertical-align: super; + font-size: 0.75em; + line-height: 1em; + padding: 0.1em; + background-color: #fffa96; + border-radius: 5px; + cursor: pointer; +} + +/* END notes */ + +/* margins */ + +.margin-icon { + float: none !important; + font-size: 3em; + vertical-align: middle; +} + +/* media-specific rules */ + +@media only screen and (max-width: 25em) { + + .sidebar { + min-width: 10em; + width: 80%; + } + + #main.single { + width: 100% !important; + } +} + + +/* END sidebar */ + + diff --git a/files_reader/css/main.css b/files_reader/vendor/epubjs/epub.main.css similarity index 98% rename from files_reader/css/main.css rename to files_reader/vendor/epubjs/epub.main.css index 436966e..3ce2b1a 100644 --- a/files_reader/css/main.css +++ b/files_reader/vendor/epubjs/epub.main.css @@ -257,22 +257,18 @@ body { color: #AAA; } -#searchBox { - width: 165px; - float: left; - margin-left: 10px; - margin-top: -1px; - /* - border-radius: 5px; - background: #9b9b9b; - float: left; - margin-left: 5px; - margin-top: -5px; - padding: 3px 10px; - color: #000; - border: none; - outline: none; */ - +.searchbox { + position: inline-block; + width: 50%; + float: left; + margin-left: 1em; +} + +.searchbox + span { + position: relative; + left: -1em; + visibility: hidden; + font-weight: bold; } input::-webkit-input-placeholder { diff --git a/files_reader/vendor/epubjs/epub.min.js b/files_reader/vendor/epubjs/epub.min.js index 7a45474..b482450 100644 --- a/files_reader/vendor/epubjs/epub.min.js +++ b/files_reader/vendor/epubjs/epub.min.js @@ -1,11 +1,11 @@ /*! * @overview RSVP - a tiny implementation of Promises/A+. - * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors + * @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.2.1 + * @version 3.3.3 */ -"use strict";(function(){function a(a){return"function"==typeof a||"object"==typeof a&&null!==a}function b(a){return"function"==typeof a}function c(a){return"object"==typeof a&&null!==a}function d(){}function e(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1}function f(a){var b=a._promiseCallbacks;return b||(b=a._promiseCallbacks={}),b}function g(a,b){return"onerror"===a?void wa.on("error",b):2!==arguments.length?wa[a]:void(wa[a]=b)}function h(){setTimeout(function(){for(var a,b=0;bh;h++)E(e.resolve(a[h]),void 0,c,d);return f}function p(a,b){var c=this,d=new c(u,b);return D(d,a),d}function q(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}function r(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}function s(a,b){this._id=Ga++,this._label=b,this._state=void 0,this._result=void 0,this._subscribers=[],wa.instrument&&ya("created",this),u!==a&&("function"!=typeof a&&q(),this instanceof s?J(this,a):r())}function t(){return new TypeError("A promises callback cannot return that same promise.")}function u(){}function v(a){try{return a.then}catch(b){return La.error=b,La}}function w(a,b,c,d){try{a.call(b,c,d)}catch(e){return e}}function x(a,b,c){wa.async(function(a){var d=!1,e=w(c,b,function(c){d||(d=!0,b!==c?A(a,c,void 0):C(a,c))},function(b){d||(d=!0,D(a,b))},"Settle: "+(a._label||" unknown promise"));!d&&e&&(d=!0,D(a,e))},a)}function y(a,b){b._state===Ja?C(a,b._result):b._state===Ka?(b._onError=null,D(a,b._result)):E(b,void 0,function(c){b!==c?A(a,c,void 0):C(a,c)},function(b){D(a,b)})}function z(a,c,d){c.constructor===a.constructor&&d===za&&constructor.resolve===Aa?y(a,c):d===La?D(a,La.error):void 0===d?C(a,c):b(d)?x(a,c,d):C(a,c)}function A(b,c){b===c?C(b,c):a(c)?z(b,c,v(c)):C(b,c)}function B(a){a._onError&&a._onError(a._result),F(a)}function C(a,b){a._state===Ia&&(a._result=b,a._state=Ja,0===a._subscribers.length?wa.instrument&&ya("fulfilled",a):wa.async(F,a))}function D(a,b){a._state===Ia&&(a._state=Ka,a._result=b,wa.async(B,a))}function E(a,b,c,d){var e=a._subscribers,f=e.length;a._onError=null,e[f]=b,e[f+Ja]=c,e[f+Ka]=d,0===f&&a._state&&wa.async(F,a)}function F(a){var b=a._subscribers,c=a._state;if(wa.instrument&&ya(c===Ja?"fulfilled":"rejected",a),0!==b.length){for(var d,e,f=a._result,g=0;ga;a+=2){var b=Ya[a],c=Ya[a+1];b(c),Ya[a]=void 0,Ya[a+1]=void 0}Ra=0}function U(){try{var a=require,b=a("vertx");return Na=b.runOnLoop||b.runOnContext,P()}catch(c){return S()}}function V(a){var b={};return b.promise=new Ha(function(a,c){b.resolve=a,b.reject=c},a),b}function W(a,c,d){return Ha.all(a,d).then(function(a){if(!b(c))throw new TypeError("You must pass a function as filter's second argument.");for(var e=a.length,f=new Array(e),g=0;e>g;g++)f[g]=c(a[g]);return Ha.all(f,d).then(function(b){for(var c=new Array(e),d=0,f=0;e>f;f++)b[f]&&(c[d]=a[f],d++);return c.length=d,c})})}function X(a,b,c){this._superConstructor(a,b,!0,c)}function Y(a,b,c){this._superConstructor(a,b,!1,c)}function Z(a,b){return new Y(Ha,a,b).promise}function $(a,b){return new _a(Ha,a,b).promise}function _(a,c,d){return Ha.all(a,d).then(function(a){if(!b(c))throw new TypeError("You must pass a function as map's second argument.");for(var e=a.length,f=new Array(e),g=0;e>g;g++)f[g]=c(a[g]);return Ha.all(f,d)})}function aa(){this.value=void 0}function ba(a){try{return a.then}catch(b){return eb.value=b,eb}}function ca(a,b,c){try{a.apply(b,c)}catch(d){return eb.value=d,eb}}function da(a,b){for(var c,d,e={},f=a.length,g=new Array(f),h=0;f>h;h++)g[h]=a[h];for(d=0;dd;d++)c[d-1]=a[d];return c}function fa(a,b){return{then:function(c,d){return a.call(b,c,d)}}}function ga(a,b){var c=function(){for(var c,d=this,e=arguments.length,f=new Array(e+1),g=!1,h=0;e>h;++h){if(c=arguments[h],!g){if(g=ja(c),g===fb){var i=new Ha(u);return D(i,fb.value),i}g&&g!==!0&&(c=fa(g,c))}f[h]=c}var j=new Ha(u);return f[e]=function(a,c){a?D(j,a):void 0===b?A(j,c):b===!0?A(j,ea(arguments)):sa(b)?A(j,da(arguments,b)):A(j,c)},g?ia(j,f,a,d):ha(j,f,a,d)};return c.__proto__=a,c}function ha(a,b,c,d){var e=ca(c,d,b);return e===eb&&D(a,e.value),a}function ia(a,b,c,d){return Ha.all(b).then(function(b){var e=ca(c,d,b);return e===eb&&D(a,e.value),a})}function ja(a){return a&&"object"==typeof a?a.constructor===Ha?!0:ba(a):!1}function ka(a,b){return Ha.race(a,b)}function la(a,b){return Ha.reject(a,b)}function ma(a,b){return Ha.resolve(a,b)}function na(a){throw setTimeout(function(){throw a}),a}function oa(a,b){wa.async(a,b)}function pa(){wa.on.apply(wa,arguments)}function qa(){wa.off.apply(wa,arguments)}var ra;ra=Array.isArray?Array.isArray:function(a){return"[object Array]"===Object.prototype.toString.call(a)};var sa=ra,ta=Date.now||function(){return(new Date).getTime()},ua=Object.create||function(a){if(arguments.length>1)throw new Error("Second argument not supported");if("object"!=typeof a)throw new TypeError("Argument must be an object");return d.prototype=a,new d},va={mixin:function(a){return a.on=this.on,a.off=this.off,a.trigger=this.trigger,a._promiseCallbacks=void 0,a},on:function(a,b){if("function"!=typeof b)throw new TypeError("Callback must be a function");var c,d=f(this);c=d[a],c||(c=d[a]=[]),-1===e(c,b)&&c.push(b)},off:function(a,b){var c,d,g=f(this);return b?(c=g[a],d=e(c,b),void(-1!==d&&c.splice(d,1))):void(g[a]=[])},trigger:function(a,b,c){var d,e,g=f(this);if(d=g[a])for(var h=0;hd;d++)this._eachEntry(c[d],d)},m.prototype._settleMaybeThenable=function(a,b){var c=this._instanceConstructor,d=c.resolve;if(d===Aa){var e=v(a);if(e===za&&a._state!==Ia)a._onError=null,this._settledAt(a._state,b,a._result);else if("function"!=typeof e)this._remaining--,this._result[b]=this._makeResult(Ja,b,a);else if(c===Ha){var f=new c(u);z(f,a,e),this._willSettleAt(f,b)}else this._willSettleAt(new c(function(b){b(a)}),b)}else this._willSettleAt(d(a),b)},m.prototype._eachEntry=function(a,b){c(a)?this._settleMaybeThenable(a,b):(this._remaining--,this._result[b]=this._makeResult(Ja,b,a))},m.prototype._settledAt=function(a,b,c){var d=this.promise;d._state===Ia&&(this._remaining--,this._abortOnReject&&a===Ka?D(d,c):this._result[b]=this._makeResult(a,b,c)),0===this._remaining&&C(d,this._result)},m.prototype._makeResult=function(a,b,c){return c},m.prototype._willSettleAt=function(a,b){var c=this;E(a,void 0,function(a){c._settledAt(Ja,b,a)},function(a){c._settledAt(Ka,b,a)})};var Ca=n,Da=o,Ea=p,Fa="rsvp_"+ta()+"-",Ga=0,Ha=s;s.cast=Aa,s.all=Ca,s.race=Da,s.resolve=Aa,s.reject=Ea,s.prototype={constructor:s,_guidKey:Fa,_onError:function(a){var b=this;wa.after(function(){b._onError&&wa.trigger("error",a,b._label)})},then:za,"catch":function(a,b){return this.then(void 0,a,b)},"finally":function(a,b){var c=this,d=c.constructor;return c.then(function(b){return d.resolve(a()).then(function(){return b})},function(b){return d.resolve(a()).then(function(){return d.reject(b)})},b)}};var Ia=void 0,Ja=1,Ka=2,La=new G,Ma=new G;K.prototype=ua(Ba.prototype),K.prototype._superConstructor=Ba,K.prototype._makeResult=l,K.prototype._validationError=function(){return new Error("allSettled must be called with an array")};var Na,Oa,Pa=L,Qa=M,Ra=0,Sa=({}.toString,N),Ta="undefined"!=typeof window?window:void 0,Ua=Ta||{},Va=Ua.MutationObserver||Ua.WebKitMutationObserver,Wa="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),Xa="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel,Ya=new Array(1e3);Oa=Wa?O():Va?Q():Xa?R():void 0===Ta&&"function"==typeof require?U():S();var Za=V,$a=W,_a=X;X.prototype=ua(Ba.prototype),X.prototype._superConstructor=Ba,X.prototype._init=function(){this._result={}},X.prototype._validateInput=function(a){return a&&"object"==typeof a},X.prototype._validationError=function(){return new Error("Promise.hash must be called with an object")},X.prototype._enumerate=function(){var a=this,b=a.promise,c=a._input,d=[];for(var e in c)b._state===Ia&&Object.prototype.hasOwnProperty.call(c,e)&&d.push({position:e,entry:c[e]});var f=d.length;a._remaining=f;for(var g,h=0;b._state===Ia&&f>h;h++)g=d[h],a._eachEntry(g.entry,g.position)},Y.prototype=ua(_a.prototype),Y.prototype._superConstructor=Ba,Y.prototype._makeResult=l,Y.prototype._validationError=function(){return new Error("hashSettled must be called with an object")};var ab,bb=Z,cb=$,db=_,eb=new aa,fb=new aa,gb=ga;if("object"==typeof self)ab=self;else{if("object"!=typeof global)throw new Error("no global: `self` or `global` found");ab=global}var hb=ab,ib=ka,jb=la,kb=ma,lb=na;wa.async=Sa,wa.after=function(a){setTimeout(a,0)};if("undefined"!=typeof window&&"object"==typeof window.__PROMISE_INSTRUMENTATION__){var mb=window.__PROMISE_INSTRUMENTATION__;g("instrument",!0);for(var nb in mb)mb.hasOwnProperty(nb)&&pa(nb,mb[nb])}var ob={race:ib,Promise:Ha,allSettled:Pa,hash:cb,hashSettled:bb,denodeify:gb,on:pa,off:qa,map:db,filter:$a,resolve:kb,reject:jb,all:Qa,rethrow:lb,defer:Za,EventTarget:va,configure:g,async:oa};"function"==typeof define&&define.amd?define(function(){return ob}):"undefined"!=typeof module&&module.exports?module.exports=ob:"undefined"!=typeof hb&&(hb.RSVP=ob)}).call(this);var EPUBJS=EPUBJS||{};EPUBJS.VERSION="0.2.15",EPUBJS.plugins=EPUBJS.plugins||{},EPUBJS.filePath=EPUBJS.filePath||"/epubjs/",EPUBJS.Render={},function(a){var b=(a.ePub||{},a.ePub=function(){var a,b;return"undefined"!=typeof arguments[0]&&("string"==typeof arguments[0]||arguments[0]instanceof ArrayBuffer)&&(a=arguments[0],arguments[1]&&"object"==typeof arguments[1]?(b=arguments[1],b.bookPath=a):b={bookPath:a}),!arguments[0]||"object"!=typeof arguments[0]||arguments[0]instanceof ArrayBuffer||(b=arguments[0]),new EPUBJS.Book(b)});"function"==typeof define&&define.amd?define(["rsvp","jszip","localforage"],function(a,c,d){return b}):"undefined"!=typeof module&&module.exports&&(global.RSVP=require("rsvp"),global.JSZip=require("jszip"),global.localForage=require("localforage"),module.exports=b)}(window),EPUBJS.Book=function(a){this.settings=EPUBJS.core.defaults(a||{},{bookPath:void 0,bookKey:void 0,packageUrl:void 0,storage:!1,fromStorage:!1,saved:!1,online:!0,contained:!1,width:void 0,height:void 0,layoutOveride:void 0,orientation:void 0,minSpreadWidth:768,gap:"auto",version:1,restore:!1,reload:!1,"goto":!1,styles:{},headTags:{},withCredentials:!1,render_method:"Iframe",displayLastPage:!1}),this.settings.EPUBJSVERSION=EPUBJS.VERSION,this.spinePos=0,this.stored=!1,this.online=this.settings.online||navigator.onLine,this.networkListeners(),this.ready={manifest:new RSVP.defer,spine:new RSVP.defer,metadata:new RSVP.defer,cover:new RSVP.defer,toc:new RSVP.defer,pageList:new RSVP.defer},this.readyPromises=[this.ready.manifest.promise,this.ready.spine.promise,this.ready.metadata.promise,this.ready.cover.promise,this.ready.toc.promise],this.pageList=[],this.pagination=new EPUBJS.Pagination,this.pageListReady=this.ready.pageList.promise,this.ready.all=RSVP.all(this.readyPromises),this.ready.all.then(this._ready.bind(this)),this.isRendered=!1,this._q=EPUBJS.core.queue(this),this._rendering=!1,this._displayQ=EPUBJS.core.queue(this),this._moving=!1,this._gotoQ=EPUBJS.core.queue(this),this.renderer=new EPUBJS.Renderer(this.settings.render_method),this.renderer.setMinSpreadWidth(this.settings.minSpreadWidth),this.renderer.setGap(this.settings.gap),this.listenToRenderer(this.renderer),this.defer_opened=new RSVP.defer,this.opened=this.defer_opened.promise,this.store=!1,this.settings.storage!==!1&&this.fromStorage(!0),("string"==typeof this.settings.bookPath||this.settings.bookPath instanceof ArrayBuffer)&&this.open(this.settings.bookPath,this.settings.reload),window.addEventListener("beforeunload",this.unload.bind(this),!1)},EPUBJS.Book.prototype.open=function(a,b){var c,d=this,e=new RSVP.defer;return this.settings.bookPath=a,this.settings.contained||this.isContained(a)?(this.settings.contained=this.contained=!0,this.bookUrl="",c=this.unarchive(a).then(function(){return d.loadPackage()})):(this.bookUrl=this.urlFrom(a),c=this.loadPackage()),this.settings.restore&&!b&&localStorage?c.then(function(a){var b=d.packageIdentifier(a),c=d.restore(b);c||d.unpack(a),e.resolve(),d.defer_opened.resolve()}):c.then(function(a){d.unpack(a),e.resolve(),d.defer_opened.resolve()}),this._registerReplacements(this.renderer),e.promise},EPUBJS.Book.prototype.loadPackage=function(a){var b,c=this,d=new EPUBJS.Parser,e=a||"META-INF/container.xml";return b=this.settings.packageUrl?c.loadXml(c.settings.packageUrl):c.loadXml(c.bookUrl+e).then(function(a){return d.container(a)}).then(function(a){return c.settings.contentsPath=c.bookUrl+a.basePath,c.settings.packageUrl=c.bookUrl+a.packagePath,c.settings.encoding=a.encoding,c.loadXml(c.settings.packageUrl)}),b["catch"](function(a){console.error("Could not load book at: "+e),c.trigger("book:loadFailed",e)}),b},EPUBJS.Book.prototype.packageIdentifier=function(a){var b=new EPUBJS.Parser;return b.identifier(a)},EPUBJS.Book.prototype.unpack=function(a){var b=this,c=new EPUBJS.Parser;b.contents=c.packageContents(a,b.settings.contentsPath),b.manifest=b.contents.manifest,b.spine=b.contents.spine,b.spineIndexByURL=b.contents.spineIndexByURL,b.metadata=b.contents.metadata,b.settings.bookKey||(b.settings.bookKey=b.generateBookKey(b.metadata.identifier)),b.globalLayoutProperties=b.parseLayoutProperties(b.metadata),b.contents.coverPath&&(b.cover=b.contents.cover=b.settings.contentsPath+b.contents.coverPath),b.spineNodeIndex=b.contents.spineNodeIndex,b.ready.manifest.resolve(b.contents.manifest),b.ready.spine.resolve(b.contents.spine),b.ready.metadata.resolve(b.contents.metadata),b.ready.cover.resolve(b.contents.cover),b.locations=new EPUBJS.Locations(b.spine,b.store,b.settings.withCredentials),b.contents.navPath?(b.settings.navUrl=b.settings.contentsPath+b.contents.navPath,b.loadXml(b.settings.navUrl).then(function(a){return c.nav(a,b.spineIndexByURL,b.spine)}).then(function(a){b.toc=b.contents.toc=a,b.ready.toc.resolve(b.contents.toc)},function(a){b.ready.toc.resolve(!1)}),b.loadXml(b.settings.navUrl).then(function(a){return c.pageList(a,b.spineIndexByURL,b.spine)}).then(function(a){var c=new EPUBJS.EpubCFI,d=0;0!==a.length&&(b.pageList=b.contents.pageList=a,b.pageList.forEach(function(a){a.cfi||(d+=1,c.generateCfiFromHref(a.href,b).then(function(c){a.cfi=c,a.packageUrl=b.settings.packageUrl,d-=1,0===d&&(b.pagination.process(b.pageList),b.ready.pageList.resolve(b.pageList))}))}),d||(b.pagination.process(b.pageList),b.ready.pageList.resolve(b.pageList)))},function(a){b.ready.pageList.resolve([])})):b.contents.tocPath?(b.settings.tocUrl=b.settings.contentsPath+b.contents.tocPath,b.loadXml(b.settings.tocUrl).then(function(a){return c.toc(a,b.spineIndexByURL,b.spine)},function(a){console.error(a)}).then(function(a){b.toc=b.contents.toc=a,b.ready.toc.resolve(b.contents.toc)},function(a){b.ready.toc.resolve(!1)})):b.ready.toc.resolve(!1)},EPUBJS.Book.prototype.createHiddenRender=function(a,b,c){var d,e,f=this.element.getBoundingClientRect(),g=b||this.settings.width||f.width,h=c||this.settings.height||f.height;return a.setMinSpreadWidth(this.settings.minSpreadWidth),a.setGap(this.settings.gap),this._registerReplacements(a),this.settings.forceSingle&&a.forceSingle(!0),d=document.createElement("div"),d.style.visibility="hidden",d.style.overflow="hidden",d.style.width="0",d.style.height="0",this.element.appendChild(d),e=document.createElement("div"),e.style.visibility="hidden",e.style.overflow="hidden",e.style.width=g+"px",e.style.height=h+"px",d.appendChild(e),a.initialize(e,this.settings.width,this.settings.height),d},EPUBJS.Book.prototype.generatePageList=function(a,b,c){var d=[],e=new EPUBJS.Renderer(this.settings.render_method,!1),f=this.createHiddenRender(e,a,b),g=new RSVP.defer,h=-1,i=this.spine.length,j=0,k=function(a){var b,g=h+1,l=a||new RSVP.defer;if(g>=i)l.resolve();else{if(c&&c.cancelled)return e.remove(),this.element.removeChild(f),void l.reject(new Error("User cancelled"));h=g,b=new EPUBJS.Chapter(this.spine[h],this.store),e.displayChapter(b,this.globalLayoutProperties).then(function(a){e.pageMap.forEach(function(a){j+=1,d.push({cfi:a.start,page:j})}),e.pageMap.length%2>0&&e.spreads&&(j+=1,d.push({cfi:e.pageMap[e.pageMap.length-1].end,page:j})),setTimeout(function(){k(l)},1)})}return l.promise}.bind(this);k().then(function(){e.remove(),this.element.removeChild(f),g.resolve(d)}.bind(this),function(a){g.reject(a)});return g.promise},EPUBJS.Book.prototype.generatePagination=function(a,b,c){var d=this,e=new RSVP.defer;return this.ready.spine.promise.then(function(){d.generatePageList(a,b,c).then(function(a){d.pageList=d.contents.pageList=a,d.pagination.process(a),d.ready.pageList.resolve(d.pageList),e.resolve(d.pageList)},function(a){e.reject(a)})}),e.promise},EPUBJS.Book.prototype.loadPagination=function(a){var b;return b="string"==typeof a?JSON.parse(a):a,b&&b.length&&(this.pageList=b,this.pagination.process(this.pageList),this.ready.pageList.resolve(this.pageList)),this.pageList},EPUBJS.Book.prototype.getPageList=function(){return this.ready.pageList.promise},EPUBJS.Book.prototype.getMetadata=function(){return this.ready.metadata.promise},EPUBJS.Book.prototype.getToc=function(){return this.ready.toc.promise},EPUBJS.Book.prototype.networkListeners=function(){var a=this;window.addEventListener("offline",function(b){a.online=!1,a.settings.storage&&a.fromStorage(!0),a.trigger("book:offline")},!1),window.addEventListener("online",function(b){a.online=!0,a.settings.storage&&a.fromStorage(!1),a.trigger("book:online")},!1)},EPUBJS.Book.prototype.listenToRenderer=function(a){var b=this;a.Events.forEach(function(c){a.on(c,function(a){b.trigger(c,a)})}),a.on("renderer:visibleRangeChanged",function(a){var b,c,d,e=[];this.pageList.length>0&&(b=this.pagination.pageFromCfi(a.start),d=this.pagination.percentageFromPage(b),e.push(b),a.end&&(c=this.pagination.pageFromCfi(a.end),e.push(c)),this.trigger("book:pageChanged",{anchorPage:b,percentage:d,pageRange:e}))}.bind(this)),a.on("render:loaded",this.loadChange.bind(this))},EPUBJS.Book.prototype.loadChange=function(a){var b,c,d=EPUBJS.core.uri(a),e=EPUBJS.core.uri(this.currentChapter.absolute);d.path!=e.path?(console.warn("Miss Match",d.path,this.currentChapter.absolute),b=this.spineIndexByURL[d.filename],c=new EPUBJS.Chapter(this.spine[b],this.store),this.currentChapter=c,this.renderer.currentChapter=c,this.renderer.afterLoad(this.renderer.render.docEl),this.renderer.beforeDisplay(function(){this.renderer.afterDisplay()}.bind(this))):this._rendering||this.renderer.reformat()},EPUBJS.Book.prototype.unlistenToRenderer=function(a){a.Events.forEach(function(b){a.off(b)})},EPUBJS.Book.prototype.coverUrl=function(){var a=this.ready.cover.promise.then(function(a){return this.settings.fromStorage?this.store.getUrl(this.contents.cover):this.settings.contained?this.zip.getUrl(this.contents.cover):this.contents.cover}.bind(this));return a.then(function(a){this.cover=a}.bind(this)),a},EPUBJS.Book.prototype.loadXml=function(a){return this.settings.fromStorage?this.store.getXml(a,this.settings.encoding):this.settings.contained?this.zip.getXml(a,this.settings.encoding):EPUBJS.core.request(a,"xml",this.settings.withCredentials)},EPUBJS.Book.prototype.urlFrom=function(a){var b,c=EPUBJS.core.uri(a),d=c.protocol,e="/"==c.path[0],f=window.location,g=f.origin||f.protocol+"//"+f.host,h=document.getElementsByTagName("base");return h.length&&(b=h[0].href),c.protocol?c.origin+c.path:!d&&e?(b||g)+c.path:d||e?void 0:EPUBJS.core.resolveUrl(b||f.pathname,c.path)},EPUBJS.Book.prototype.unarchive=function(a){return this.zip=new EPUBJS.Unarchiver,this.store=this.zip,this.zip.open(a)},EPUBJS.Book.prototype.isContained=function(a){if(a instanceof ArrayBuffer)return!0;var b=EPUBJS.core.uri(a);return!b.extension||"epub"!=b.extension&&"zip"!=b.extension?!1:!0},EPUBJS.Book.prototype.isSaved=function(a){var b;return localStorage?(b=localStorage.getItem(a),localStorage&&null!==b?!0:!1):!1},EPUBJS.Book.prototype.generateBookKey=function(a){return"epubjs:"+EPUBJS.VERSION+":"+window.location.host+":"+a},EPUBJS.Book.prototype.saveContents=function(){return localStorage?void localStorage.setItem(this.settings.bookKey,JSON.stringify(this.contents)):!1},EPUBJS.Book.prototype.removeSavedContents=function(){return localStorage?void localStorage.removeItem(this.settings.bookKey):!1},EPUBJS.Book.prototype.renderTo=function(a){var b,c=this;if(EPUBJS.core.isElement(a))this.element=a;else{if("string"!=typeof a)return void console.error("Not an Element");this.element=EPUBJS.core.getEl(a)}return b=this.opened.then(function(){return c.renderer.initialize(c.element,c.settings.width,c.settings.height),c.metadata.direction&&c.renderer.setDirection(c.metadata.direction),c._rendered(),c.startDisplay()})},EPUBJS.Book.prototype.startDisplay=function(){var a;return a=this.settings["goto"]?this["goto"](this.settings["goto"]):this.settings.previousLocationCfi?this.gotoCfi(this.settings.previousLocationCfi):this.displayChapter(this.spinePos,this.settings.displayLastPage)},EPUBJS.Book.prototype.restore=function(a){var b,c=this,d=["manifest","spine","metadata","cover","toc","spineNodeIndex","spineIndexByURL","globalLayoutProperties"],e=!1,f=this.generateBookKey(a),g=localStorage.getItem(f),h=d.length;if(this.settings.clearSaved&&(e=!0),!e&&"undefined"!=g&&null!==g)for(c.contents=JSON.parse(g),b=0;h>b;b++){var i=d[b];if(!c.contents[i]){e=!0;break}c[i]=c.contents[i]}return!e&&g&&this.contents&&this.settings.contentsPath?(this.settings.bookKey=f,this.ready.manifest.resolve(this.manifest),this.ready.spine.resolve(this.spine),this.ready.metadata.resolve(this.metadata),this.ready.cover.resolve(this.cover),this.ready.toc.resolve(this.toc),!0):!1},EPUBJS.Book.prototype.displayChapter=function(a,b,c){var d,e,f,g,h=this,i=c||new RSVP.defer;return this.isRendered?this._rendering||this.renderer._moving?(this._displayQ.enqueue("displayChapter",[a,b,i]),i.promise):(EPUBJS.core.isNumber(a)?f=a:(e=new EPUBJS.EpubCFI(a),f=e.spinePos),(0>f||f>=this.spine.length)&&(console.warn("Not A Valid Location"),f=0,b=!1,e=!1),g=new EPUBJS.Chapter(this.spine[f],this.store),this._rendering=!0,this._needsAssetReplacement()&&g.registerHook("beforeChapterRender",[EPUBJS.replace.head,EPUBJS.replace.resources,EPUBJS.replace.svg],!0),h.currentChapter=g,d=h.renderer.displayChapter(g,this.globalLayoutProperties),e?h.renderer.gotoCfi(e):b&&h.renderer.lastPage(),d.then(function(a){h.spinePos=f,i.resolve(h.renderer),h.settings.fromStorage===!1&&h.settings.contained===!1&&h.preloadNextChapter(),h._rendering=!1,h._displayQ.dequeue(),0===h._displayQ.length()&&h._gotoQ.dequeue()},function(a){console.error("Could not load Chapter: "+g.absolute,a),h.trigger("book:chapterLoadFailed",g.absolute),h._rendering=!1,i.reject(a)}),i.promise):(this._q.enqueue("displayChapter",arguments),i.reject({message:"Rendering",stack:(new Error).stack}),i.promise)},EPUBJS.Book.prototype.nextPage=function(a){var a=a||new RSVP.defer;if(!this.isRendered)return this._q.enqueue("nextPage",[a]),a.promise;var b=this.renderer.nextPage();return b?(a.resolve(!0),a.promise):this.nextChapter(a)},EPUBJS.Book.prototype.prevPage=function(a){var a=a||new RSVP.defer;if(!this.isRendered)return this._q.enqueue("prevPage",[a]),a.promise;var b=this.renderer.prevPage();return b?(a.resolve(!0),a.promise):this.prevChapter(a)},EPUBJS.Book.prototype.nextChapter=function(a){var a=a||new RSVP.defer;if(this.spinePos0){for(var b=this.spinePos-1;this.spine[b]&&this.spine[b].linear&&"no"==this.spine[b].linear;)b--;if(b>=0)return this.displayChapter(b,!0,a)}return this.trigger("book:atStart"),a.resolve(!0),a.promise},EPUBJS.Book.prototype.getCurrentLocationCfi=function(){return this.isRendered?this.renderer.currentLocationCfi:!1},EPUBJS.Book.prototype["goto"]=function(a){return 0===a.indexOf("epubcfi(")?this.gotoCfi(a):a.indexOf("%")===a.length-1?this.gotoPercentage(parseInt(a.substring(0,a.length-1))/100):"number"==typeof a||isNaN(a)===!1?this.gotoPage(a):this.gotoHref(a)},EPUBJS.Book.prototype.gotoCfi=function(a,b){var c,d,e,f,g,h=b||new RSVP.defer;return this.isRendered?this._moving||this._rendering?(console.warn("Renderer is moving"),this._gotoQ.enqueue("gotoCfi",[a,h]),!1):(c=new EPUBJS.EpubCFI(a),d=c.spinePos,-1==d?!1:(e=this.spine[d],f=h.promise,this._moving=!0,this.currentChapter&&this.spinePos===d?(this.renderer.gotoCfi(c),this._moving=!1,h.resolve(this.renderer.currentLocationCfi)):(e&&-1!=d||(d=0,e=this.spine[d]),g=this.displayChapter(a),g.then(function(a){this._moving=!1,h.resolve(a.currentLocationCfi)}.bind(this),function(){this._moving=!1}.bind(this))),f.then(function(){this._gotoQ.dequeue()}.bind(this)),f)):(console.warn("Not yet Rendered"),this.settings.previousLocationCfi=a,!1)},EPUBJS.Book.prototype.gotoHref=function(a,b){var c,d,e,f,g,h=b||new RSVP.defer;return this.isRendered?this._moving||this._rendering?(this._gotoQ.enqueue("gotoHref",[a,h]),!1):(c=a.split("#"),d=c[0],e=c[1]||!1,f=-1==d.search("://")?d.replace(EPUBJS.core.uri(this.settings.contentsPath).path,""):d.replace(this.settings.contentsPath,""),g=this.spineIndexByURL[f],d||(g=this.currentChapter?this.currentChapter.spinePos:0),"number"!=typeof g?!1:this.currentChapter&&g==this.currentChapter.spinePos?(e?this.renderer.section(e):this.renderer.firstPage(),h.resolve(this.renderer.currentLocationCfi),h.promise.then(function(){this._gotoQ.dequeue()}.bind(this)),h.promise):this.displayChapter(g).then(function(){e&&this.renderer.section(e),h.resolve(this.renderer.currentLocationCfi)}.bind(this))):(this.settings["goto"]=a,!1)},EPUBJS.Book.prototype.gotoPage=function(a){var b=this.pagination.cfiFromPage(a);return this.gotoCfi(b)},EPUBJS.Book.prototype.gotoPercentage=function(a){var b=this.pagination.pageFromPercentage(a);return this.gotoPage(b)},EPUBJS.Book.prototype.preloadNextChapter=function(){var a,b=this.spinePos+1;return b>=this.spine.length?!1:(a=new EPUBJS.Chapter(this.spine[b]),void(a&&EPUBJS.core.request(a.absolute)))},EPUBJS.Book.prototype.storeOffline=function(){var a=this,b=EPUBJS.core.values(this.manifest);return this.store.put(b).then(function(){a.settings.stored=!0,a.trigger("book:stored")})},EPUBJS.Book.prototype.availableOffline=function(){return this.settings.stored>0?!0:!1},EPUBJS.Book.prototype.toStorage=function(){var a=this.settings.bookKey;this.store.isStored(a).then(function(b){return b===!0?(this.settings.stored=!0,!0):this.storeOffline().then(function(){this.store.token(a,!0)}.bind(this))}.bind(this))},EPUBJS.Book.prototype.fromStorage=function(a){[EPUBJS.replace.head,EPUBJS.replace.resources,EPUBJS.replace.svg];this.contained||this.settings.contained||(this.online&&this.opened.then(this.toStorage.bind(this)),this.store&&this.settings.fromStorage&&a===!1?(this.settings.fromStorage=!1,this.store.off("offline"),this.store=!1):this.settings.fromStorage||(this.store=new EPUBJS.Storage(this.settings.credentials),this.store.on("offline",function(a){a?(this.offline=!0,this.settings.fromStorage=!0,this.trigger("book:offline")):(this.offline=!1,this.settings.fromStorage=!1,this.trigger("book:online"))}.bind(this))))},EPUBJS.Book.prototype.setStyle=function(a,b,c){var d=["color","background","background-color"];return this.isRendered?(this.settings.styles[a]=b,this.renderer.setStyle(a,b,c),void(-1===d.indexOf(a)&&this.renderer.reformat())):this._q.enqueue("setStyle",arguments)},EPUBJS.Book.prototype.removeStyle=function(a){return this.isRendered?(this.renderer.removeStyle(a),this.renderer.reformat(),void delete this.settings.styles[a]):this._q.enqueue("removeStyle",arguments)},EPUBJS.Book.prototype.addHeadTag=function(a,b){return this.isRendered?void(this.settings.headTags[a]=b):this._q.enqueue("addHeadTag",arguments)},EPUBJS.Book.prototype.useSpreads=function(a){console.warn("useSpreads is deprecated, use forceSingle or set a layoutOveride instead"),a===!1?this.forceSingle(!0):this.forceSingle(!1)},EPUBJS.Book.prototype.forceSingle=function(a){var b="undefined"==typeof a?!0:a;this.renderer.forceSingle(b),this.settings.forceSingle=b,this.isRendered&&this.renderer.reformat()},EPUBJS.Book.prototype.setMinSpreadWidth=function(a){this.settings.minSpreadWidth=a,this.isRendered&&(this.renderer.setMinSpreadWidth(this.settings.minSpreadWidth),this.renderer.reformat())},EPUBJS.Book.prototype.setGap=function(a){this.settings.gap=a,this.isRendered&&(this.renderer.setGap(this.settings.gap),this.renderer.reformat())},EPUBJS.Book.prototype.chapter=function(a){ -var b,c,d=this.spineIndexByURL[a];return d&&(b=this.spine[d],c=new EPUBJS.Chapter(b,this.store,this.settings.withCredentials),c.load()),c},EPUBJS.Book.prototype.unload=function(){this.settings.restore&&localStorage&&this.saveContents(),this.unlistenToRenderer(this.renderer),this.trigger("book:unload")},EPUBJS.Book.prototype.destroy=function(){window.removeEventListener("beforeunload",this.unload),this.currentChapter&&this.currentChapter.unload(),this.unload(),this.renderer&&this.renderer.remove()},EPUBJS.Book.prototype._ready=function(){this.trigger("book:ready")},EPUBJS.Book.prototype._rendered=function(a){this.isRendered=!0,this.trigger("book:rendered"),this._q.flush()},EPUBJS.Book.prototype.applyStyles=function(a,b){a.applyStyles(this.settings.styles),b()},EPUBJS.Book.prototype.applyHeadTags=function(a,b){a.applyHeadTags(this.settings.headTags),b()},EPUBJS.Book.prototype._registerReplacements=function(a){a.registerHook("beforeChapterDisplay",this.applyStyles.bind(this,a),!0),a.registerHook("beforeChapterDisplay",this.applyHeadTags.bind(this,a),!0),a.registerHook("beforeChapterDisplay",EPUBJS.replace.hrefs.bind(this),!0)},EPUBJS.Book.prototype._needsAssetReplacement=function(){return this.settings.fromStorage?!0:this.settings.contained?!0:!1},EPUBJS.Book.prototype.parseLayoutProperties=function(a){var b=this.settings.layoutOveride&&this.settings.layoutOveride.layout||a.layout||"reflowable",c=this.settings.layoutOveride&&this.settings.layoutOveride.spread||a.spread||"auto",d=this.settings.layoutOveride&&this.settings.layoutOveride.orientation||a.orientation||"auto";return{layout:b,spread:c,orientation:d}},RSVP.EventTarget.mixin(EPUBJS.Book.prototype),RSVP.on("error",function(a){console.error(a)}),EPUBJS.Chapter=function(a,b,c){this.href=a.href,this.absolute=a.url,this.id=a.id,this.spinePos=a.index,this.cfiBase=a.cfiBase,this.properties=a.properties,this.manifestProperties=a.manifestProperties,this.linear=a.linear,this.pages=1,this.store=b,this.credentials=c,this.epubcfi=new EPUBJS.EpubCFI,this.deferred=new RSVP.defer,this.loaded=this.deferred.promise,EPUBJS.Hooks.mixin(this),this.getHooks("beforeChapterRender"),this.caches={}},EPUBJS.Chapter.prototype.load=function(a,b){var c,d=a||this.store,e=b||this.credentials;return c=d?d.getXml(this.absolute):EPUBJS.core.request(this.absolute,!1,e),c.then(function(a){try{this.setDocument(a),this.deferred.resolve(this)}catch(b){this.deferred.reject({message:this.absolute+" -> "+b.message,stack:(new Error).stack})}}.bind(this)),c},EPUBJS.Chapter.prototype.render=function(a){return this.load().then(function(a){var b=a.querySelector("head"),c=a.createElement("base");return c.setAttribute("href",this.absolute),b.insertBefore(c,b.firstChild),this.contents=a,new RSVP.Promise(function(b,c){this.triggerHooks("beforeChapterRender",function(){b(a)}.bind(this),this)}.bind(this))}.bind(this)).then(function(a){var b=new XMLSerializer,c=b.serializeToString(a);return c}.bind(this))},EPUBJS.Chapter.prototype.url=function(a){var b,c=new RSVP.defer,d=a||this.store,e=this;return d?this.tempUrl?(b=this.tempUrl,c.resolve(b)):d.getUrl(this.absolute).then(function(a){e.tempUrl=a,c.resolve(a)}):(b=this.absolute,c.resolve(b)),c.promise},EPUBJS.Chapter.prototype.setPages=function(a){this.pages=a},EPUBJS.Chapter.prototype.getPages=function(a){return this.pages},EPUBJS.Chapter.prototype.getID=function(){return this.ID},EPUBJS.Chapter.prototype.unload=function(a){this.document=null,this.tempUrl&&a&&(a.revokeUrl(this.tempUrl),this.tempUrl=!1)},EPUBJS.Chapter.prototype.setDocument=function(a){this.document=a,this.contents=a.documentElement,!this.document.evaluate&&document.evaluate&&(this.document.evaluate=document.evaluate)},EPUBJS.Chapter.prototype.cfiFromRange=function(a){var b,c,d,e,f,g,h;if(this.document){if("undefined"!=typeof document.evaluate){if(c=EPUBJS.core.getElementXPath(a.startContainer),d=EPUBJS.core.getElementXPath(a.endContainer),e=this.document.evaluate(c,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,a.collapsed||(f=this.document.evaluate(d,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue),b=this.document.createRange(),e)try{b.setStart(e,a.startOffset),!a.collapsed&&f&&b.setEnd(f,a.endOffset)}catch(i){console.log("missed"),e=!1}e||(console.log("not found, try fuzzy match"),g=EPUBJS.core.cleanStringForXpath(a.startContainer.textContent),c="//text()[contains(.,"+g+")]",e=this.document.evaluate(c,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,e&&(b.setStart(e,a.startOffset),a.collapsed||(h=EPUBJS.core.cleanStringForXpath(a.endContainer.textContent),d="//text()[contains(.,"+h+")]",f=this.document.evaluate(d,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,f&&b.setEnd(f,a.endOffset))))}else b=a;return this.epubcfi.generateCfiFromRange(b,this.cfiBase)}},EPUBJS.Chapter.prototype.find=function(a){var b=this,c=[],d=a.toLowerCase(),e=function(a){for(var e,f,g,h=a.textContent.toLowerCase(),i=b.document.createRange(),j=-1,k=150;-1!=f;)f=h.indexOf(d,j+1),-1!=f&&(i=b.document.createRange(),i.setStart(a,f),i.setEnd(a,f+d.length),e=b.cfiFromRange(i),a.textContent.length=g&&c&&c(!0),e=!0)};b(a,f)}.bind(this))},EPUBJS.Chapter.prototype.replaceWithStored=function(a,b,c,d){var e,f={},g=this.store,h=this.caches[a],i=EPUBJS.core.uri(this.absolute),j=i.base,k=b,l=5,m=function(a,b,c){f[b]=a},n=function(a){d&&d(),EPUBJS.core.values(e).forEach(function(a){g.revokeUrl(a)}),h=f};g&&(h||(h={}),e=EPUBJS.core.clone(h),this.replace(a,function(b,d){var h=b.getAttribute(k),i=EPUBJS.core.resolveUrl(j,h),m=function(c){var e;b.onload=function(){clearTimeout(e),d(c,i)},"svg image"==a&&b.setAttribute("externalResourcesRequired","true"),"link[href]"==a&&"stylesheet"!==b.getAttribute("rel")?d(c,i):e=setTimeout(function(){d(c,i)},l),c&&b.setAttribute(k,c)};i in e?(m(e[i]),f[i]=e[i],delete e[i]):c(g,i,m,b)},n,m))};var EPUBJS=EPUBJS||{};EPUBJS.core={},EPUBJS.core.getEl=function(a){return document.getElementById(a)},EPUBJS.core.getEls=function(a){return document.getElementsByClassName(a)},EPUBJS.core.request=function(a,b,c){var d,e=window.URL,f=e?"blob":"arraybuffer",g=new RSVP.defer,h=new XMLHttpRequest,i=XMLHttpRequest.prototype,j=function(){var a;this.readyState==this.DONE&&(200!==this.status&&0!==this.status||!this.response?g.reject({message:this.response,stack:(new Error).stack}):(a="xml"==b?this.responseXML?this.responseXML:(new DOMParser).parseFromString(this.response,"application/xml"):"xhtml"==b?this.responseXML?this.responseXML:(new DOMParser).parseFromString(this.response,"application/xhtml+xml"):"html"==b?this.responseXML?this.responseXML:(new DOMParser).parseFromString(this.response,"text/html"):"json"==b?JSON.parse(this.response):"blob"==b?e?this.response:new Blob([this.response]):this.response,g.resolve(a)))};return"overrideMimeType"in i||Object.defineProperty(i,"overrideMimeType",{value:function(a){}}),h.onreadystatechange=j,h.open("GET",a,!0),c&&(h.withCredentials=!0),b||(d=EPUBJS.core.uri(a),b=d.extension,b={htm:"html"}[b]||b),"blob"==b&&(h.responseType=f),"json"==b&&h.setRequestHeader("Accept","application/json"),"xml"==b&&(h.responseType="document",h.overrideMimeType("text/xml")),"xhtml"==b&&(h.responseType="document"),"html"==b&&(h.responseType="document"),"binary"==b&&(h.responseType="arraybuffer"),h.send(),g.promise},EPUBJS.core.toArray=function(a){var b=[];for(var c in a){var d;a.hasOwnProperty(c)&&(d=a[c],d.ident=c,b.push(d))}return b},EPUBJS.core.uri=function(a){var b,c,d,e={protocol:"",host:"",path:"",origin:"",directory:"",base:"",filename:"",extension:"",fragment:"",href:a},f=a.indexOf("blob:"),g=a.indexOf("://"),h=a.indexOf("?"),i=a.indexOf("#");return 0===f?(e.protocol="blob",e.base=a.indexOf(0,i),e):(-1!=i&&(e.fragment=a.slice(i+1),a=a.slice(0,i)),-1!=h&&(e.search=a.slice(h+1),a=a.slice(0,h),href=a),-1!=g?(e.protocol=a.slice(0,g),b=a.slice(g+3),d=b.indexOf("/"),-1===d?(e.host=e.path,e.path=""):(e.host=b.slice(0,d),e.path=b.slice(d)),e.origin=e.protocol+"://"+e.host,e.directory=EPUBJS.core.folder(e.path),e.base=e.origin+e.directory):(e.path=a,e.directory=EPUBJS.core.folder(a),e.base=e.directory),e.filename=a.replace(e.base,""),c=e.filename.lastIndexOf("."),-1!=c&&(e.extension=e.filename.slice(c+1)),e)},EPUBJS.core.folder=function(a){var b=a.lastIndexOf("/");if(-1==b)var c="";return c=a.slice(0,b+1)},EPUBJS.core.dataURLToBlob=function(a){var b,c,d,e,f,g=";base64,";if(-1==a.indexOf(g))return b=a.split(","),c=b[0].split(":")[1],d=b[1],new Blob([d],{type:c});b=a.split(g),c=b[0].split(":")[1],d=window.atob(b[1]),e=d.length,f=new Uint8Array(e);for(var h=0;e>h;++h)f[h]=d.charCodeAt(h);return new Blob([f],{type:c})},EPUBJS.core.addScript=function(a,b,c){var d,e;e=!1,d=document.createElement("script"),d.type="text/javascript",d.async=!1,d.src=a,d.onload=d.onreadystatechange=function(){e||this.readyState&&"complete"!=this.readyState||(e=!0,b&&b())},c=c||document.body,c.appendChild(d)},EPUBJS.core.addScripts=function(a,b,c){var d=a.length,e=0,f=function(){e++,d==e?b&&b():EPUBJS.core.addScript(a[e],f,c)};EPUBJS.core.addScript(a[e],f,c)},EPUBJS.core.addCss=function(a,b,c){var d,e;e=!1,d=document.createElement("link"),d.type="text/css",d.rel="stylesheet",d.href=a,d.onload=d.onreadystatechange=function(){e||this.readyState&&"complete"!=this.readyState||(e=!0,b&&b())},c=c||document.body,c.appendChild(d)},EPUBJS.core.prefixed=function(a){var b=["Webkit","Moz","O","ms"],c=a[0].toUpperCase()+a.slice(1),d=b.length;if("undefined"!=typeof document.documentElement.style[a])return a;for(var e=0;d>e;e++)if("undefined"!=typeof document.documentElement.style[b[e]+c])return b[e]+c;return a},EPUBJS.core.resolveUrl=function(a,b){var c,d,e=[],f=EPUBJS.core.uri(b),g=a.split("/");return f.host?b:(g.pop(),d=b.split("/"),d.forEach(function(a){".."===a?g.pop():e.push(a)}),c=g.concat(e),c.join("/"))},EPUBJS.core.uuid=function(){var a=(new Date).getTime(),b="xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(b){var c=(a+16*Math.random())%16|0;return a=Math.floor(a/16),("x"==b?c:7&c|8).toString(16)});return b},EPUBJS.core.insert=function(a,b,c){var d=EPUBJS.core.locationOf(a,b,c);return b.splice(d,0,a),d},EPUBJS.core.locationOf=function(a,b,c,d,e){var f,g=d||0,h=e||b.length,i=parseInt(g+(h-g)/2);return c||(c=function(a,b){return a>b?1:b>a?-1:(a=b)?0:void 0}),0>=h-g?i:(f=c(b[i],a),h-g===1?f>0?i:i+1:0===f?i:-1===f?EPUBJS.core.locationOf(a,b,c,i,h):EPUBJS.core.locationOf(a,b,c,g,i))},EPUBJS.core.indexOfSorted=function(a,b,c,d,e){var f,g=d||0,h=e||b.length,i=parseInt(g+(h-g)/2);return c||(c=function(a,b){return a>b?1:b>a?-1:(a=b)?0:void 0}),0>=h-g?-1:(f=c(b[i],a),h-g===1?0===f?i:-1:0===f?i:-1===f?EPUBJS.core.indexOfSorted(a,b,c,i,h):EPUBJS.core.indexOfSorted(a,b,c,g,i))},EPUBJS.core.queue=function(a){var b=[],c=a,d=function(a,c,d){return b.push({funcName:a,args:c,context:d}),b},e=function(){var a;b.length&&(a=b.shift(),c[a.funcName].apply(a.context||c,a.args))},f=function(){for(;b.length;)e()},g=function(){b=[]},h=function(){return b.length};return{enqueue:d,dequeue:e,flush:f,clear:g,length:h}},EPUBJS.core.getElementXPath=function(a){return a&&a.id?'//*[@id="'+a.id+'"]':EPUBJS.core.getElementTreeXPath(a)},EPUBJS.core.getElementTreeXPath=function(a){var b,c,d,e,f=[],g="http://www.w3.org/1999/xhtml"===a.ownerDocument.documentElement.getAttribute("xmlns");for(a.nodeType===Node.TEXT_NODE&&(b=EPUBJS.core.indexOfTextNode(a)+1,f.push("text()["+b+"]"),a=a.parentNode);a&&1==a.nodeType;a=a.parentNode){b=0;for(var h=a.previousSibling;h;h=h.previousSibling)h.nodeType!=Node.DOCUMENT_TYPE_NODE&&h.nodeName==a.nodeName&&++b;c=a.nodeName.toLowerCase(),d=g?"xhtml:"+c:c,e=b?"["+(b+1)+"]":"",f.splice(0,0,d+e)}return f.length?"./"+f.join("/"):null},EPUBJS.core.nsResolver=function(a){var b={xhtml:"http://www.w3.org/1999/xhtml",epub:"http://www.idpf.org/2007/ops"};return b[a]||null},EPUBJS.core.cleanStringForXpath=function(a){var b=a.match(/[^'"]+|['"]/g);return b=b.map(function(a){return"'"===a?'"\'"':'"'===a?"'\"'":"'"+a+"'"}),"concat('',"+b.join(",")+")"},EPUBJS.core.indexOfTextNode=function(a){for(var b,c=a.parentNode,d=c.childNodes,e=-1,f=0;fb;b++){var d=arguments[b];for(var e in d)void 0===a[e]&&(a[e]=d[e])}return a},EPUBJS.core.extend=function(a){var b=[].slice.call(arguments,1);return b.forEach(function(b){b&&Object.getOwnPropertyNames(b).forEach(function(c){Object.defineProperty(a,c,Object.getOwnPropertyDescriptor(b,c))})}),a},EPUBJS.core.clone=function(a){return EPUBJS.core.isArray(a)?a.slice():EPUBJS.core.extend({},a)},EPUBJS.core.isElement=function(a){return!(!a||1!=a.nodeType)},EPUBJS.core.isNumber=function(a){return!isNaN(parseFloat(a))&&isFinite(a)},EPUBJS.core.isString=function(a){return"string"==typeof a||a instanceof String},EPUBJS.core.isArray=Array.isArray||function(a){return"[object Array]"===Object.prototype.toString.call(a)},EPUBJS.core.values=function(a){var b,c,d,e=-1;if(!a)return[];for(b=Object.keys(a),c=b.length,d=Array(c);++e0;){if(c=d.shift(),"text"===c.type?(e=g.childNodes[c.index],g=e.parentNode||g):g=c.id?f.getElementById(c.id):h[c.index],!g||"undefined"==typeof g)return console.error("No Element For",c,a.str),!1;h=Array.prototype.slice.call(g.children)}return g},EPUBJS.EpubCFI.prototype.compare=function(a,b){if("string"==typeof a&&(a=new EPUBJS.EpubCFI(a)),"string"==typeof b&&(b=new EPUBJS.EpubCFI(b)),a.spinePos>b.spinePos)return 1;if(a.spinePosb.steps[c].index)return 1;if(a.steps[c].indexb.characterOffset?1:a.characterOffset")},EPUBJS.EpubCFI.prototype.generateRangeFromCfi=function(a,b){var c,d,e,f,g,h,i=b||document,j=i.createRange();return"string"==typeof a&&(a=this.parse(a)),-1===a.spinePos?!1:(c=a.steps[a.steps.length-1],"undefined"!=typeof document.evaluate?(d=this.generateXpathFromSteps(a.steps),e=i.evaluate(d,i,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue):(g=this.generateQueryFromSteps(a.steps),h=i.querySelector(g),h&&"text"==c.type&&(e=h.childNodes[c.index])),e?(e&&a.characterOffset>=0?(f=e.length,a.characterOffset-1&&this.hooks[a].splice(c,1)):Array.isArray(b)&&b.forEach(function(b){c=this.hooks[a].indexOf(b),c>-1&&this.hooks[a].splice(c,1)},this))},a.prototype.triggerHooks=function(a,b,c){function d(){f--,0>=f&&b&&b()}var e,f;return"undefined"==typeof this.hooks[a]?!1:(e=this.hooks[a],f=e.length,0===f&&b&&b(),void e.forEach(function(a){a(d,c)}))},{register:function(a){if(void 0===EPUBJS.hooks[a]&&(EPUBJS.hooks[a]={}),"object"!=typeof EPUBJS.hooks[a])throw"Already registered: "+a;return EPUBJS.hooks[a]},mixin:function(b){for(var c in a.prototype)b[c]=a.prototype[c]}}}(),EPUBJS.Layout=EPUBJS.Layout||{},EPUBJS.Layout.isFixedLayout=function(a){var b=a.querySelector("[name=viewport]");if(!b||!b.hasAttribute("content"))return!1;var c=b.getAttribute("content");return/,/.test(c)},EPUBJS.Layout.Reflowable=function(){this.documentElement=null,this.spreadWidth=null},EPUBJS.Layout.Reflowable.prototype.format=function(a,b,c,d){var e=EPUBJS.core.prefixed("columnAxis"),f=EPUBJS.core.prefixed("columnGap"),g=EPUBJS.core.prefixed("columnWidth"),h=EPUBJS.core.prefixed("columnFill"),i=Math.floor(b),j=Math.floor(i/8),k=d>=0?d:j%2===0?j:j-1;return this.documentElement=a,this.spreadWidth=i+k,a.style.overflow="hidden",a.style.width=i+"px",a.style.height=c+"px",a.style[e]="horizontal",a.style[h]="auto",a.style[g]=i+"px",a.style[f]=k+"px",this.colWidth=i,this.gap=k,{pageWidth:this.spreadWidth,pageHeight:c}},EPUBJS.Layout.Reflowable.prototype.calculatePages=function(){var a,b;return this.documentElement.style.width="auto",a=this.documentElement.scrollWidth,b=Math.ceil(a/this.spreadWidth),{displayedPages:b,pageCount:b}},EPUBJS.Layout.ReflowableSpreads=function(){this.documentElement=null,this.spreadWidth=null},EPUBJS.Layout.ReflowableSpreads.prototype.format=function(a,b,c,d){var e=EPUBJS.core.prefixed("columnAxis"),f=EPUBJS.core.prefixed("columnGap"),g=EPUBJS.core.prefixed("columnWidth"),h=EPUBJS.core.prefixed("columnFill"),i=2,j=Math.floor(b),k=j%2===0?j:j-1,l=Math.floor(k/8),m=d>=0?d:l%2===0?l:l-1,n=Math.floor((k-m)/i);return this.documentElement=a,this.spreadWidth=(n+m)*i,a.style.overflow="hidden",a.style.width=k+"px",a.style.height=c+"px",a.style[e]="horizontal",a.style[h]="auto",a.style[f]=m+"px",a.style[g]=n+"px",this.colWidth=n,this.gap=m,{pageWidth:this.spreadWidth,pageHeight:c}},EPUBJS.Layout.ReflowableSpreads.prototype.calculatePages=function(){var a=this.documentElement.scrollWidth,b=Math.ceil(a/this.spreadWidth);return this.documentElement.style.width=b*this.spreadWidth-this.gap+"px",{displayedPages:b,pageCount:2*b}},EPUBJS.Layout.Fixed=function(){this.documentElement=null},EPUBJS.Layout.Fixed.prototype.format=function(a,b,c,d){var e,f,g,h,i=EPUBJS.core.prefixed("columnWidth"),j=EPUBJS.core.prefixed("transform"),k=EPUBJS.core.prefixed("transformOrigin"),l=a.querySelector("[name=viewport]");this.documentElement=a,l&&l.hasAttribute("content")&&(e=l.getAttribute("content"),f=e.split(","),f[0]&&(g=f[0].replace("width=","")),f[1]&&(h=f[1].replace("height=","")));var m=b/g,n=c/h,o=n>m?m:n;return a.style.position="absolute",a.style.top="50%",a.style.left="50%",a.style[j]="scale("+o+") translate(-50%, -50%)",a.style[k]="0px 0px 0px",a.style.width=g+"px"||"auto",a.style.height=h+"px"||"auto",a.style[i]="auto",a.style.overflow="auto",this.colWidth=g,this.gap=0,{pageWidth:g,pageHeight:h}},EPUBJS.Layout.Fixed.prototype.calculatePages=function(){return{displayedPages:1,pageCount:1}},EPUBJS.Locations=function(a,b,c){this.spine=a,this.store=b,this.credentials=c,this.epubcfi=new EPUBJS.EpubCFI,this._locations=[],this.total=0,this["break"]=150,this._current=0},EPUBJS.Locations.prototype.generate=function(a){var b,c=new RSVP.defer,d=-1,e=this.spine.length,f=function(a){var b,c=d+1,g=a||new RSVP.defer;return c>=e?g.resolve():(d=c,b=new EPUBJS.Chapter(this.spine[d],this.store,this.credentials),this.process(b).then(function(){setTimeout(function(){f(g)},1)})),g.promise}.bind(this);return"number"==typeof a&&(this["break"]=a),b=f().then(function(){this.total=this._locations.length-1,this._currentCfi&&(this.currentLocation=this._currentCfi),c.resolve(this._locations)}.bind(this)),c.promise},EPUBJS.Locations.prototype.process=function(a){return a.load().then(function(b){var c,d,e,f=b,g=f.documentElement.querySelector("body"),h=0;this.sprint(g,function(b){var g,i=b.length,j=0;for(0===h&&(c=f.createRange(),c.setStart(b,0)),g=this["break"]-h,g>i&&(h+=i,j=i);i>j;)h=this["break"],j+=this["break"],j>=i?h=i-(j-this["break"]):(c.setEnd(b,j),e=a.cfiFromRange(c),this._locations.push(e),h=0,j+=1,c=f.createRange(),c.setStart(b,j));d=b}.bind(this)),c&&(c.setEnd(d,d.length),e=a.cfiFromRange(c),this._locations.push(e),h=0)}.bind(this))},EPUBJS.Locations.prototype.sprint=function(a,b){for(var c,d=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,null,!1);c=d.nextNode();)b(c)},EPUBJS.Locations.prototype.locationFromCfi=function(a){return 0===this._locations.length?-1:EPUBJS.core.locationOf(a,this._locations,this.epubcfi.compare)},EPUBJS.Locations.prototype.percentageFromCfi=function(a){var b=this.locationFromCfi(a);return this.percentageFromLocation(b)},EPUBJS.Locations.prototype.percentageFromLocation=function(a){return a&&this.total?a/this.total:0},EPUBJS.Locations.prototype.cfiFromLocation=function(a){var b=-1;return"number"!=typeof a&&(a=parseInt(a)),a>=0&&a1?a/100:a,c=Math.ceil(this.total*b);return this.cfiFromLocation(c)},EPUBJS.Locations.prototype.load=function(a){return this._locations=JSON.parse(a),this.total=this._locations.length-1,this._locations},EPUBJS.Locations.prototype.save=function(a){return JSON.stringify(this._locations)},EPUBJS.Locations.prototype.getCurrent=function(a){return this._current},EPUBJS.Locations.prototype.setCurrent=function(a){var b;if("string"==typeof a)this._currentCfi=a;else{if("number"!=typeof a)return;this._current=a}0!==this._locations.length&&("string"==typeof a?(b=this.locationFromCfi(a),this._current=b):b=a,this.trigger("changed",{percentage:this.percentageFromLocation(b)}))},Object.defineProperty(EPUBJS.Locations.prototype,"currentLocation",{get:function(){return this._current},set:function(a){this.setCurrent(a)}}),RSVP.EventTarget.mixin(EPUBJS.Locations.prototype),EPUBJS.Pagination=function(a){this.pages=[],this.locations=[],this.epubcfi=new EPUBJS.EpubCFI,a&&a.length&&this.process(a)},EPUBJS.Pagination.prototype.process=function(a){a.forEach(function(a){this.pages.push(a.page),this.locations.push(a.cfi)},this),this.pageList=a,this.firstPage=parseInt(this.pages[0]),this.lastPage=parseInt(this.pages[this.pages.length-1]),this.totalPages=this.lastPage-this.firstPage},EPUBJS.Pagination.prototype.pageFromCfi=function(a){var b=-1;if(0===this.locations.length)return-1;var c=EPUBJS.core.indexOfSorted(a,this.locations,this.epubcfi.compare);return-1!=c?b=this.pages[c]:(c=EPUBJS.core.locationOf(a,this.locations,this.epubcfi.compare),b=c-1>=0?this.pages[c-1]:this.pages[0],void 0!==b||(b=-1)),b},EPUBJS.Pagination.prototype.cfiFromPage=function(a){var b=-1;"number"!=typeof a&&(a=parseInt(a));var c=this.pages.indexOf(a);return-1!=c&&(b=this.locations[c]),b},EPUBJS.Pagination.prototype.pageFromPercentage=function(a){var b=Math.round(this.totalPages*a);return b},EPUBJS.Pagination.prototype.percentageFromPage=function(a){var b=(a-this.firstPage)/this.totalPages;return Math.round(1e3*b)/1e3},EPUBJS.Pagination.prototype.percentageFromCfi=function(a){var b=this.pageFromCfi(a),c=this.percentageFromPage(b);return c},EPUBJS.Parser=function(a){this.baseUrl=a||""},EPUBJS.Parser.prototype.container=function(a){var b,c,d,e;return a?(b=a.querySelector("rootfile"))?(c=b.getAttribute("full-path"),d=EPUBJS.core.uri(c).directory,e=a.xmlEncoding,{packagePath:c,basePath:d,encoding:e}):void console.error("No RootFile Found"):void console.error("Container File Not Found")},EPUBJS.Parser.prototype.identifier=function(a){var b;return a?(b=a.querySelector("metadata"),b?this.getElementText(b,"identifier"):void console.error("No Metadata Found")):void console.error("Package File Not Found")},EPUBJS.Parser.prototype.packageContents=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=this;return b&&(this.baseUrl=b),a?(c=a.querySelector("metadata"))?(d=a.querySelector("manifest"))?(e=a.querySelector("spine"))?(f=n.manifest(d),g=n.findNavPath(d),h=n.findTocPath(d,e),i=n.findCoverPath(a),j=Array.prototype.indexOf.call(e.parentNode.childNodes,e),k=n.spine(e,f),l={},k.forEach(function(a){l[a.href]=a.index}),m=n.metadata(c),m.direction=e.getAttribute("page-progression-direction"),{metadata:m,spine:k,manifest:f,navPath:g,tocPath:h,coverPath:i,spineNodeIndex:j,spineIndexByURL:l}):void console.error("No Spine Found"):void console.error("No Manifest Found"):void console.error("No Metadata Found"):void console.error("Package File Not Found")},EPUBJS.Parser.prototype.findNavPath=function(a){var b=a.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']");return b?b.getAttribute("href"):!1},EPUBJS.Parser.prototype.findTocPath=function(a,b){var c,d=a.querySelector("item[media-type='application/x-dtbncx+xml']");return d||(c=b.getAttribute("toc"),c&&(d=a.querySelector("item[id='"+c+"']"))),d?d.getAttribute("href"):!1},EPUBJS.Parser.prototype.metadata=function(a){var b={},c=this;return b.bookTitle=c.getElementText(a,"title"),b.creator=c.getElementText(a,"creator"),b.description=c.getElementText(a,"description"),b.pubdate=c.getElementText(a,"date"),b.publisher=c.getElementText(a,"publisher"),b.identifier=c.getElementText(a,"identifier"),b.language=c.getElementText(a,"language"),b.rights=c.getElementText(a,"rights"), -b.modified_date=c.querySelectorText(a,"meta[property='dcterms:modified']"),b.layout=c.querySelectorText(a,"meta[property='rendition:layout']"),b.orientation=c.querySelectorText(a,"meta[property='rendition:orientation']"),b.spread=c.querySelectorText(a,"meta[property='rendition:spread']"),b},EPUBJS.Parser.prototype.findCoverPath=function(a){var b=a.querySelector("package").getAttribute("version");if("2.0"===b){var c=a.querySelector('meta[name="cover"]');if(c){var d=c.getAttribute("content"),e=a.querySelector("item[id='"+d+"']");return e?e.getAttribute("href"):!1}return!1}var f=a.querySelector("item[properties='cover-image']");return f?f.getAttribute("href"):!1},EPUBJS.Parser.prototype.getElementText=function(a,b){var c,d=a.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/",b);return d&&0!==d.length?(c=d[0],c.childNodes.length?c.childNodes[0].nodeValue:""):""},EPUBJS.Parser.prototype.querySelectorText=function(a,b){var c=a.querySelector(b);return c&&c.childNodes.length?c.childNodes[0].nodeValue:""},EPUBJS.Parser.prototype.manifest=function(a){var b=this.baseUrl,c={},d=a.querySelectorAll("item"),e=Array.prototype.slice.call(d);return e.forEach(function(a){var d=a.getAttribute("id"),e=a.getAttribute("href")||"",f=a.getAttribute("media-type")||"",g=a.getAttribute("properties")||"";c[d]={href:e,url:b+e,type:f,properties:g}}),c},EPUBJS.Parser.prototype.spine=function(a,b){var c=a.getElementsByTagName("itemref"),d=Array.prototype.slice.call(c),e=Array.prototype.indexOf.call(a.parentNode.childNodes,a),f=new EPUBJS.EpubCFI;return d.map(function(a,c){var d=a.getAttribute("idref"),g=f.generateChapterComponent(e,c,d),h=a.getAttribute("properties")||"",i=h.length?h.split(" "):[],j=b[d].properties,k=j.length?j.split(" "):[];return{id:d,linear:a.getAttribute("linear")||"",properties:i,manifestProperties:k,href:b[d].href,url:b[d].url,index:c,cfiBase:g,cfi:"epubcfi("+g+")"}})},EPUBJS.Parser.prototype.querySelectorByType=function(a,b,c){var d=a.querySelector(b+'[*|type="'+c+'"]');if(null!==d&&0!==d.length)return d;d=a.querySelectorAll(b);for(var e=0;ed;++d)e=this.tocItem(g[d],b,c),i[e.id]=e,e.parent?(f=i[e.parent],f.subitems.push(e)):j.push(e);return j},EPUBJS.Parser.prototype.tocItem=function(a,b,c){var d,e=a.getAttribute("id")||!1,f=a.querySelector("content"),g=f.getAttribute("src"),h=a.querySelector("navLabel"),i=h.textContent?h.textContent:"",j=g.split("#"),k=j[0],l=b[k],m=c[l],n=[],o=a.parentNode,p=m?m.cfi:"";return o&&"navPoint"===o.nodeName&&(d=o.getAttribute("id")),e||(l?(m=c[l],e=m.id,p=m.cfi):(e="epubjs-autogen-toc-id-"+EPUBJS.core.uuid(),a.setAttribute("id",e))),{id:e,href:g,label:i,spinePos:l,subitems:n,parent:d,cfi:p}},EPUBJS.Parser.prototype.pageList=function(a,b,c){var d,e,f=this.querySelectorByType(a,"nav","page-list"),g=f?f.querySelectorAll("ol li"):[],h=g.length,i=[];if(!g||0===h)return i;for(d=0;h>d;++d)e=this.pageListItem(g[d],b,c),i.push(e);return i},EPUBJS.Parser.prototype.pageListItem=function(a,b,c){var d,e,f,g=(a.getAttribute("id")||!1,a.querySelector("a")),h=g.getAttribute("href")||"",i=g.textContent||"",j=parseInt(i),k=h.indexOf("epubcfi");return-1!=k?(d=h.split("#"),e=d[0],f=d.length>1?d[1]:!1,{cfi:f,href:h,packageUrl:e,page:j}):{href:h,page:j}},EPUBJS.Render.Iframe=function(){this.iframe=null,this.document=null,this.window=null,this.docEl=null,this.bodyEl=null,this.leftPos=0,this.pageWidth=0,this.id=EPUBJS.core.uuid()},EPUBJS.Render.Iframe.prototype.create=function(){return this.element=document.createElement("div"),this.element.id="epubjs-view:"+this.id,this.isMobile=navigator.userAgent.match(/(iPad|iPhone|iPod|Mobile|Android)/g),this.transform=EPUBJS.core.prefixed("transform"),this.element},EPUBJS.Render.Iframe.prototype.addIframe=function(){return this.iframe=document.createElement("iframe"),this.iframe.id="epubjs-iframe:"+this.id,this.iframe.scrolling=this.scrolling||"no",this.iframe.seamless="seamless",this.iframe.style.border="none",this.iframe.addEventListener("load",this.loaded.bind(this),!1),(this._width||this._height)&&(this.iframe.height=this._height,this.iframe.width=this._width),this.iframe},EPUBJS.Render.Iframe.prototype.load=function(a,b){var c=this,d=new RSVP.defer;return this.window&&this.unload(),this.iframe&&this.element.removeChild(this.iframe),this.iframe=this.addIframe(),this.element.appendChild(this.iframe),this.iframe.onload=function(a){c.document=c.iframe.contentDocument,c.docEl=c.document.documentElement,c.headEl=c.document.head,c.bodyEl=c.document.body||c.document.querySelector("body"),c.window=c.iframe.contentWindow,c.window.addEventListener("resize",c.resized.bind(c),!1),c.leftPos=0,c.setLeft(0),c.bodyEl&&(c.bodyEl.style.margin="0"),d.resolve(c.docEl)},this.iframe.onerror=function(a){d.reject({message:"Error Loading Contents: "+a,stack:(new Error).stack})},this.document=this.iframe.contentDocument,this.document?(this.iframe.contentDocument.open(),this.iframe.contentDocument.write(a),this.iframe.contentDocument.close(),d.promise):(d.reject(new Error("No Document Available")),d.promise)},EPUBJS.Render.Iframe.prototype.loaded=function(a){var b,c,d=this.iframe.contentWindow.location.href;this.document=this.iframe.contentDocument,this.docEl=this.document.documentElement,this.headEl=this.document.head,this.bodyEl=this.document.body||this.document.querySelector("body"),this.window=this.iframe.contentWindow,this.window.focus(),"about:blank"!=d&&(b=this.iframe.contentDocument.querySelector("base"),c=b.getAttribute("href"),this.trigger("render:loaded",c))},EPUBJS.Render.Iframe.prototype.resize=function(a,b){this.element&&(this.element.style.height=b,isNaN(a)||a%2===0||(a+=1),this.element.style.width=a,this.iframe&&(this.iframe.height=b,this.iframe.width=a),this._height=b,this._width=a,this.width=this.element.getBoundingClientRect().width||a,this.height=this.element.getBoundingClientRect().height||b)},EPUBJS.Render.Iframe.prototype.resized=function(a){this.width=this.iframe.getBoundingClientRect().width,this.height=this.iframe.getBoundingClientRect().height},EPUBJS.Render.Iframe.prototype.totalWidth=function(){return this.docEl.scrollWidth},EPUBJS.Render.Iframe.prototype.totalHeight=function(){return this.docEl.scrollHeight},EPUBJS.Render.Iframe.prototype.setPageDimensions=function(a,b){this.pageWidth=a,this.pageHeight=b},EPUBJS.Render.Iframe.prototype.setDirection=function(a){this.direction=a,this.docEl&&"rtl"==this.docEl.dir&&(this.docEl.dir="rtl","pre-paginated"!==this.layout&&(this.docEl.style.position="static",this.docEl.style.right="auto"))},EPUBJS.Render.Iframe.prototype.setLeft=function(a){this.isMobile?this.docEl.style[this.transform]="translate("+-a+"px, 0)":this.document.defaultView.scrollTo(a,0)},EPUBJS.Render.Iframe.prototype.setLayout=function(a){this.layout=a},EPUBJS.Render.Iframe.prototype.setStyle=function(a,b,c){c&&(a=EPUBJS.core.prefixed(a)),this.bodyEl&&(this.bodyEl.style[a]=b)},EPUBJS.Render.Iframe.prototype.removeStyle=function(a){this.bodyEl&&(this.bodyEl.style[a]="")},EPUBJS.Render.Iframe.prototype.addHeadTag=function(a,b,c){var d=c||this.document,e=d.createElement(a),f=d.head;for(var g in b)e.setAttribute(g,b[g]);f&&f.insertBefore(e,f.firstChild)},EPUBJS.Render.Iframe.prototype.page=function(a){this.leftPos=this.pageWidth*(a-1),"rtl"===this.direction&&(this.leftPos=-1*this.leftPos),this.setLeft(this.leftPos)},EPUBJS.Render.Iframe.prototype.getPageNumberByElement=function(a){var b,c;if(a)return b=this.leftPos+a.getBoundingClientRect().left,c=Math.floor(b/this.pageWidth)+1},EPUBJS.Render.Iframe.prototype.getPageNumberByRect=function(a){var b,c;return b=this.leftPos+a.left,c=Math.floor(b/this.pageWidth)+1},EPUBJS.Render.Iframe.prototype.getBaseElement=function(){return this.bodyEl},EPUBJS.Render.Iframe.prototype.getDocumentElement=function(){return this.docEl},EPUBJS.Render.Iframe.prototype.isElementVisible=function(a){var b,c;return a&&"function"==typeof a.getBoundingClientRect&&(b=a.getBoundingClientRect(),c=b.left,0!==b.width&&0!==b.height&&c>=0&&c=1&&a<=this.displayedPages?(this.chapterPos=a,this.render.page(a),this.visibleRangeCfi=this.getVisibleRangeCfi(),this.currentLocationCfi=this.visibleRangeCfi.start,this.trigger("renderer:locationChanged",this.currentLocationCfi),this.trigger("renderer:visibleRangeChanged",this.visibleRangeCfi),!0):!1:(console.warn("pageMap not set, queuing"),this._q.enqueue("page",arguments),!0)},EPUBJS.Renderer.prototype.nextPage=function(){return this.page(this.chapterPos+1)},EPUBJS.Renderer.prototype.prevPage=function(){return this.page(this.chapterPos-1)},EPUBJS.Renderer.prototype.pageByElement=function(a){var b;a&&(b=this.render.getPageNumberByElement(a),this.page(b))},EPUBJS.Renderer.prototype.lastPage=function(){return this._moving?this._q.enqueue("lastPage",arguments):void this.page(this.displayedPages)},EPUBJS.Renderer.prototype.firstPage=function(){return this._moving?this._q.enqueue("firstPage",arguments):void this.page(1)},EPUBJS.Renderer.prototype.section=function(a){var b=this.doc.getElementById(a);b&&this.pageByElement(b)},EPUBJS.Renderer.prototype.firstElementisTextNode=function(a){var b=a.childNodes,c=b.length;return c&&b[0]&&3===b[0].nodeType&&b[0].textContent.trim().length?!0:!1},EPUBJS.Renderer.prototype.isGoodNode=function(a){var b=["audio","canvas","embed","iframe","img","math","object","svg","video"];return-1!==b.indexOf(a.tagName.toLowerCase())?!0:this.firstElementisTextNode(a)},EPUBJS.Renderer.prototype.walk=function(a,b,c){for(var d,e,f,g,h=a,i=[h],j=1e4,k=0;!d&&i.length;){if(a=i.shift(),this.containsPoint(a,b,c)&&this.isGoodNode(a)&&(d=a),!d&&a&&a.childElementCount>0){if(e=a.children,!e||!e.length)return d;f=e.length?e.length:0;for(var l=f-1;l>=0;l--)e[l]!=g&&i.unshift(e[l])}if(!d&&0===i.length&&h&&null!==h.parentNode&&(i.push(h.parentNode),g=h,h=h.parentNode),k++,k>j){console.error("ENDLESS LOOP");break}}return d},EPUBJS.Renderer.prototype.containsPoint=function(a,b,c){var d;return a&&"function"==typeof a.getBoundingClientRect&&(d=a.getBoundingClientRect(),0!==d.width&&0!==d.height&&d.left>=b&&b<=d.left+d.width)?!0:!1},EPUBJS.Renderer.prototype.textSprint=function(a,b){var c,d,e=function(a){return/^\s*$/.test(a.data)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT};try{for(c=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,{acceptNode:e},!1);d=c.nextNode();)b(d)}catch(f){for(c=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,e,!1);d=c.nextNode();)b(d)}},EPUBJS.Renderer.prototype.sprint=function(a,b){for(var c,d=document.createTreeWalker(a,NodeFilter.SHOW_ELEMENT,null,!1);c=d.nextNode();)b(c)},EPUBJS.Renderer.prototype.mapPage=function(){var a,b,c,d,e,f,g,h,i=this,j=[],k=this.render.getBaseElement(),l=1,m=this.layout.colWidth+this.layout.gap,n=this.formated.pageWidth*(this.chapterPos-1),o=m*l-n,p=0,q=function(b){var c,e,f;if(b.nodeType==Node.TEXT_NODE){if(e=document.createRange(),e.selectNodeContents(b),c=e.getBoundingClientRect(),!c||0===c.width&&0===c.height)return;c.left>p&&(f=r(b)),c.right>p&&(f=r(b)),d=b,f&&(a=null)}},r=function(e){var f,g=i.splitTextNodeIntoWordsRanges(e);return g.forEach(function(e){var g=e.getBoundingClientRect();!g||0===g.width&&0===g.height||(g.left+g.width0&&(b&&(b.setEnd(a,e),c.push(b)),b=this.doc.createRange(),b.setStart(a,e+1));return b&&(b.setEnd(a,d.length),c.push(b)),c},EPUBJS.Renderer.prototype.rangePosition=function(a){var b,c;return c=a.getClientRects(),c.length?b=c[0]:null},EPUBJS.Renderer.prototype.getPageCfi=function(){var a=2*this.chapterPos-1;return this.pageMap[a].start},EPUBJS.Renderer.prototype.getRange=function(a,b,c){var d,e=this.doc.createRange();return c=!0,"undefined"==typeof document.caretPositionFromPoint||c?"undefined"==typeof document.caretRangeFromPoint||c?(this.visibileEl=this.findElementAfter(a,b),e.setStart(this.visibileEl,1)):e=this.doc.caretRangeFromPoint(a,b):(d=this.doc.caretPositionFromPoint(a,b),e.setStart(d.offsetNode,d.offset)),e},EPUBJS.Renderer.prototype.pagesInCurrentChapter=function(){var a;return this.pageMap?a=this.pageMap.length:(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.currentRenderedPage=function(){var a;return this.pageMap?a=this.spreads&&this.pageMap.length>1?2*this.chapterPos-1:this.chapterPos:(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.getRenderedPagesLeft=function(){var a,b,c;return this.pageMap?(b=this.pageMap.length,a=this.spreads?2*this.chapterPos-1:this.chapterPos,c=b-a):(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.getVisibleRangeCfi=function(){var a,b,c;return this.pageMap?(this.spreads?(a=2*this.chapterPos,b=this.pageMap[a-2],c=b,this.pageMap.length>1&&this.pageMap.length>a-1&&(c=this.pageMap[a-1])):(a=this.chapterPos,b=this.pageMap[a-1],c=b),b||(console.warn("page range miss:",a,this.pageMap),b=this.pageMap[this.pageMap.length-1],c=b),{start:b.start,end:c.end}):(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.gotoCfi=function(a){var b,c,d;if(this._moving)return this._q.enqueue("gotoCfi",arguments);if(EPUBJS.core.isString(a)&&(a=this.epubcfi.parse(a)),"undefined"==typeof document.evaluate)c=this.epubcfi.addMarker(a,this.doc),c&&(b=this.render.getPageNumberByElement(c),this.epubcfi.removeMarker(c,this.doc),this.page(b));else if(d=this.epubcfi.generateRangeFromCfi(a,this.doc)){var e=d.getBoundingClientRect();b=e?this.render.getPageNumberByRect(e):1,this.page(b),this.currentLocationCfi=a.str}else this.page(1)},EPUBJS.Renderer.prototype.findFirstVisible=function(a){var b,c=a||this.render.getBaseElement();return b=this.walk(c,0,0),b?b:a},EPUBJS.Renderer.prototype.findElementAfter=function(a,b,c){var d,e=c||this.render.getBaseElement();return d=this.walk(e,a,b),d?d:e},EPUBJS.Renderer.prototype.resize=function(a,b,c){this.width=a,this.height=b,c!==!1&&this.render.resize(this.width,this.height),this.contents&&this.reformat(),this.trigger("renderer:resized",{width:this.width,height:this.height})},EPUBJS.Renderer.prototype.onResized=function(a){this.trigger("renderer:beforeResize");var b=this.container.clientWidth,c=this.container.clientHeight;this.resize(b,c,!1)},EPUBJS.Renderer.prototype.addEventListeners=function(){this.render.document&&this.listenedEvents.forEach(function(a){this.render.document.addEventListener(a,this.triggerEvent.bind(this),!1)},this)},EPUBJS.Renderer.prototype.removeEventListeners=function(){this.render.document&&this.listenedEvents.forEach(function(a){this.render.document.removeEventListener(a,this.triggerEvent,!1)},this)},EPUBJS.Renderer.prototype.triggerEvent=function(a){this.trigger("renderer:"+a.type,a)},EPUBJS.Renderer.prototype.addSelectionListeners=function(){this.render.document.addEventListener("selectionchange",this.onSelectionChange.bind(this),!1)},EPUBJS.Renderer.prototype.removeSelectionListeners=function(){this.render.document&&this.doc.removeEventListener("selectionchange",this.onSelectionChange,!1)},EPUBJS.Renderer.prototype.onSelectionChange=function(a){this.selectionEndTimeout&&clearTimeout(this.selectionEndTimeout),this.selectionEndTimeout=setTimeout(function(){this.selectedRange=this.render.window.getSelection(),this.trigger("renderer:selected",this.selectedRange)}.bind(this),500)},EPUBJS.Renderer.prototype.setMinSpreadWidth=function(a){this.minSpreadWidth=a,this.spreads=this.determineSpreads(a)},EPUBJS.Renderer.prototype.determineSpreads=function(a){return this.isForcedSingle||!a||this.width=g&&c&&c(!0),e=!0)};b(a,f)}.bind(this))},RSVP.EventTarget.mixin(EPUBJS.Renderer.prototype);var EPUBJS=EPUBJS||{};EPUBJS.replace={},EPUBJS.replace.hrefs=function(a,b){var c=this,d=function(a,d){var e,f,g,h,i,j=a.getAttribute("href"),k=j.search("://");-1!=k?a.setAttribute("target","_blank"):(g=b.render.docEl.querySelector("base"),i=g.getAttribute("href"),h=EPUBJS.core.uri(i),e=h.directory,f=e?"file"===h.protocol?EPUBJS.core.resolveUrl(h.base,j):EPUBJS.core.resolveUrl(e,j):j,a.onclick=function(){return c["goto"](f),!1}),d()};b.replace("a[href]",d,a)},EPUBJS.replace.head=function(a,b){b.replaceWithStored("link[href]","href",EPUBJS.replace.links,a)},EPUBJS.replace.resources=function(a,b){b.replaceWithStored("[src]","src",EPUBJS.replace.srcs,a)},EPUBJS.replace.svg=function(a,b){b.replaceWithStored("svg image","xlink:href",function(a,b,c){a.getUrl(b).then(c)},a)},EPUBJS.replace.srcs=function(a,b,c){a.getUrl(b).then(c)},EPUBJS.replace.links=function(a,b,c,d){"stylesheet"===d.getAttribute("rel")?EPUBJS.replace.stylesheets(a,b).then(function(a,b){c(a,b)},function(a){c(null)}):a.getUrl(b).then(c,function(a){c(null)})},EPUBJS.replace.stylesheets=function(a,b){var c=new RSVP.defer;if(a)return a.getText(b).then(function(d){EPUBJS.replace.cssImports(a,b,d).then(function(e){d=e+d,EPUBJS.replace.cssUrls(a,b,d).then(function(a){var b=window.URL||window.webkitURL||window.mozURL,d=new Blob([a],{type:"text/css"}),e=b.createObjectURL(d);c.resolve(e)},function(a){c.reject(a)})},function(a){c.reject(a)})},function(a){c.reject(a)}),c.promise},EPUBJS.replace.cssImports=function(a,b,c){var d=new RSVP.defer;if(a){for(var e,f=/@import\s+(?:url\()?\'?\"?((?!data:)[^\'|^\"^\)]*)\'?\"?\)?/gi,g=[],h="";e=f.exec(c);)g.push(e[1]);return 0===g.length&&d.resolve(h),g.forEach(function(c){var e=EPUBJS.core.resolveUrl(b,c);e=EPUBJS.core.uri(e).path,a.getText(e).then(function(a){h+=a,g.indexOf(c)===g.length-1&&d.resolve(h)},function(a){d.reject(a)})}),d.promise}},EPUBJS.replace.cssUrls=function(a,b,c){var d=new RSVP.defer,e=c.match(/url\(\'?\"?((?!data:)[^\'|^\"^\)]*)\'?\"?\)/g);if(a){if(!e)return d.resolve(c),d.promise;var f=e.map(function(e){var f=EPUBJS.core.resolveUrl(b,e.replace(/url\(|[|\)|\'|\"]|\?.*$/g,""));return a.getUrl(f).then(function(a){c=c.replace(e,'url("'+a+'")')},function(a){d.reject(a)})});return RSVP.all(f).then(function(){d.resolve(c)}),d.promise}},EPUBJS.Storage=function(a){this.checkRequirements(),this.urlCache={},this.withCredentials=a,this.URL=window.URL||window.webkitURL||window.mozURL,this.offline=!1},EPUBJS.Storage.prototype.checkRequirements=function(a){"undefined"==typeof localforage&&console.error("localForage library not loaded")},EPUBJS.Storage.prototype.put=function(a,b){var c=new RSVP.defer,d=a.length,e=0,f=function(b){var c,g,h=b||new RSVP.defer;return e>=d?h.resolve():(c=a[e].url,g=window.encodeURIComponent(c),EPUBJS.core.request(c,"binary").then(function(a){return localforage.setItem(g,a)}).then(function(a){e++,setTimeout(function(){f(h)},1)})),h.promise}.bind(this);return Array.isArray(a)||(a=[a]),f().then(function(){c.resolve()}.bind(this)),c.promise},EPUBJS.Storage.prototype.token=function(a,b){var c=window.encodeURIComponent(a);return localforage.setItem(c,b).then(function(a){return null===a?!1:!0})},EPUBJS.Storage.prototype.isStored=function(a){var b=window.encodeURIComponent(a);return localforage.getItem(b).then(function(a){return null===a?!1:!0})},EPUBJS.Storage.prototype.getText=function(a){var b=window.encodeURIComponent(a);return EPUBJS.core.request(a,"arraybuffer",this.withCredentials).then(function(a){return this.offline&&(this.offline=!1,this.trigger("offline",!1)),localforage.setItem(b,a),a}.bind(this)).then(function(b){var c=new RSVP.defer,d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;return f.addEventListener("loadend",function(){c.resolve(f.result)}),f.readAsText(e,d),c.promise})["catch"](function(){var c=new RSVP.defer,d=localforage.getItem(b);return this.offline||(this.offline=!0,this.trigger("offline",!0)),d?(d.then(function(b){var d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;f.addEventListener("loadend",function(){c.resolve(f.result)}),f.readAsText(e,d)}),c.promise):(c.reject({message:"File not found in the storage: "+a,stack:(new Error).stack}),c.promise)}.bind(this))},EPUBJS.Storage.prototype.getUrl=function(a){var b=window.encodeURIComponent(a);return EPUBJS.core.request(a,"arraybuffer",this.withCredentials).then(function(c){return this.offline&&(this.offline=!1,this.trigger("offline",!1)),localforage.setItem(b,c),a}.bind(this))["catch"](function(){var c,d,e=new RSVP.defer,f=window.URL||window.webkitURL||window.mozURL;return this.offline||(this.offline=!0,this.trigger("offline",!0)),b in this.urlCache?(e.resolve(this.urlCache[b]),e.promise):(c=localforage.getItem(b))?(c.then(function(c){var g=new Blob([c],{type:EPUBJS.core.getMimeType(a)});d=f.createObjectURL(g),e.resolve(d),this.urlCache[b]=d}.bind(this)),e.promise):(e.reject({message:"File not found in the storage: "+a,stack:(new Error).stack}),e.promise)}.bind(this))},EPUBJS.Storage.prototype.getXml=function(a){var b=window.encodeURIComponent(a);return EPUBJS.core.request(a,"arraybuffer",this.withCredentials).then(function(a){return this.offline&&(this.offline=!1,this.trigger("offline",!1)),localforage.setItem(b,a),a}.bind(this)).then(function(b){var c=new RSVP.defer,d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;return f.addEventListener("loadend",function(){var a=new DOMParser,b=a.parseFromString(f.result,"text/xml");c.resolve(b)}),f.readAsText(e,d),c.promise})["catch"](function(){var c=new RSVP.defer,d=localforage.getItem(b);return this.offline||(this.offline=!0,this.trigger("offline",!0)),d?(d.then(function(b){var d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;f.addEventListener("loadend",function(){var a=new DOMParser,b=a.parseFromString(f.result,"text/xml");c.resolve(b)}),f.readAsText(e,d)}),c.promise):(c.reject({message:"File not found in the storage: "+a,stack:(new Error).stack}),c.promise)}.bind(this))},EPUBJS.Storage.prototype.revokeUrl=function(a){var b=window.URL||window.webkitURL||window.mozURL,c=this.urlCache[a];c&&b.revokeObjectURL(c)},EPUBJS.Storage.prototype.failed=function(a){console.error(a)},RSVP.EventTarget.mixin(EPUBJS.Storage.prototype),EPUBJS.Unarchiver=function(a){this.checkRequirements(),this.urlCache={}},EPUBJS.Unarchiver.prototype.checkRequirements=function(a){"undefined"==typeof JSZip&&console.error("JSZip lib not loaded")},EPUBJS.Unarchiver.prototype.open=function(a,b){if(a instanceof ArrayBuffer){this.zip=new JSZip(a);var c=new RSVP.defer;return c.resolve(),c.promise}return EPUBJS.core.request(a,"binary").then(function(a){this.zip=new JSZip(a)}.bind(this))},EPUBJS.Unarchiver.prototype.getXml=function(a,b){ -var c=window.decodeURIComponent(a);return this.getText(c,b).then(function(b){var c=new DOMParser,d=EPUBJS.core.getMimeType(a);return c.parseFromString(b,d)})},EPUBJS.Unarchiver.prototype.getUrl=function(a,b){var c,d,e=this,f=new RSVP.defer,g=window.decodeURIComponent(a),h=this.zip.file(g),i=window.URL||window.webkitURL||window.mozURL;return h?a in this.urlCache?(f.resolve(this.urlCache[a]),f.promise):(d=new Blob([h.asUint8Array()],{type:EPUBJS.core.getMimeType(h.name)}),c=i.createObjectURL(d),f.resolve(c),e.urlCache[a]=c,f.promise):(f.reject({message:"File not found in the epub: "+a,stack:(new Error).stack}),f.promise)},EPUBJS.Unarchiver.prototype.getText=function(a,b){var c,d=new RSVP.defer,e=window.decodeURIComponent(a),f=this.zip.file(e);return f?(c=f.asText(),d.resolve(c),d.promise):(d.reject({message:"File not found in the epub: "+a,stack:(new Error).stack}),d.promise)},EPUBJS.Unarchiver.prototype.revokeUrl=function(a){var b=window.URL||window.webkitURL||window.mozURL,c=this.urlCache[a];c&&b.revokeObjectURL(c)},EPUBJS.Unarchiver.prototype.failed=function(a){console.error(a)},EPUBJS.Unarchiver.prototype.afterSaved=function(a){this.callback()},EPUBJS.Unarchiver.prototype.toStorage=function(a){function b(){f--,0===f&&e.afterSaved()}var c=0,d=20,e=this,f=a.length;a.forEach(function(a){setTimeout(function(a){e.saveEntryFileToStorage(a,b)},c,a),c+=d}),console.log("time",c)},function(){var a={application:{ecmascript:["es","ecma"],javascript:"js",ogg:"ogx",pdf:"pdf",postscript:["ps","ai","eps","epsi","epsf","eps2","eps3"],"rdf+xml":"rdf",smil:["smi","smil"],"xhtml+xml":["xhtml","xht"],xml:["xml","xsl","xsd","opf","ncx"],zip:"zip","x-httpd-eruby":"rhtml","x-latex":"latex","x-maker":["frm","maker","frame","fm","fb","book","fbdoc"],"x-object":"o","x-shockwave-flash":["swf","swfl"],"x-silverlight":"scr","epub+zip":"epub","font-tdpfr":"pfr","inkml+xml":["ink","inkml"],json:"json","jsonml+json":"jsonml","mathml+xml":"mathml","metalink+xml":"metalink",mp4:"mp4s","omdoc+xml":"omdoc",oxps:"oxps","vnd.amazon.ebook":"azw",widget:"wgt","x-dtbook+xml":"dtb","x-dtbresource+xml":"res","x-font-bdf":"bdf","x-font-ghostscript":"gsf","x-font-linux-psf":"psf","x-font-otf":"otf","x-font-pcf":"pcf","x-font-snf":"snf","x-font-ttf":["ttf","ttc"],"x-font-type1":["pfa","pfb","pfm","afm"],"x-font-woff":"woff","x-mobipocket-ebook":["prc","mobi"],"x-mspublisher":"pub","x-nzb":"nzb","x-tgif":"obj","xaml+xml":"xaml","xml-dtd":"dtd","xproc+xml":"xpl","xslt+xml":"xslt","internet-property-stream":"acx","x-compress":"z","x-compressed":"tgz","x-gzip":"gz"},audio:{flac:"flac",midi:["mid","midi","kar","rmi"],mpeg:["mpga","mpega","mp2","mp3","m4a","mp2a","m2a","m3a"],mpegurl:"m3u",ogg:["oga","ogg","spx"],"x-aiff":["aif","aiff","aifc"],"x-ms-wma":"wma","x-wav":"wav",adpcm:"adp",mp4:"mp4a",webm:"weba","x-aac":"aac","x-caf":"caf","x-matroska":"mka","x-pn-realaudio-plugin":"rmp",xm:"xm",mid:["mid","rmi"]},image:{gif:"gif",ief:"ief",jpeg:["jpeg","jpg","jpe"],pcx:"pcx",png:"png","svg+xml":["svg","svgz"],tiff:["tiff","tif"],"x-icon":"ico",bmp:"bmp",webp:"webp","x-pict":["pic","pct"],"x-tga":"tga","cis-cod":"cod"},message:{rfc822:["eml","mime","mht","mhtml","nws"]},text:{"cache-manifest":["manifest","appcache"],calendar:["ics","icz","ifb"],css:"css",csv:"csv",h323:"323",html:["html","htm","shtml","stm"],iuls:"uls",mathml:"mml",plain:["txt","text","brf","conf","def","list","log","in","bas"],richtext:"rtx","tab-separated-values":"tsv","x-bibtex":"bib","x-dsrc":"d","x-diff":["diff","patch"],"x-haskell":"hs","x-java":"java","x-literate-haskell":"lhs","x-moc":"moc","x-pascal":["p","pas"],"x-pcs-gcd":"gcd","x-perl":["pl","pm"],"x-python":"py","x-scala":"scala","x-setext":"etx","x-tcl":["tcl","tk"],"x-tex":["tex","ltx","sty","cls"],"x-vcard":"vcf",sgml:["sgml","sgm"],"x-c":["c","cc","cxx","cpp","h","hh","dic"],"x-fortran":["f","for","f77","f90"],"x-opml":"opml","x-nfo":"nfo","x-sfv":"sfv","x-uuencode":"uu",webviewhtml:"htt"},video:{mpeg:["mpeg","mpg","mpe","m1v","m2v","mp2","mpa","mpv2"],mp4:["mp4","mp4v","mpg4"],quicktime:["qt","mov"],ogg:"ogv","vnd.mpegurl":["mxu","m4u"],"x-flv":"flv","x-la-asf":["lsf","lsx"],"x-mng":"mng","x-ms-asf":["asf","asx","asr"],"x-ms-wm":"wm","x-ms-wmv":"wmv","x-ms-wmx":"wmx","x-ms-wvx":"wvx","x-msvideo":"avi","x-sgi-movie":"movie","x-matroska":["mpv","mkv","mk3d","mks"],"3gpp2":"3g2",h261:"h261",h263:"h263",h264:"h264",jpeg:"jpgv",jpm:["jpm","jpgm"],mj2:["mj2","mjp2"],"vnd.ms-playready.media.pyv":"pyv","vnd.uvvu.mp4":["uvu","uvvu"],"vnd.vivo":"viv",webm:"webm","x-f4v":"f4v","x-m4v":"m4v","x-ms-vob":"vob","x-smv":"smv"}},b=function(){var b,c,d,e,f={};for(b in a)if(a.hasOwnProperty(b))for(c in a[b])if(a[b].hasOwnProperty(c))if(d=a[b][c],"string"==typeof d)f[d]=b+"/"+c;else for(e=0;e1)throw new Error("Second argument not supported");if("object"!=typeof a)throw new TypeError("Argument must be an object");return h.prototype=a,new h},Aa=[],Ba=void 0,Ca=1,Da=2,Ea=new y,Fa=new y;E.prototype._validateInput=function(a){return xa(a)},E.prototype._validationError=function(){return new Error("Array Methods must be provided an Array")},E.prototype._init=function(){this._result=new Array(this.length)},E.prototype._enumerate=function(){for(var a=this.length,b=this.promise,c=this._input,d=0;b._state===Ba&&d=i)l.resolve();else{if(c&&c.cancelled)return e.remove(),this.element.removeChild(f),void l.reject(new Error("User cancelled"));h=g,b=new EPUBJS.Chapter(this.spine[h],this.store),e.displayChapter(b,this.globalLayoutProperties).then(function(a){e.pageMap.forEach(function(a){j+=1,d.push({cfi:a.start,page:j})}),e.pageMap.length%2>0&&e.spreads&&(j+=1,d.push({cfi:e.pageMap[e.pageMap.length-1].end,page:j})),setTimeout(function(){k(l)},1)})}return l.promise}.bind(this);k().then(function(){e.remove(),this.element.removeChild(f),g.resolve(d)}.bind(this),function(a){g.reject(a)});return g.promise},EPUBJS.Book.prototype.generatePagination=function(a,b,c){var d=this,e=new RSVP.defer;return this.ready.spine.promise.then(function(){d.generatePageList(a,b,c).then(function(a){d.pageList=d.contents.pageList=a,d.pagination.process(a),d.ready.pageList.resolve(d.pageList),e.resolve(d.pageList)},function(a){e.reject(a)})}),e.promise},EPUBJS.Book.prototype.loadPagination=function(a){var b;return b="string"==typeof a?JSON.parse(a):a,b&&b.length&&(this.pageList=b,this.pagination.process(this.pageList),this.ready.pageList.resolve(this.pageList)),this.pageList},EPUBJS.Book.prototype.getPageList=function(){return this.ready.pageList.promise},EPUBJS.Book.prototype.getMetadata=function(){return this.ready.metadata.promise},EPUBJS.Book.prototype.getToc=function(){return this.ready.toc.promise},EPUBJS.Book.prototype.networkListeners=function(){var a=this;window.addEventListener("offline",function(b){a.online=!1,a.settings.storage&&a.fromStorage(!0),a.trigger("book:offline")},!1),window.addEventListener("online",function(b){a.online=!0,a.settings.storage&&a.fromStorage(!1),a.trigger("book:online")},!1)},EPUBJS.Book.prototype.listenToRenderer=function(a){var b=this;a.Events.forEach(function(c){a.on(c,function(a){b.trigger(c,a)})}),a.on("renderer:visibleRangeChanged",function(a){var b,c,d,e=[];this.pageList.length>0&&(b=this.pagination.pageFromCfi(a.start),d=this.pagination.percentageFromPage(b),e.push(b),a.end&&(c=this.pagination.pageFromCfi(a.end),e.push(c)),this.trigger("book:pageChanged",{anchorPage:b,percentage:d,pageRange:e}))}.bind(this)),a.on("render:loaded",this.loadChange.bind(this))},EPUBJS.Book.prototype.loadChange=function(a){var b,c,d=EPUBJS.core.uri(a),e=EPUBJS.core.uri(this.currentChapter.absolute);d.path!=e.path?(console.warn("Miss Match",d.path,this.currentChapter.absolute),b=this.spineIndexByURL[d.filename],c=new EPUBJS.Chapter(this.spine[b],this.store),this.currentChapter=c,this.renderer.currentChapter=c,this.renderer.afterLoad(this.renderer.render.docEl),this.renderer.beforeDisplay(function(){this.renderer.afterDisplay()}.bind(this))):this._rendering||this.renderer.reformat()},EPUBJS.Book.prototype.unlistenToRenderer=function(a){a.Events.forEach(function(b){a.off(b)})},EPUBJS.Book.prototype.coverUrl=function(){var a=this.ready.cover.promise.then(function(a){return this.settings.fromStorage?this.store.getUrl(this.contents.cover):this.settings.contained?this.zip.getUrl(this.contents.cover):this.contents.cover}.bind(this));return a.then(function(a){this.cover=a}.bind(this)),a},EPUBJS.Book.prototype.loadXml=function(a){return this.settings.fromStorage?this.store.getXml(a,this.settings.encoding):this.settings.contained?this.zip.getXml(a,this.settings.encoding):EPUBJS.core.request(a,"xml",this.settings.withCredentials)},EPUBJS.Book.prototype.urlFrom=function(a){var b,c=EPUBJS.core.uri(a),d=c.protocol,e="/"==c.path[0],f=window.location,g=f.origin||f.protocol+"//"+f.host,h=document.getElementsByTagName("base");return h.length&&(b=h[0].href),c.protocol?c.origin+c.path:!d&&e?(b||g)+c.path:d||e?void 0:EPUBJS.core.resolveUrl(b||f.pathname,c.path)},EPUBJS.Book.prototype.unarchive=function(a){return this.zip=new EPUBJS.Unarchiver,this.store=this.zip,this.zip.open(a)},EPUBJS.Book.prototype.isContained=function(a){if(a instanceof ArrayBuffer)return!0;var b=EPUBJS.core.uri(a);return!(!b.extension||"epub"!=b.extension&&"zip"!=b.extension)},EPUBJS.Book.prototype.isSaved=function(a){var b;return!!localStorage&&(b=localStorage.getItem(a),!(!localStorage||null===b))},EPUBJS.Book.prototype.generateBookKey=function(a){return"epubjs:"+EPUBJS.VERSION+":"+window.location.host+":"+a},EPUBJS.Book.prototype.saveContents=function(){return!!localStorage&&void localStorage.setItem(this.settings.bookKey,JSON.stringify(this.contents))},EPUBJS.Book.prototype.removeSavedContents=function(){return!!localStorage&&void localStorage.removeItem(this.settings.bookKey)},EPUBJS.Book.prototype.renderTo=function(a){var b,c=this;if(EPUBJS.core.isElement(a))this.element=a;else{if("string"!=typeof a)return void console.error("Not an Element");this.element=EPUBJS.core.getEl(a)}return b=this.opened.then(function(){return c.renderer.initialize(c.element,c.settings.width,c.settings.height),c.metadata.direction&&c.renderer.setDirection(c.metadata.direction),c._rendered(),c.startDisplay()})},EPUBJS.Book.prototype.startDisplay=function(){var a;return a=this.settings.goto?this.goto(this.settings.goto):this.settings.previousLocationCfi?this.gotoCfi(this.settings.previousLocationCfi):this.displayChapter(this.spinePos,this.settings.displayLastPage)},EPUBJS.Book.prototype.restore=function(a){var b,c=this,d=["manifest","spine","metadata","cover","toc","spineNodeIndex","spineIndexByURL","globalLayoutProperties"],e=!1,f=this.generateBookKey(a),g=localStorage.getItem(f),h=d.length;if(this.settings.clearSaved&&(e=!0),!e&&"undefined"!=g&&null!==g)for(c.contents=JSON.parse(g),b=0;b=this.spine.length)&&(console.warn("Not A Valid Location"),f=0,b=!1,e=!1),g=new EPUBJS.Chapter(this.spine[f],this.store),this._rendering=!0,this._needsAssetReplacement()&&g.registerHook("beforeChapterRender",[EPUBJS.replace.head,EPUBJS.replace.resources,EPUBJS.replace.svg],!0),h.currentChapter=g,d=h.renderer.displayChapter(g,this.globalLayoutProperties),e?h.renderer.gotoCfi(e):b&&h.renderer.lastPage(),d.then(function(a){h.spinePos=f,i.resolve(h.renderer),h.settings.fromStorage===!1&&h.settings.contained===!1&&h.preloadNextChapter(),h._rendering=!1,h._displayQ.dequeue(),0===h._displayQ.length()&&h._gotoQ.dequeue()},function(a){console.error("Could not load Chapter: "+g.absolute,a),h.trigger("book:chapterLoadFailed",g.absolute),h._rendering=!1,i.reject(a)}),i.promise):(this._q.enqueue("displayChapter",arguments),i.reject({message:"Rendering",stack:(new Error).stack}),i.promise)},EPUBJS.Book.prototype.nextPage=function(a){var a=a||new RSVP.defer;if(!this.isRendered)return this._q.enqueue("nextPage",[a]),a.promise;var b=this.renderer.nextPage();return b?(a.resolve(!0),a.promise):this.nextChapter(a)},EPUBJS.Book.prototype.prevPage=function(a){var a=a||new RSVP.defer;if(!this.isRendered)return this._q.enqueue("prevPage",[a]),a.promise;var b=this.renderer.prevPage();return b?(a.resolve(!0),a.promise):this.prevChapter(a)},EPUBJS.Book.prototype.nextChapter=function(a){var a=a||new RSVP.defer;if(this.spinePos0){for(var b=this.spinePos-1;this.spine[b]&&this.spine[b].linear&&"no"==this.spine[b].linear;)b--;if(b>=0)return this.displayChapter(b,!0,a)}return this.trigger("book:atStart"),a.resolve(!0),a.promise},EPUBJS.Book.prototype.getCurrentLocationCfi=function(){return!!this.isRendered&&this.renderer.currentLocationCfi},EPUBJS.Book.prototype.goto=function(a){return 0===a.indexOf("epubcfi(")?this.gotoCfi(a):a.indexOf("%")===a.length-1?this.gotoPercentage(parseInt(a.substring(0,a.length-1))/100):"number"==typeof a||isNaN(a)===!1?this.gotoPage(a):this.gotoHref(a)},EPUBJS.Book.prototype.gotoCfi=function(a,b){var c,d,e,f,g,h=b||new RSVP.defer;return this.isRendered?this._moving||this._rendering?(console.warn("Renderer is moving"),this._gotoQ.enqueue("gotoCfi",[a,h]),!1):(c=new EPUBJS.EpubCFI(a),d=c.spinePos,d!=-1&&(e=this.spine[d],f=h.promise,this._moving=!0,this.currentChapter&&this.spinePos===d?(this.renderer.gotoCfi(c),this._moving=!1,h.resolve(this.renderer.currentLocationCfi)):(e&&d!=-1||(d=0,e=this.spine[d]),g=this.displayChapter(a),g.then(function(a){this._moving=!1,h.resolve(a.currentLocationCfi)}.bind(this),function(){this._moving=!1}.bind(this))),f.then(function(){this._gotoQ.dequeue()}.bind(this)),f)):(console.warn("Not yet Rendered"),this.settings.previousLocationCfi=a,!1)},EPUBJS.Book.prototype.gotoHref=function(a,b){var c,d,e,f,g,h=b||new RSVP.defer;return this.isRendered?this._moving||this._rendering?(this._gotoQ.enqueue("gotoHref",[a,h]),!1):(c=a.split("#"),d=c[0],e=c[1]||!1,f=d.search("://")==-1?d.replace(EPUBJS.core.uri(this.settings.contentsPath).path,""):d.replace(this.settings.contentsPath,""),g=this.spineIndexByURL[f],d||(g=this.currentChapter?this.currentChapter.spinePos:0),"number"==typeof g&&(this.currentChapter&&g==this.currentChapter.spinePos?(e?this.renderer.section(e):this.renderer.firstPage(),h.resolve(this.renderer.currentLocationCfi),h.promise.then(function(){this._gotoQ.dequeue()}.bind(this)),h.promise):this.displayChapter(g).then(function(){e&&this.renderer.section(e),h.resolve(this.renderer.currentLocationCfi)}.bind(this)))):(this.settings.goto=a,!1)},EPUBJS.Book.prototype.gotoPage=function(a){var b=this.pagination.cfiFromPage(a);return this.gotoCfi(b)},EPUBJS.Book.prototype.gotoPercentage=function(a){var b=this.pagination.pageFromPercentage(a);return this.gotoPage(b)},EPUBJS.Book.prototype.preloadNextChapter=function(){var a,b=this.spinePos+1;return!(b>=this.spine.length)&&(a=new EPUBJS.Chapter(this.spine[b]),void(a&&EPUBJS.core.request(a.absolute)))},EPUBJS.Book.prototype.storeOffline=function(){var a=this,b=EPUBJS.core.values(this.manifest);return this.store.put(b).then(function(){a.settings.stored=!0,a.trigger("book:stored")})},EPUBJS.Book.prototype.availableOffline=function(){return this.settings.stored>0},EPUBJS.Book.prototype.toStorage=function(){var a=this.settings.bookKey;this.store.isStored(a).then(function(b){return b===!0?(this.settings.stored=!0,!0):this.storeOffline().then(function(){this.store.token(a,!0)}.bind(this))}.bind(this))},EPUBJS.Book.prototype.fromStorage=function(a){[EPUBJS.replace.head,EPUBJS.replace.resources,EPUBJS.replace.svg];this.contained||this.settings.contained||(this.online&&this.opened.then(this.toStorage.bind(this)),this.store&&this.settings.fromStorage&&a===!1?(this.settings.fromStorage=!1,this.store.off("offline"),this.store=!1):this.settings.fromStorage||(this.store=new EPUBJS.Storage(this.settings.credentials),this.store.on("offline",function(a){a?(this.offline=!0,this.settings.fromStorage=!0,this.trigger("book:offline")):(this.offline=!1,this.settings.fromStorage=!1,this.trigger("book:online"))}.bind(this))))},EPUBJS.Book.prototype.setStyle=function(a,b,c){var d=["color","background","background-color"];return this.isRendered?(this.settings.styles[a]=b,this.renderer.setStyle(a,b,c),void(d.indexOf(a)===-1&&this.renderer.reformat())):this._q.enqueue("setStyle",arguments)},EPUBJS.Book.prototype.removeStyle=function(a){return this.isRendered?(this.renderer.removeStyle(a),this.renderer.reformat(),void delete this.settings.styles[a]):this._q.enqueue("removeStyle",arguments)},EPUBJS.Book.prototype.resetClasses=function(a){return this.isRendered?(a.constructor===String&&(a=[a]),this.settings.classes=a,this.renderer.setClasses(this.settings.classes),void this.renderer.reformat()):this._q.enqueue("setClasses",arguments)}, +EPUBJS.Book.prototype.addClass=function(a){return this.isRendered?(this.settings.classes.indexOf(a)==-1&&this.settings.classes.push(a),this.renderer.setClasses(this.settings.classes),void this.renderer.reformat()):this._q.enqueue("addClass",arguments)},EPUBJS.Book.prototype.removeClass=function(a){if(!this.isRendered)return this._q.enqueue("removeClass",arguments);var b=this.settings.classes.indexOf(a);b!=-1&&(delete this.settings.classes[b],this.renderer.setClasses(this.settings.classes),this.renderer.reformat())},EPUBJS.Book.prototype.addHeadTag=function(a,b){return this.isRendered?void(this.settings.headTags[a]=b):this._q.enqueue("addHeadTag",arguments)},EPUBJS.Book.prototype.useSpreads=function(a){console.warn("useSpreads is deprecated, use forceSingle or set a layoutOveride instead"),a===!1?this.forceSingle(!0):this.forceSingle(!1)},EPUBJS.Book.prototype.forceSingle=function(a){var b="undefined"==typeof a||a;this.renderer.forceSingle(b),this.settings.forceSingle=b,this.isRendered&&this.renderer.reformat()},EPUBJS.Book.prototype.setMinSpreadWidth=function(a){this.settings.minSpreadWidth=a,this.isRendered&&(this.renderer.setMinSpreadWidth(this.settings.minSpreadWidth),this.renderer.reformat())},EPUBJS.Book.prototype.setGap=function(a){this.settings.gap=a,this.isRendered&&(this.renderer.setGap(this.settings.gap),this.renderer.reformat())},EPUBJS.Book.prototype.chapter=function(a){var b,c,d=this.spineIndexByURL[a];return d&&(b=this.spine[d],c=new EPUBJS.Chapter(b,this.store,this.settings.withCredentials),c.load()),c},EPUBJS.Book.prototype.unload=function(){this.settings.restore&&localStorage&&this.saveContents(),this.unlistenToRenderer(this.renderer),this.trigger("book:unload")},EPUBJS.Book.prototype.destroy=function(){window.removeEventListener("beforeunload",this.unload),this.currentChapter&&this.currentChapter.unload(),this.unload(),this.renderer&&this.renderer.remove()},EPUBJS.Book.prototype._ready=function(){this.trigger("book:ready")},EPUBJS.Book.prototype._rendered=function(a){this.isRendered=!0,this.trigger("book:rendered"),this._q.flush()},EPUBJS.Book.prototype.applyStyles=function(a,b){a.applyStyles(this.settings.styles),b()},EPUBJS.Book.prototype.applyClasses=function(a,b){a.setClasses(this.settings.classes),b()},EPUBJS.Book.prototype.applyHeadTags=function(a,b){a.applyHeadTags(this.settings.headTags),b()},EPUBJS.Book.prototype._registerReplacements=function(a){a.registerHook("beforeChapterDisplay",this.applyStyles.bind(this,a),!0),a.registerHook("beforeChapterDisplay",this.applyHeadTags.bind(this,a),!0),a.registerHook("beforeChapterDisplay",this.applyClasses.bind(this,a),!0),a.registerHook("beforeChapterDisplay",EPUBJS.replace.hrefs.bind(this),!0)},EPUBJS.Book.prototype._needsAssetReplacement=function(){return!!this.settings.fromStorage||!!this.settings.contained},EPUBJS.Book.prototype.parseLayoutProperties=function(a){var b=this.settings.layoutOveride&&this.settings.layoutOveride.layout||a.layout||"reflowable",c=this.settings.layoutOveride&&this.settings.layoutOveride.spread||a.spread||"auto",d=this.settings.layoutOveride&&this.settings.layoutOveride.orientation||a.orientation||"auto";return{layout:b,spread:c,orientation:d}},RSVP.EventTarget.mixin(EPUBJS.Book.prototype),RSVP.on("error",function(a){console.error(a)}),EPUBJS.Chapter=function(a,b,c){this.href=a.href,this.absolute=a.url,this.id=a.id,this.spinePos=a.index,this.cfiBase=a.cfiBase,this.properties=a.properties,this.manifestProperties=a.manifestProperties,this.linear=a.linear,this.pages=1,this.store=b,this.credentials=c,this.epubcfi=new EPUBJS.EpubCFI,this.deferred=new RSVP.defer,this.loaded=this.deferred.promise,EPUBJS.Hooks.mixin(this),this.getHooks("beforeChapterRender"),this.caches={}},EPUBJS.Chapter.prototype.load=function(a,b){var c,d=a||this.store,e=b||this.credentials;return c=d?d.getXml(this.absolute):EPUBJS.core.request(this.absolute,!1,e),c.then(function(a){try{this.setDocument(a),this.deferred.resolve(this)}catch(a){this.deferred.reject({message:this.absolute+" -> "+a.message,stack:(new Error).stack})}}.bind(this)),c},EPUBJS.Chapter.prototype.render=function(a){return this.load().then(function(a){var b=a.querySelector("head"),c=a.createElement("base");return c.setAttribute("href",this.absolute),b.insertBefore(c,b.firstChild),this.contents=a,new RSVP.Promise(function(b,c){this.triggerHooks("beforeChapterRender",function(){b(a)}.bind(this),this)}.bind(this))}.bind(this)).then(function(a){var b=new XMLSerializer,c=b.serializeToString(a);return c}.bind(this))},EPUBJS.Chapter.prototype.url=function(a){var b,c=new RSVP.defer,d=a||this.store,e=this;return d?this.tempUrl?(b=this.tempUrl,c.resolve(b)):d.getUrl(this.absolute).then(function(a){e.tempUrl=a,c.resolve(a)}):(b=this.absolute,c.resolve(b)),c.promise},EPUBJS.Chapter.prototype.setPages=function(a){this.pages=a},EPUBJS.Chapter.prototype.getPages=function(a){return this.pages},EPUBJS.Chapter.prototype.getID=function(){return this.ID},EPUBJS.Chapter.prototype.unload=function(a){this.document=null,this.tempUrl&&a&&(a.revokeUrl(this.tempUrl),this.tempUrl=!1)},EPUBJS.Chapter.prototype.setDocument=function(a){this.document=a,this.contents=a.documentElement,!this.document.evaluate&&document.evaluate&&(this.document.evaluate=document.evaluate)},EPUBJS.Chapter.prototype.cfiFromRange=function(a){var b,c,d,e,f,g,h;if(this.document){if("undefined"!=typeof document.evaluate){if(c=EPUBJS.core.getElementXPath(a.startContainer),d=EPUBJS.core.getElementXPath(a.endContainer),e=this.document.evaluate(c,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,a.collapsed||(f=this.document.evaluate(d,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue),b=this.document.createRange(),e)try{b.setStart(e,a.startOffset),!a.collapsed&&f&&b.setEnd(f,a.endOffset)}catch(a){console.log("missed"),e=!1}e||(console.log("not found, try fuzzy match"),g=EPUBJS.core.cleanStringForXpath(a.startContainer.textContent),c="//text()[contains(.,"+g+")]",e=this.document.evaluate(c,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,e&&(b.setStart(e,a.startOffset),a.collapsed||(h=EPUBJS.core.cleanStringForXpath(a.endContainer.textContent),d="//text()[contains(.,"+h+")]",f=this.document.evaluate(d,this.document,EPUBJS.core.nsResolver,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue,f&&b.setEnd(f,a.endOffset))))}else b=a;return this.epubcfi.generateCfiFromRange(b,this.cfiBase)}},EPUBJS.Chapter.prototype.find=function(a){var b=this,c=[],d=a.toLowerCase(),e=function(a){for(var e,f,g,h=a.textContent.toLowerCase(),i=b.document.createRange(),j=-1,k=150;f!=-1;)f=h.indexOf(d,j+1),f!=-1&&(i=b.document.createRange(),i.setStart(a,f),i.setEnd(a,f+d.length),e=b.cfiFromRange(i),a.textContent.lengthb?1:a0?i:i+1:0===f?i:f===-1?EPUBJS.core.locationOf(a,b,c,i,h):EPUBJS.core.locationOf(a,b,c,g,i))},EPUBJS.core.indexOfSorted=function(a,b,c,d,e){var f,g=d||0,h=e||b.length,i=parseInt(g+(h-g)/2);return c||(c=function(a,b){return a>b?1:a0;){if(c=d.shift(),"text"===c.type?(e=g.childNodes[c.index],g=e.parentNode||g):g=c.id?f.getElementById(c.id):h[c.index],!g||"undefined"==typeof g)return console.error("No Element For",c,a.str),!1;h=Array.prototype.slice.call(g.children)}return g},EPUBJS.EpubCFI.prototype.compare=function(a,b){if("string"==typeof a&&(a=new EPUBJS.EpubCFI(a)),"string"==typeof b&&(b=new EPUBJS.EpubCFI(b)),a.spinePos>b.spinePos)return 1;if(a.spinePosb.steps[c].index)return 1;if(a.steps[c].indexb.characterOffset?1:a.characterOffset")},EPUBJS.EpubCFI.prototype.generateRangeFromCfi=function(a,b){var c,d,e,f,g,h,i=b||document,j=i.createRange();return"string"==typeof a&&(a=this.parse(a)),a.spinePos!==-1&&(c=a.steps[a.steps.length-1],"undefined"!=typeof document.evaluate?(d=this.generateXpathFromSteps(a.steps),e=i.evaluate(d,i,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue):(g=this.generateQueryFromSteps(a.steps),h=i.querySelector(g),h&&"text"==c.type&&(e=h.childNodes[c.index])),e?(e&&a.characterOffset>=0?(f=e.length,a.characterOffset-1&&this.hooks[a].splice(c,1)):Array.isArray(b)&&b.forEach(function(b){c=this.hooks[a].indexOf(b),c>-1&&this.hooks[a].splice(c,1)},this))},a.prototype.triggerHooks=function(a,b,c){function d(){f--,f<=0&&b&&b()}var e,f;return"undefined"!=typeof this.hooks[a]&&(e=this.hooks[a],f=e.length,0===f&&b&&b(),void e.forEach(function(a){a(d,c)}))},{register:function(a){if(void 0===EPUBJS.hooks[a]&&(EPUBJS.hooks[a]={}),"object"!=typeof EPUBJS.hooks[a])throw"Already registered: "+a;return EPUBJS.hooks[a]},mixin:function(b){for(var c in a.prototype)b[c]=a.prototype[c]}}}(),EPUBJS.Layout=EPUBJS.Layout||{},EPUBJS.Layout.isFixedLayout=function(a){var b=a.querySelector("[name=viewport]");if(!b||!b.hasAttribute("content"))return!1;var c=b.getAttribute("content");return/,/.test(c)},EPUBJS.Layout.Reflowable=function(){this.documentElement=null,this.spreadWidth=null},EPUBJS.Layout.Reflowable.prototype.format=function(a,b,c,d){var e=EPUBJS.core.prefixed("columnAxis"),f=EPUBJS.core.prefixed("columnGap"),g=EPUBJS.core.prefixed("columnWidth"),h=EPUBJS.core.prefixed("columnFill"),i=Math.floor(b),j=Math.floor(i/8),k=d>=0?d:j%2===0?j:j-1;return this.documentElement=a,this.spreadWidth=i+k,a.style.overflow="hidden",a.style.width=i+"px",a.style.height=c+"px",a.style[e]="horizontal",a.style[h]="auto",a.style[g]=i+"px",a.style[f]=k+"px",this.colWidth=i,this.gap=k,{pageWidth:this.spreadWidth,pageHeight:c}},EPUBJS.Layout.Reflowable.prototype.calculatePages=function(){var a,b;return this.documentElement.style.width="auto",a=this.documentElement.scrollWidth,b=Math.ceil(a/this.spreadWidth),{displayedPages:b,pageCount:b}},EPUBJS.Layout.ReflowableSpreads=function(){this.documentElement=null,this.spreadWidth=null},EPUBJS.Layout.ReflowableSpreads.prototype.format=function(a,b,c,d){var e=EPUBJS.core.prefixed("columnAxis"),f=EPUBJS.core.prefixed("columnGap"),g=EPUBJS.core.prefixed("columnWidth"),h=EPUBJS.core.prefixed("columnFill"),i=2,j=Math.floor(b),k=j%2===0?j:j-1,l=Math.floor(k/8),m=d>=0?d:l%2===0?l:l-1,n=Math.floor((k-m)/i);return this.documentElement=a,this.spreadWidth=(n+m)*i,a.style.overflow="hidden",a.style.width=k+"px",a.style.height=c+"px",a.style[e]="horizontal",a.style[h]="auto",a.style[f]=m+"px",a.style[g]=n+"px",this.colWidth=n,this.gap=m,{pageWidth:this.spreadWidth,pageHeight:c}},EPUBJS.Layout.ReflowableSpreads.prototype.calculatePages=function(){var a=this.documentElement.scrollWidth,b=Math.ceil(a/this.spreadWidth);return this.documentElement.style.width=b*this.spreadWidth-this.gap+"px",{displayedPages:b,pageCount:2*b}},EPUBJS.Layout.Fixed=function(){this.documentElement=null},EPUBJS.Layout.Fixed.prototype.format=function(a,b,c,d){var e,f,g,h,i=EPUBJS.core.prefixed("columnWidth"),j=EPUBJS.core.prefixed("transform"),k=EPUBJS.core.prefixed("transformOrigin"),l=a.querySelector("[name=viewport]");this.documentElement=a,l&&l.hasAttribute("content")&&(e=l.getAttribute("content"),f=e.split(","),f[0]&&(g=f[0].replace("width=","")),f[1]&&(h=f[1].replace("height=","")));var m=b/g,n=c/h,o=m=e?g.resolve():(d=c,b=new EPUBJS.Chapter(this.spine[d],this.store,this.credentials),this.process(b).then(function(){setTimeout(function(){f(g)},1)})),g.promise}.bind(this);return"number"==typeof a&&(this.break=a),b=f().then(function(){this.total=this._locations.length-1,this._currentCfi&&(this.currentLocation=this._currentCfi),c.resolve(this._locations)}.bind(this)),c.promise},EPUBJS.Locations.prototype.process=function(a){return a.load().then(function(b){var c,d,e,f=b,g=f.documentElement.querySelector("body"),h=0;this.sprint(g,function(b){var g,i=b.length,j=0;for(0===h&&(c=f.createRange(),c.setStart(b,0)),g=this.break-h,g>i&&(h+=i,j=i);j=i?h=i-(j-this.break):(c.setEnd(b,j),e=a.cfiFromRange(c),this._locations.push(e),h=0,j+=1,c=f.createRange(),c.setStart(b,j));d=b}.bind(this)),c&&(c.setEnd(d,d.length),e=a.cfiFromRange(c),this._locations.push(e),h=0)}.bind(this))},EPUBJS.Locations.prototype.sprint=function(a,b){for(var c,d=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,null,!1);c=d.nextNode();)b(c)},EPUBJS.Locations.prototype.locationFromCfi=function(a){return 0===this._locations.length?-1:EPUBJS.core.locationOf(a,this._locations,this.epubcfi.compare)},EPUBJS.Locations.prototype.percentageFromCfi=function(a){var b=this.locationFromCfi(a);return this.percentageFromLocation(b)},EPUBJS.Locations.prototype.percentageFromLocation=function(a){return a&&this.total?a/this.total:0},EPUBJS.Locations.prototype.cfiFromLocation=function(a){var b=-1;return"number"!=typeof a&&(a=parseInt(a)),a>=0&&a1?a/100:a,c=Math.ceil(this.total*b);return this.cfiFromLocation(c)},EPUBJS.Locations.prototype.load=function(a){return this._locations=JSON.parse(a),this.total=this._locations.length-1,this._locations},EPUBJS.Locations.prototype.save=function(a){return JSON.stringify(this._locations)},EPUBJS.Locations.prototype.getCurrent=function(a){return this._current},EPUBJS.Locations.prototype.setCurrent=function(a){var b;if("string"==typeof a)this._currentCfi=a;else{if("number"!=typeof a)return;this._current=a}0!==this._locations.length&&("string"==typeof a?(b=this.locationFromCfi(a),this._current=b):b=a,this.trigger("changed",{percentage:this.percentageFromLocation(b)}))},Object.defineProperty(EPUBJS.Locations.prototype,"currentLocation",{get:function(){return this._current},set:function(a){this.setCurrent(a)}}),RSVP.EventTarget.mixin(EPUBJS.Locations.prototype),EPUBJS.Pagination=function(a){this.pages=[],this.locations=[],this.epubcfi=new EPUBJS.EpubCFI,a&&a.length&&this.process(a)},EPUBJS.Pagination.prototype.process=function(a){a.forEach(function(a){this.pages.push(a.page),this.locations.push(a.cfi)},this),this.pageList=a,this.firstPage=parseInt(this.pages[0]),this.lastPage=parseInt(this.pages[this.pages.length-1]),this.totalPages=this.lastPage-this.firstPage},EPUBJS.Pagination.prototype.pageFromCfi=function(a){var b=-1;if(0===this.locations.length)return-1;var c=EPUBJS.core.indexOfSorted(a,this.locations,this.epubcfi.compare);return c!=-1?b=this.pages[c]:(c=EPUBJS.core.locationOf(a,this.locations,this.epubcfi.compare),b=c-1>=0?this.pages[c-1]:this.pages[0],void 0!==b||(b=-1)),b},EPUBJS.Pagination.prototype.cfiFromPage=function(a){var b=-1;"number"!=typeof a&&(a=parseInt(a));var c=this.pages.indexOf(a);return c!=-1&&(b=this.locations[c]),b},EPUBJS.Pagination.prototype.pageFromPercentage=function(a){var b=Math.round(this.totalPages*a);return b},EPUBJS.Pagination.prototype.percentageFromPage=function(a){var b=(a-this.firstPage)/this.totalPages;return Math.round(1e3*b)/1e3},EPUBJS.Pagination.prototype.percentageFromCfi=function(a){var b=this.pageFromCfi(a),c=this.percentageFromPage(b);return c},EPUBJS.Parser=function(a){this.baseUrl=a||""},EPUBJS.Parser.prototype.container=function(a){var b,c,d,e;return a?(b=a.querySelector("rootfile"))?(c=b.getAttribute("full-path"), +d=EPUBJS.core.uri(c).directory,e=a.xmlEncoding,{packagePath:c,basePath:d,encoding:e}):void console.error("No RootFile Found"):void console.error("Container File Not Found")},EPUBJS.Parser.prototype.identifier=function(a){var b;return a?(b=a.querySelector("metadata"),b?this.getElementText(b,"identifier"):void console.error("No Metadata Found")):void console.error("Package File Not Found")},EPUBJS.Parser.prototype.packageContents=function(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=this;return b&&(this.baseUrl=b),a?(c=a.querySelector("metadata"))?(d=a.querySelector("manifest"))?(e=a.querySelector("spine"))?(f=n.manifest(d),g=n.findNavPath(d),h=n.findTocPath(d,e),i=n.findCoverPath(a),j=Array.prototype.indexOf.call(e.parentNode.childNodes,e),k=n.spine(e,f),l={},k.forEach(function(a){l[a.href]=a.index}),m=n.metadata(c),m.direction=e.getAttribute("page-progression-direction"),{metadata:m,spine:k,manifest:f,navPath:g,tocPath:h,coverPath:i,spineNodeIndex:j,spineIndexByURL:l}):void console.error("No Spine Found"):void console.error("No Manifest Found"):void console.error("No Metadata Found"):void console.error("Package File Not Found")},EPUBJS.Parser.prototype.findNavPath=function(a){var b=a.querySelector("item[properties$='nav'], item[properties^='nav '], item[properties*=' nav ']");return!!b&&b.getAttribute("href")},EPUBJS.Parser.prototype.findTocPath=function(a,b){var c,d=a.querySelector("item[media-type='application/x-dtbncx+xml']");return d||(c=b.getAttribute("toc"),c&&(d=a.querySelector("item[id='"+c+"']"))),!!d&&d.getAttribute("href")},EPUBJS.Parser.prototype.metadata=function(a){var b={},c=this;return b.bookTitle=c.getElementText(a,"title"),b.creator=c.getElementText(a,"creator"),b.description=c.getElementText(a,"description"),b.pubdate=c.getElementText(a,"date"),b.publisher=c.getElementText(a,"publisher"),b.identifier=c.getElementText(a,"identifier"),b.language=c.getElementText(a,"language"),b.rights=c.getElementText(a,"rights"),b.modified_date=c.querySelectorText(a,"meta[property='dcterms:modified']"),b.layout=c.querySelectorText(a,"meta[property='rendition:layout']"),b.orientation=c.querySelectorText(a,"meta[property='rendition:orientation']"),b.spread=c.querySelectorText(a,"meta[property='rendition:spread']"),b},EPUBJS.Parser.prototype.findCoverPath=function(a){var b=a.querySelector("package").getAttribute("version");if("2.0"===b){var c=a.querySelector('meta[name="cover"]');if(c){var d=c.getAttribute("content"),e=a.querySelector("item[id='"+d+"']");return!!e&&e.getAttribute("href")}return!1}var f=a.querySelector("item[properties='cover-image']");return!!f&&f.getAttribute("href")},EPUBJS.Parser.prototype.getElementText=function(a,b){var c,d=a.getElementsByTagNameNS("http://purl.org/dc/elements/1.1/",b);return d&&0!==d.length?(c=d[0],c.childNodes.length?c.childNodes[0].nodeValue:""):""},EPUBJS.Parser.prototype.querySelectorText=function(a,b){var c=a.querySelector(b);return c&&c.childNodes.length?c.childNodes[0].nodeValue:""},EPUBJS.Parser.prototype.manifest=function(a){var b=this.baseUrl,c={},d=a.querySelectorAll("item"),e=Array.prototype.slice.call(d);return e.forEach(function(a){var d=a.getAttribute("id"),e=a.getAttribute("href")||"",f=a.getAttribute("media-type")||"",g=a.getAttribute("properties")||"";c[d]={href:e,url:b+e,type:f,properties:g}}),c},EPUBJS.Parser.prototype.spine=function(a,b){var c=a.getElementsByTagName("itemref"),d=Array.prototype.slice.call(c),e=Array.prototype.indexOf.call(a.parentNode.childNodes,a),f=new EPUBJS.EpubCFI;return d.map(function(a,c){var d=a.getAttribute("idref"),g=f.generateChapterComponent(e,c,d),h=a.getAttribute("properties")||"",i=h.length?h.split(" "):[],j=b[d].properties,k=j.length?j.split(" "):[];return{id:d,linear:a.getAttribute("linear")||"",properties:i,manifestProperties:k,href:b[d].href,url:b[d].url,index:c,cfiBase:g,cfi:"epubcfi("+g+")"}})},EPUBJS.Parser.prototype.querySelectorByType=function(a,b,c){var d=a.querySelector(b+'[*|type="'+c+'"]');if(null!==d&&0!==d.length)return d;d=a.querySelectorAll(b);for(var e=0;e1&&d[1],{cfi:f,href:h,packageUrl:e,page:j}):{href:h,page:j}},EPUBJS.Render.Iframe=function(){this.iframe=null,this.document=null,this.window=null,this.docEl=null,this.bodyEl=null,this.headEl=null,this.leftPos=0,this.pageWidth=0,this.id=EPUBJS.core.uuid()},EPUBJS.Render.Iframe.prototype.create=function(){return this.element=document.createElement("div"),this.element.id="epubjs-view:"+this.id,this.isMobile=navigator.userAgent.match(/(iPad|iPhone|iPod|Mobile|Android)/g),this.transform=EPUBJS.core.prefixed("transform"),this.element},EPUBJS.Render.Iframe.prototype.addIframe=function(){return this.iframe=document.createElement("iframe"),this.iframe.id="epubjs-iframe:"+this.id,this.iframe.scrolling=this.scrolling||"no",this.iframe.seamless="seamless",this.iframe.style.border="none",this.iframe.addEventListener("load",this.loaded.bind(this),!1),(this._width||this._height)&&(this.iframe.height=this._height,this.iframe.width=this._width),this.iframe},EPUBJS.Render.Iframe.prototype.load=function(a,b){var c=this,d=new RSVP.defer;return this.window&&this.unload(),this.iframe&&this.element.removeChild(this.iframe),this.iframe=this.addIframe(),this.element.appendChild(this.iframe),this.iframe.onload=function(a){c.document=c.iframe.contentDocument,c.docEl=c.document.documentElement,c.headEl=c.document.head,c.bodyEl=c.document.body||c.document.querySelector("body"),c.window=c.iframe.contentWindow,c.window.addEventListener("resize",c.resized.bind(c),!1),c.leftPos=0,c.setLeft(0),c.bodyEl&&(c.bodyEl.style.margin="0"),d.resolve(c.docEl)},this.iframe.onerror=function(a){d.reject({message:"Error Loading Contents: "+a,stack:(new Error).stack})},this.document=this.iframe.contentDocument,this.document?(this.iframe.contentDocument.open(),this.iframe.contentDocument.write(a),this.iframe.contentDocument.close(),d.promise):(d.reject(new Error("No Document Available")),d.promise)},EPUBJS.Render.Iframe.prototype.loaded=function(a){var b,c,d=this.iframe.contentWindow.location.href;this.document=this.iframe.contentDocument,this.docEl=this.document.documentElement,this.headEl=this.document.head,this.bodyEl=this.document.body||this.document.querySelector("body"),this.window=this.iframe.contentWindow,this.window.focus(),"about:blank"!=d&&(b=this.iframe.contentDocument.querySelector("base"),c=b.getAttribute("href"),this.trigger("render:loaded",c))},EPUBJS.Render.Iframe.prototype.resize=function(a,b){this.element&&(this.element.style.height=b,isNaN(a)||a%2===0||(a+=1),this.element.style.width=a,this.iframe&&(this.iframe.height=b,this.iframe.width=a),this._height=b,this._width=a,this.width=parseInt(window.getComputedStyle(document.getElementById("viewer")).width)||a,this.height=parseInt(window.getComputedStyle(document.getElementById("viewer")).height)||b)},EPUBJS.Render.Iframe.prototype.resized=function(a){this.width=parseInt(window.getComputedStyle(document.getElementById("viewer")).width),this.height=parseInt(window.getComputedStyle(document.getElementById("viewer")).height)},EPUBJS.Render.Iframe.prototype.totalWidth=function(){return this.docEl.scrollWidth},EPUBJS.Render.Iframe.prototype.totalHeight=function(){return this.docEl.scrollHeight},EPUBJS.Render.Iframe.prototype.setPageDimensions=function(a,b){this.pageWidth=a,this.pageHeight=b},EPUBJS.Render.Iframe.prototype.setDirection=function(a){this.direction=a,this.docEl&&"rtl"==this.docEl.dir&&(this.docEl.dir="rtl","pre-paginated"!==this.layout&&(this.docEl.style.position="static",this.docEl.style.right="auto"))},EPUBJS.Render.Iframe.prototype.setLeft=function(a){this.isMobile?this.docEl.style[this.transform]="translate("+-a+"px, 0)":this.document.defaultView.scrollTo(a,0)},EPUBJS.Render.Iframe.prototype.setLayout=function(a){this.layout=a},EPUBJS.Render.Iframe.prototype.setStyle=function(a,b,c){c&&(a=EPUBJS.core.prefixed(a)),this.headEl&&(this.headEl.style[a]=b)},EPUBJS.Render.Iframe.prototype.removeStyle=function(a){this.headEl&&(this.headEl.style[a]="")},EPUBJS.Render.Iframe.prototype.setClasses=function(a){this.bodyEl&&(this.bodyEl.className=a.join(" "))},EPUBJS.Render.Iframe.prototype.addHeadTag=function(a,b,c){var d=c||this.document,e=d.createElement(a),f=d.head;for(var g in b)e.setAttribute(g,b[g]);f&&f.insertBefore(e,f.firstChild)},EPUBJS.Render.Iframe.prototype.page=function(a){this.leftPos=this.pageWidth*(a-1),"rtl"===this.direction&&(this.leftPos=this.leftPos*-1),this.setLeft(this.leftPos)},EPUBJS.Render.Iframe.prototype.getPageNumberByElement=function(a){var b,c;if(a)return b=this.leftPos+a.getBoundingClientRect().left,c=Math.floor(b/this.pageWidth)+1},EPUBJS.Render.Iframe.prototype.getPageNumberByRect=function(a){var b,c;return b=this.leftPos+a.left,c=Math.floor(b/this.pageWidth)+1},EPUBJS.Render.Iframe.prototype.getBaseElement=function(){return this.bodyEl},EPUBJS.Render.Iframe.prototype.getDocumentElement=function(){return this.docEl},EPUBJS.Render.Iframe.prototype.isElementVisible=function(a){var b,c;return!!(a&&"function"==typeof a.getBoundingClientRect&&(b=a.getBoundingClientRect(),c=b.left,0!==b.width&&0!==b.height&&c>=0&&c=1&&a<=this.displayedPages&&(this.chapterPos=a,this.render.page(a),this.visibleRangeCfi=this.getVisibleRangeCfi(),this.currentLocationCfi=this.visibleRangeCfi.start,this.trigger("renderer:locationChanged",this.currentLocationCfi),this.trigger("renderer:visibleRangeChanged",this.visibleRangeCfi),!0):(console.warn("pageMap not set, queuing"),this._q.enqueue("page",arguments),!0)},EPUBJS.Renderer.prototype.nextPage=function(){return this.page(this.chapterPos+1)},EPUBJS.Renderer.prototype.prevPage=function(){return this.page(this.chapterPos-1)},EPUBJS.Renderer.prototype.pageByElement=function(a){var b;a&&(b=this.render.getPageNumberByElement(a),this.page(b))},EPUBJS.Renderer.prototype.lastPage=function(){return this._moving?this._q.enqueue("lastPage",arguments):void this.page(this.displayedPages)},EPUBJS.Renderer.prototype.firstPage=function(){return this._moving?this._q.enqueue("firstPage",arguments):void this.page(1)},EPUBJS.Renderer.prototype.section=function(a){var b=this.doc.getElementById(a);b&&this.pageByElement(b)},EPUBJS.Renderer.prototype.firstElementisTextNode=function(a){var b=a.childNodes,c=b.length;return!!(c&&b[0]&&3===b[0].nodeType&&b[0].textContent.trim().length)},EPUBJS.Renderer.prototype.isGoodNode=function(a){var b=["audio","canvas","embed","iframe","img","math","object","svg","video"];return b.indexOf(a.tagName.toLowerCase())!==-1||this.firstElementisTextNode(a)},EPUBJS.Renderer.prototype.walk=function(a,b,c){for(var d,e,f,g,h=a,i=[h],j=1e4,k=0;!d&&i.length;){if(a=i.shift(),this.containsPoint(a,b,c)&&this.isGoodNode(a)&&(d=a),!d&&a&&a.childElementCount>0){if(e=a.children,!e||!e.length)return d;f=e.length?e.length:0;for(var l=f-1;l>=0;l--)e[l]!=g&&i.unshift(e[l])}if(!d&&0===i.length&&h&&null!==h.parentNode&&(i.push(h.parentNode),g=h,h=h.parentNode),k++,k>j){console.error("ENDLESS LOOP");break}}return d},EPUBJS.Renderer.prototype.containsPoint=function(a,b,c){var d;return!!(a&&"function"==typeof a.getBoundingClientRect&&(d=a.getBoundingClientRect(),0!==d.width&&0!==d.height&&d.left>=b&&b<=d.left+d.width))},EPUBJS.Renderer.prototype.textSprint=function(a,b){var c,d,e=function(a){return/^\s*$/.test(a.data)?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT};try{for(c=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,{acceptNode:e},!1);d=c.nextNode();)b(d)}catch(f){for(c=document.createTreeWalker(a,NodeFilter.SHOW_TEXT,e,!1);d=c.nextNode();)b(d)}},EPUBJS.Renderer.prototype.sprint=function(a,b){for(var c,d=document.createTreeWalker(a,NodeFilter.SHOW_ELEMENT,null,!1);c=d.nextNode();)b(c)},EPUBJS.Renderer.prototype.mapPage=function(){var a,b,c,d,e,f,g,h,i=this,j=[],k=this.render.getBaseElement(),l=1,m=this.layout.colWidth+this.layout.gap,n=this.formated.pageWidth*(this.chapterPos-1),o=m*l-n,p=0,q=function(b){var c,e,f;if(b.nodeType==Node.TEXT_NODE){if(e=document.createRange(),e.selectNodeContents(b),c=e.getBoundingClientRect(),!c||0===c.width&&0===c.height)return;c.left>p&&(f=r(b)),c.right>p&&(f=r(b)),d=b,f&&(a=null)}},r=function(e){var f,g=i.splitTextNodeIntoWordsRanges(e);return g.forEach(function(e){var g=e.getBoundingClientRect();!g||0===g.width&&0===g.height||(g.left+g.width0&&(b&&(b.setEnd(a,e),c.push(b)),b=this.doc.createRange(),b.setStart(a,e+1));return b&&(b.setEnd(a,d.length),c.push(b)),c},EPUBJS.Renderer.prototype.rangePosition=function(a){var b,c;return c=a.getClientRects(),c.length?b=c[0]:null},EPUBJS.Renderer.prototype.getPageCfi=function(){var a=2*this.chapterPos-1;return this.pageMap[a].start},EPUBJS.Renderer.prototype.getRange=function(a,b,c){var d,e=this.doc.createRange();return c=!0,"undefined"==typeof document.caretPositionFromPoint||c?"undefined"==typeof document.caretRangeFromPoint||c?(this.visibileEl=this.findElementAfter(a,b),e.setStart(this.visibileEl,1)):e=this.doc.caretRangeFromPoint(a,b):(d=this.doc.caretPositionFromPoint(a,b),e.setStart(d.offsetNode,d.offset)),e},EPUBJS.Renderer.prototype.pagesInCurrentChapter=function(){var a;return this.pageMap?a=this.pageMap.length:(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.currentRenderedPage=function(){var a;return this.pageMap?a=this.spreads&&this.pageMap.length>1?2*this.chapterPos-1:this.chapterPos:(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.getRenderedPagesLeft=function(){var a,b,c;return this.pageMap?(b=this.pageMap.length,a=this.spreads?2*this.chapterPos-1:this.chapterPos,c=b-a):(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.getVisibleRangeCfi=function(){var a,b,c;return this.pageMap?(this.spreads?(a=2*this.chapterPos,b=this.pageMap[a-2],c=b,this.pageMap.length>1&&this.pageMap.length>a-1&&(c=this.pageMap[a-1])):(a=this.chapterPos,b=this.pageMap[a-1],c=b),b||(console.warn("page range miss:",a,this.pageMap),b=this.pageMap[this.pageMap.length-1],c=b),{start:b.start,end:c.end}):(console.warn("page map not loaded"),!1)},EPUBJS.Renderer.prototype.gotoCfi=function(a){var b,c,d;if(this._moving)return this._q.enqueue("gotoCfi",arguments);if(EPUBJS.core.isString(a)&&(a=this.epubcfi.parse(a)),"undefined"==typeof document.evaluate)c=this.epubcfi.addMarker(a,this.doc),c&&(b=this.render.getPageNumberByElement(c),this.epubcfi.removeMarker(c,this.doc),this.page(b));else if(d=this.epubcfi.generateRangeFromCfi(a,this.doc)){var e=d.getBoundingClientRect();b=e?this.render.getPageNumberByRect(e):1,this.page(b),this.currentLocationCfi=a.str}else this.page(1)},EPUBJS.Renderer.prototype.findFirstVisible=function(a){var b,c=a||this.render.getBaseElement();return b=this.walk(c,0,0),b?b:a},EPUBJS.Renderer.prototype.findElementAfter=function(a,b,c){var d,e=c||this.render.getBaseElement();return d=this.walk(e,a,b),d?d:e},EPUBJS.Renderer.prototype.resize=function(a,b,c){this.width=a,this.height=b,c!==!1&&this.render.resize(this.width,this.height),this.contents&&this.reformat(),this.trigger("renderer:resized",{width:this.width,height:this.height})},EPUBJS.Renderer.prototype.onResized=function(a){this.trigger("renderer:beforeResize");var b=parseInt(window.getComputedStyle(document.getElementById("viewer")).width),c=parseInt(window.getComputedStyle(document.getElementById("viewer")).height);this.resize(b,c,!1)},EPUBJS.Renderer.prototype.addEventListeners=function(){this.render.document&&this.listenedEvents.forEach(function(a){this.render.document.addEventListener(a,this.triggerEvent.bind(this),!1)},this)},EPUBJS.Renderer.prototype.removeEventListeners=function(){this.render.document&&this.listenedEvents.forEach(function(a){this.render.document.removeEventListener(a,this.triggerEvent,!1)},this)},EPUBJS.Renderer.prototype.triggerEvent=function(a){this.trigger("renderer:"+a.type,a)},EPUBJS.Renderer.prototype.addSelectionListeners=function(){this.render.document.addEventListener("selectionchange",this.onSelectionChange.bind(this),!1)},EPUBJS.Renderer.prototype.removeSelectionListeners=function(){this.render.document&&this.doc.removeEventListener("selectionchange",this.onSelectionChange,!1)},EPUBJS.Renderer.prototype.onSelectionChange=function(a){this.selectionEndTimeout&&clearTimeout(this.selectionEndTimeout),this.selectionEndTimeout=setTimeout(function(){this.selectedRange=this.render.window.getSelection(),this.trigger("renderer:selected",this.selectedRange)}.bind(this),500)},EPUBJS.Renderer.prototype.setMinSpreadWidth=function(a){this.minSpreadWidth=a,this.spreads=this.determineSpreads(a)},EPUBJS.Renderer.prototype.determineSpreads=function(a){return!(this.isForcedSingle||!a||this.width=d?h.resolve():(c=a[e].url,g=window.encodeURIComponent(c),EPUBJS.core.request(c,"binary").then(function(a){return localforage.setItem(g,a)}).then(function(a){e++,setTimeout(function(){f(h)},1)})),h.promise}.bind(this);return Array.isArray(a)||(a=[a]),f().then(function(){c.resolve()}.bind(this)),c.promise},EPUBJS.Storage.prototype.token=function(a,b){var c=window.encodeURIComponent(a);return localforage.setItem(c,b).then(function(a){return null!==a})},EPUBJS.Storage.prototype.isStored=function(a){var b=window.encodeURIComponent(a);return localforage.getItem(b).then(function(a){return null!==a})},EPUBJS.Storage.prototype.getText=function(a){var b=window.encodeURIComponent(a);return EPUBJS.core.request(a,"arraybuffer",this.withCredentials).then(function(a){return this.offline&&(this.offline=!1,this.trigger("offline",!1)),localforage.setItem(b,a),a}.bind(this)).then(function(b){var c=new RSVP.defer,d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;return f.addEventListener("loadend",function(){c.resolve(f.result)}),f.readAsText(e,d),c.promise}).catch(function(){var c=new RSVP.defer,d=localforage.getItem(b);return this.offline||(this.offline=!0,this.trigger("offline",!0)),d?(d.then(function(b){var d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;f.addEventListener("loadend",function(){c.resolve(f.result)}),f.readAsText(e,d)}),c.promise):(c.reject({message:"File not found in the storage: "+a,stack:(new Error).stack}),c.promise)}.bind(this))},EPUBJS.Storage.prototype.getUrl=function(a){var b=window.encodeURIComponent(a);return EPUBJS.core.request(a,"arraybuffer",this.withCredentials).then(function(c){ +return this.offline&&(this.offline=!1,this.trigger("offline",!1)),localforage.setItem(b,c),a}.bind(this)).catch(function(){var c,d,e=new RSVP.defer,f=window.URL||window.webkitURL||window.mozURL;return this.offline||(this.offline=!0,this.trigger("offline",!0)),b in this.urlCache?(e.resolve(this.urlCache[b]),e.promise):(c=localforage.getItem(b))?(c.then(function(c){var g=new Blob([c],{type:EPUBJS.core.getMimeType(a)});d=f.createObjectURL(g),e.resolve(d),this.urlCache[b]=d}.bind(this)),e.promise):(e.reject({message:"File not found in the storage: "+a,stack:(new Error).stack}),e.promise)}.bind(this))},EPUBJS.Storage.prototype.getXml=function(a){var b=window.encodeURIComponent(a);return EPUBJS.core.request(a,"arraybuffer",this.withCredentials).then(function(a){return this.offline&&(this.offline=!1,this.trigger("offline",!1)),localforage.setItem(b,a),a}.bind(this)).then(function(b){var c=new RSVP.defer,d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;return f.addEventListener("loadend",function(){var a=new DOMParser,b=a.parseFromString(f.result,"text/xml");c.resolve(b)}),f.readAsText(e,d),c.promise}).catch(function(){var c=new RSVP.defer,d=localforage.getItem(b);return this.offline||(this.offline=!0,this.trigger("offline",!0)),d?(d.then(function(b){var d=EPUBJS.core.getMimeType(a),e=new Blob([b],{type:d}),f=new FileReader;f.addEventListener("loadend",function(){var a=new DOMParser,b=a.parseFromString(f.result,"text/xml");c.resolve(b)}),f.readAsText(e,d)}),c.promise):(c.reject({message:"File not found in the storage: "+a,stack:(new Error).stack}),c.promise)}.bind(this))},EPUBJS.Storage.prototype.revokeUrl=function(a){var b=window.URL||window.webkitURL||window.mozURL,c=this.urlCache[a];c&&b.revokeObjectURL(c)},EPUBJS.Storage.prototype.failed=function(a){console.error(a)},RSVP.EventTarget.mixin(EPUBJS.Storage.prototype),EPUBJS.Unarchiver=function(a){this.checkRequirements(),this.urlCache={}},EPUBJS.Unarchiver.prototype.checkRequirements=function(a){"undefined"==typeof JSZip&&console.error("JSZip lib not loaded")},EPUBJS.Unarchiver.prototype.open=function(a,b){if(a instanceof ArrayBuffer){this.zip=new JSZip(a);var c=new RSVP.defer;return c.resolve(),c.promise}return EPUBJS.core.request(a,"binary").then(function(a){this.zip=new JSZip(a)}.bind(this))},EPUBJS.Unarchiver.prototype.getXml=function(a,b){var c=window.decodeURIComponent(a);return this.getText(c,b).then(function(b){var c=new DOMParser,d=EPUBJS.core.getMimeType(a);return c.parseFromString(b,d)})},EPUBJS.Unarchiver.prototype.getUrl=function(a,b){var c,d,e=this,f=new RSVP.defer,g=window.decodeURIComponent(a),h=this.zip.file(g),i=window.URL||window.webkitURL||window.mozURL;return h?a in this.urlCache?(f.resolve(this.urlCache[a]),f.promise):(d=new Blob([h.asUint8Array()],{type:EPUBJS.core.getMimeType(h.name)}),c=i.createObjectURL(d),f.resolve(c),e.urlCache[a]=c,f.promise):(f.reject({message:"File not found in the epub: "+a,stack:(new Error).stack}),f.promise)},EPUBJS.Unarchiver.prototype.getText=function(a,b){var c,d=new RSVP.defer,e=window.decodeURIComponent(a),f=this.zip.file(e);return f?(c=f.asText(),d.resolve(c),d.promise):(d.reject({message:"File not found in the epub: "+a,stack:(new Error).stack}),d.promise)},EPUBJS.Unarchiver.prototype.revokeUrl=function(a){var b=window.URL||window.webkitURL||window.mozURL,c=this.urlCache[a];c&&b.revokeObjectURL(c)},EPUBJS.Unarchiver.prototype.failed=function(a){console.error(a)},EPUBJS.Unarchiver.prototype.afterSaved=function(a){this.callback()},EPUBJS.Unarchiver.prototype.toStorage=function(a){function b(){f--,0===f&&e.afterSaved()}var c=0,d=20,e=this,f=a.length;a.forEach(function(a){setTimeout(function(a){e.saveEntryFileToStorage(a,b)},c,a),c+=d}),console.log("time",c)},function(){var a={application:{ecmascript:["es","ecma"],javascript:"js",ogg:"ogx",pdf:"pdf",postscript:["ps","ai","eps","epsi","epsf","eps2","eps3"],"rdf+xml":"rdf",smil:["smi","smil"],"xhtml+xml":["xhtml","xht"],xml:["xml","xsl","xsd","opf","ncx"],zip:"zip","x-httpd-eruby":"rhtml","x-latex":"latex","x-maker":["frm","maker","frame","fm","fb","book","fbdoc"],"x-object":"o","x-shockwave-flash":["swf","swfl"],"x-silverlight":"scr","epub+zip":"epub","font-tdpfr":"pfr","inkml+xml":["ink","inkml"],json:"json","jsonml+json":"jsonml","mathml+xml":"mathml","metalink+xml":"metalink",mp4:"mp4s","omdoc+xml":"omdoc",oxps:"oxps","vnd.amazon.ebook":"azw",widget:"wgt","x-dtbook+xml":"dtb","x-dtbresource+xml":"res","x-font-bdf":"bdf","x-font-ghostscript":"gsf","x-font-linux-psf":"psf","x-font-otf":"otf","x-font-pcf":"pcf","x-font-snf":"snf","x-font-ttf":["ttf","ttc"],"x-font-type1":["pfa","pfb","pfm","afm"],"x-font-woff":"woff","x-mobipocket-ebook":["prc","mobi"],"x-mspublisher":"pub","x-nzb":"nzb","x-tgif":"obj","xaml+xml":"xaml","xml-dtd":"dtd","xproc+xml":"xpl","xslt+xml":"xslt","internet-property-stream":"acx","x-compress":"z","x-compressed":"tgz","x-gzip":"gz"},audio:{flac:"flac",midi:["mid","midi","kar","rmi"],mpeg:["mpga","mpega","mp2","mp3","m4a","mp2a","m2a","m3a"],mpegurl:"m3u",ogg:["oga","ogg","spx"],"x-aiff":["aif","aiff","aifc"],"x-ms-wma":"wma","x-wav":"wav",adpcm:"adp",mp4:"mp4a",webm:"weba","x-aac":"aac","x-caf":"caf","x-matroska":"mka","x-pn-realaudio-plugin":"rmp",xm:"xm",mid:["mid","rmi"]},image:{gif:"gif",ief:"ief",jpeg:["jpeg","jpg","jpe"],pcx:"pcx",png:"png","svg+xml":["svg","svgz"],tiff:["tiff","tif"],"x-icon":"ico",bmp:"bmp",webp:"webp","x-pict":["pic","pct"],"x-tga":"tga","cis-cod":"cod"},message:{rfc822:["eml","mime","mht","mhtml","nws"]},text:{"cache-manifest":["manifest","appcache"],calendar:["ics","icz","ifb"],css:"css",csv:"csv",h323:"323",html:["html","htm","shtml","stm"],iuls:"uls",mathml:"mml",plain:["txt","text","brf","conf","def","list","log","in","bas"],richtext:"rtx","tab-separated-values":"tsv","x-bibtex":"bib","x-dsrc":"d","x-diff":["diff","patch"],"x-haskell":"hs","x-java":"java","x-literate-haskell":"lhs","x-moc":"moc","x-pascal":["p","pas"],"x-pcs-gcd":"gcd","x-perl":["pl","pm"],"x-python":"py","x-scala":"scala","x-setext":"etx","x-tcl":["tcl","tk"],"x-tex":["tex","ltx","sty","cls"],"x-vcard":"vcf",sgml:["sgml","sgm"],"x-c":["c","cc","cxx","cpp","h","hh","dic"],"x-fortran":["f","for","f77","f90"],"x-opml":"opml","x-nfo":"nfo","x-sfv":"sfv","x-uuencode":"uu",webviewhtml:"htt"},video:{mpeg:["mpeg","mpg","mpe","m1v","m2v","mp2","mpa","mpv2"],mp4:["mp4","mp4v","mpg4"],quicktime:["qt","mov"],ogg:"ogv","vnd.mpegurl":["mxu","m4u"],"x-flv":"flv","x-la-asf":["lsf","lsx"],"x-mng":"mng","x-ms-asf":["asf","asx","asr"],"x-ms-wm":"wm","x-ms-wmv":"wmv","x-ms-wmx":"wmx","x-ms-wvx":"wvx","x-msvideo":"avi","x-sgi-movie":"movie","x-matroska":["mpv","mkv","mk3d","mks"],"3gpp2":"3g2",h261:"h261",h263:"h263",h264:"h264",jpeg:"jpgv",jpm:["jpm","jpgm"],mj2:["mj2","mjp2"],"vnd.ms-playready.media.pyv":"pyv","vnd.uvvu.mp4":["uvu","uvvu"],"vnd.vivo":"viv",webm:"webm","x-f4v":"f4v","x-m4v":"m4v","x-ms-vob":"vob","x-smv":"smv"}},b=function(){var b,c,d,e,f={};for(b in a)if(a.hasOwnProperty(b))for(c in a[b])if(a[b].hasOwnProperty(c))if(d=a[b][c],"string"==typeof d)f[d]=b+"/"+c;else for(e=0;ef/2.5&&(p=f/2.5,pop_content.style.maxHeight=p+"px"),popRect.height+n>=f-25?(c.style.top=n-popRect.height+"px",c.classList.add("above")):c.classList.remove("above"),m-popRect.width<=0?(c.style.left=m+"px",c.classList.add("left")):c.classList.remove("left"),m+popRect.width/2>=j?(c.style.left=m-300+"px",popRect=c.getBoundingClientRect(),c.style.left=m-popRect.width+"px",popRect.height+n>=f-25?(c.style.top=n-popRect.height+"px",c.classList.add("above")):c.classList.remove("above"),c.classList.add("right")):c.classList.remove("right")}function d(){h[k].classList.add("on")}function g(){h[k].classList.remove("on")}function i(){setTimeout(function(){h[k].classList.remove("show")},100)}var j,k,l,m,n,o,p=a.getAttribute(e);p==f&&(j=a.getAttribute("href"),k=j.replace("#",""),l=b.render.document.getElementById(k),a.addEventListener("mouseover",c,!1),a.addEventListener("mouseout",i,!1))}),a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").mathml=function(a,b){if(b.currentChapter.manifestProperties.indexOf("mathml")!==-1){b.render.iframe.contentWindow.mathmlCallback=a;var c=document.createElement("script");c.type="text/x-mathjax-config",c.innerHTML=' MathJax.Hub.Register.StartupHook("End",function () { window.mathmlCallback(); }); MathJax.Hub.Config({jax: ["input/TeX","input/MathML","output/SVG"],extensions: ["tex2jax.js","mml2jax.js","MathEvents.js"],TeX: {extensions: ["noErrors.js","noUndefined.js","autoload-all.js"]},MathMenu: {showRenderer: false},menuSettings: {zoom: "Click"},messageStyle: "none"}); ',b.doc.body.appendChild(c),EPUBJS.core.addScript("http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML",null,b.doc.head)}else a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").smartimages=function(a,b){var c=b.contents.querySelectorAll("img"),d=Array.prototype.slice.call(c),e=b.height;return"reflowable"!=b.layoutSettings.layout?void a():(d.forEach(function(a){var c=function(){var c,d=a.getBoundingClientRect(),f=d.height,g=d.top,h=a.getAttribute("data-height"),i=h||f,j=Number(getComputedStyle(a,"").fontSize.match(/(\d*(\.\d*)?)px/)[1]),k=j?j/2:0;e=b.contents.clientHeight,g<0&&(g=0),i+g>=e?(ge&&(a.style.maxHeight=e+"px",a.style.width="auto",d=a.getBoundingClientRect(),i=d.height),a.style.display="block",a.style.WebkitColumnBreakBefore="always",a.style.breakBefore="column"),a.setAttribute("data-height",c)):(a.style.removeProperty("max-height"),a.style.removeProperty("margin-top"))},d=function(){b.off("renderer:resized",c),b.off("renderer:chapterUnload",this)};a.addEventListener("load",c,!1),b.on("renderer:resized",c),b.on("renderer:chapterUnload",d),c()}),void(a&&a()))},EPUBJS.Hooks.register("beforeChapterDisplay").transculsions=function(a,b){var c=b.contents.querySelectorAll("[transclusion]"),d=Array.prototype.slice.call(c);d.forEach(function(a){function c(){j=g,k=h,j>chapter.colWidth&&(d=chapter.colWidth/j,j=chapter.colWidth,k*=d),f.width=j,f.height=k}var d,e=a.getAttribute("ref"),f=document.createElement("iframe"),g=a.getAttribute("width"),h=a.getAttribute("height"),i=a.parentNode,j=g,k=h;c(),b.listenUntil("renderer:resized","renderer:chapterUnloaded",c),f.src=e,i.replaceChild(f,a)}),a&&a()}; \ No newline at end of file +EPUBJS.Hooks.register("beforeChapterDisplay").endnotes=function(a,b){var c=b.contents.querySelectorAll("a[href]"),d=Array.prototype.slice.call(c),e="epub:type",f="noteref",g=(parent.location.origin+parent.location.pathname,EPUBJS.basePath+EPUBJS.cssPath||folder),h={};EPUBJS.core.addCss(g+"popup.css",!1,b.render.document.head),d.forEach(function(a){function c(){var c,e,f=b.height,j=b.width,o=225;n||(c=k.cloneNode(!0),n=c.querySelector("p")),h[id]||(h[id]=document.createElement("div"),h[id].setAttribute("class","popup"),pop_content=document.createElement("div"),h[id].appendChild(pop_content),pop_content.appendChild(n),pop_content.setAttribute("class","pop_content"),b.render.document.body.appendChild(h[id]),h[id].addEventListener("mouseover",d,!1),h[id].addEventListener("mouseout",g,!1),b.on("renderer:pageChanged",i,this),b.on("renderer:pageChanged",g,this)),c=h[id],e=a.getBoundingClientRect(),l=e.left,m=e.top,c.classList.add("show"),popRect=c.getBoundingClientRect(),c.style.left=l-popRect.width/2+"px",c.style.top=m+"px",o>f/2.5&&(o=f/2.5,pop_content.style.maxHeight=o+"px"),popRect.height+m>=f-25?(c.style.top=m-popRect.height+"px",c.classList.add("above")):c.classList.remove("above"),l-popRect.width<=0?(c.style.left=l+"px",c.classList.add("left")):c.classList.remove("left"),l+popRect.width/2>=j?(c.style.left=l-300+"px",popRect=c.getBoundingClientRect(),c.style.left=l-popRect.width+"px",popRect.height+m>=f-25?(c.style.top=m-popRect.height+"px",c.classList.add("above")):c.classList.remove("above"),c.classList.add("right")):c.classList.remove("right")}function d(){h[id].classList.add("on")}function g(){h[id].classList.remove("on")}function i(){setTimeout(function(){h[id].classList.remove("show")},100)}var j,k,l,m,n,o=a.getAttribute(e);o==f&&(j=a.getAttribute("href"),id=j.replace("#",""),k=b.render.document.getElementById(id),a.addEventListener("mouseover",c,!1),a.addEventListener("mouseout",i,!1))}),a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").mathml=function(a,b){if(b.currentChapter.manifestProperties.indexOf("mathml")!==-1){b.render.iframe.contentWindow.mathmlCallback=a;var c=document.createElement("script");c.type="text/x-mathjax-config",c.innerHTML=' MathJax.Hub.Register.StartupHook("End",function () { window.mathmlCallback(); }); MathJax.Hub.Config({jax: ["input/TeX","input/MathML","output/SVG"],extensions: ["tex2jax.js","mml2jax.js","MathEvents.js"],TeX: {extensions: ["noErrors.js","noUndefined.js","autoload-all.js"]},MathMenu: {showRenderer: false},menuSettings: {zoom: "Click"},messageStyle: "none"}); ',b.doc.body.appendChild(c),EPUBJS.core.addScript("http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML",null,b.doc.head)}else a&&a()},EPUBJS.Hooks.register("beforeChapterDisplay").smartimages=function(a,b){var c=b.contents.querySelectorAll("img"),d=Array.prototype.slice.call(c),e=b.height;return"reflowable"!=b.layoutSettings.layout?void a():(d.forEach(function(a){var c=function(){var c,d=a.getBoundingClientRect(),f=d.height,g=d.top,h=a.getAttribute("data-height"),i=h||f,j=Number(getComputedStyle(a,"").fontSize.match(/(\d*(\.\d*)?)px/)[1]),k=j?j/2:0;e=b.contents.clientHeight,g<0&&(g=0),i+g>=e?(ge&&(a.style.maxHeight=e+"px",a.style.width="auto",d=a.getBoundingClientRect(),i=d.height),a.style.display="block",a.style.WebkitColumnBreakBefore="always",a.style.breakBefore="column"),a.setAttribute("data-height",c)):(a.style.removeProperty("max-height"),a.style.removeProperty("margin-top"))},d=function(){b.off("renderer:resized",c),b.off("renderer:chapterUnload",this)};a.addEventListener("load",c,!1),b.on("renderer:resized",c),b.on("renderer:chapterUnload",d),c()}),void(a&&a()))},EPUBJS.Hooks.register("beforeChapterDisplay").transculsions=function(a,b){var c=b.contents.querySelectorAll("[transclusion]"),d=Array.prototype.slice.call(c);d.forEach(function(a){function c(){j=g,k=h,j>chapter.colWidth&&(d=chapter.colWidth/j,j=chapter.colWidth,k*=d),f.width=j,f.height=k}var d,e=a.getAttribute("ref"),f=document.createElement("iframe"),g=a.getAttribute("width"),h=a.getAttribute("height"),i=a.parentNode,j=g,k=h;c(),b.listenUntil("renderer:resized","renderer:chapterUnloaded",c),f.src=e,i.replaceChild(f,a)}),a&&a()}; \ No newline at end of file diff --git a/files_reader/vendor/epubjs/reader.min.js b/files_reader/vendor/epubjs/reader.min.js index cc9de05..12934e6 100644 --- a/files_reader/vendor/epubjs/reader.min.js +++ b/files_reader/vendor/epubjs/reader.min.js @@ -1 +1 @@ -EPUBJS.reader={},EPUBJS.reader.plugins={},function(a,b){var c=(a.ePubReader||{},a.ePubReader=function(a,b){return new EPUBJS.Reader(a,b)});"function"==typeof define&&define.amd?define(function(){return Reader}):"undefined"!=typeof module&&module.exports&&(module.exports=c)}(window,jQuery),EPUBJS.Reader=function(a,b){var c,d,e,f=this,g=$("#viewer"),h=window.location.search;this.settings=EPUBJS.core.defaults(b||{},{bookPath:a,restore:!1,reload:!1,bookmarks:void 0,annotations:void 0,contained:void 0,bookKey:void 0,styles:void 0,sidebarReflow:!1,generatePagination:!1,history:!0}),h&&(e=h.slice(1).split("&"),e.forEach(function(a){var b=a.split("="),c=b[0],d=b[1]||"";f.settings[c]=decodeURIComponent(d)})),this.setBookKey(this.settings.bookPath),this.settings.restore&&this.isSaved()&&this.applySavedSettings(),this.settings.styles=this.settings.styles||{fontSize:"100%"},this.book=c=new EPUBJS.Book(this.settings),this.settings.previousLocationCfi&&c.gotoCfi(this.settings.previousLocationCfi),this.offline=!1,this.sidebarOpen=!1,this.settings.bookmarks||(this.settings.bookmarks=[]),this.settings.annotations||(this.settings.annotations=[]),this.settings.generatePagination&&c.generatePagination(g.width(),g.height()),c.renderTo("viewer"),f.ReaderController=EPUBJS.reader.ReaderController.call(f,c),f.SettingsController=EPUBJS.reader.SettingsController.call(f,c),f.ControlsController=EPUBJS.reader.ControlsController.call(f,c),f.SidebarController=EPUBJS.reader.SidebarController.call(f,c),f.BookmarksController=EPUBJS.reader.BookmarksController.call(f,c),f.NotesController=EPUBJS.reader.NotesController.call(f,c);for(d in EPUBJS.reader.plugins)EPUBJS.reader.plugins.hasOwnProperty(d)&&(f[d]=EPUBJS.reader.plugins[d].call(f,c));return c.ready.all.then(function(){f.ReaderController.hideLoader()}),c.getMetadata().then(function(a){f.MetaController=EPUBJS.reader.MetaController.call(f,a)}),c.getToc().then(function(a){f.TocController=EPUBJS.reader.TocController.call(f,a)}),window.addEventListener("beforeunload",this.unload.bind(this),!1),window.addEventListener("hashchange",this.hashChanged.bind(this),!1),document.addEventListener("keydown",this.adjustFontSize.bind(this),!1),c.on("renderer:keydown",this.adjustFontSize.bind(this)),c.on("renderer:keydown",f.ReaderController.arrowKeys.bind(this)),c.on("renderer:selected",this.selectedRange.bind(this)),this},EPUBJS.Reader.prototype.adjustFontSize=function(a){var b,c=2,d=187,e=189,f=48,g=a.ctrlKey||a.metaKey;this.settings.styles&&(this.settings.styles.fontSize||(this.settings.styles.fontSize="100%"),b=parseInt(this.settings.styles.fontSize.slice(0,-1)),g&&a.keyCode==d&&(a.preventDefault(),this.book.setStyle("fontSize",b+c+"%")),g&&a.keyCode==e&&(a.preventDefault(),this.book.setStyle("fontSize",b-c+"%")),g&&a.keyCode==f&&(a.preventDefault(),this.book.setStyle("fontSize","100%")))},EPUBJS.Reader.prototype.addBookmark=function(a){var b=this.isBookmarked(a);b>-1||(this.settings.bookmarks.push(a),this.trigger("reader:bookmarked",a))},EPUBJS.Reader.prototype.removeBookmark=function(a){var b=this.isBookmarked(a);b!==-1&&(this.settings.bookmarks.splice(b,1),this.trigger("reader:unbookmarked",b))},EPUBJS.Reader.prototype.isBookmarked=function(a){var b=this.settings.bookmarks;return b.indexOf(a)},EPUBJS.Reader.prototype.clearBookmarks=function(){this.settings.bookmarks=[]},EPUBJS.Reader.prototype.addNote=function(a){this.settings.annotations.push(a)},EPUBJS.Reader.prototype.removeNote=function(a){var b=this.settings.annotations.indexOf(a);b!==-1&&delete this.settings.annotations[b]},EPUBJS.Reader.prototype.clearNotes=function(){this.settings.annotations=[]},EPUBJS.Reader.prototype.setBookKey=function(a){return this.settings.bookKey||(this.settings.bookKey="epubjsreader:"+EPUBJS.VERSION+":"+window.location.host+":"+a),this.settings.bookKey},EPUBJS.Reader.prototype.isSaved=function(a){var b;return!!localStorage&&(b=localStorage.getItem(this.settings.bookKey),null!==b)},EPUBJS.Reader.prototype.removeSavedSettings=function(){return!!localStorage&&void localStorage.removeItem(this.settings.bookKey)},EPUBJS.Reader.prototype.applySavedSettings=function(){var a;if(!localStorage)return!1;try{a=JSON.parse(localStorage.getItem(this.settings.bookKey))}catch(a){return!1}return!!a&&(a.styles&&(this.settings.styles=EPUBJS.core.defaults(this.settings.styles||{},a.styles)),this.settings=EPUBJS.core.defaults(this.settings,a),!0)},EPUBJS.Reader.prototype.saveSettings=function(){return this.book&&(this.settings.previousLocationCfi=this.book.getCurrentLocationCfi()),!!localStorage&&void localStorage.setItem(this.settings.bookKey,JSON.stringify(this.settings))},EPUBJS.Reader.prototype.unload=function(){this.settings.restore&&localStorage&&this.saveSettings()},EPUBJS.Reader.prototype.hashChanged=function(){var a=window.location.hash.slice(1);this.book.goto(a)},EPUBJS.Reader.prototype.selectedRange=function(a){var b=new EPUBJS.EpubCFI,c=b.generateCfiFromRangeAnchor(a,this.book.renderer.currentChapter.cfiBase),d="#"+c;this.settings.history&&window.location.hash!=d&&(history.pushState({},"",d),this.currentLocationCfi=c)},RSVP.EventTarget.mixin(EPUBJS.Reader.prototype),EPUBJS.reader.BookmarksController=function(){var a=this.book,b=$("#bookmarksView"),c=b.find("#bookmarks"),d=document.createDocumentFragment(),e=function(){b.show()},f=function(){b.hide()},g=0,h=function(b){var c=document.createElement("li"),d=document.createElement("a");return c.id="bookmark-"+g,c.classList.add("list_item"),d.textContent=b,d.href=b,d.classList.add("bookmark_link"),d.addEventListener("click",function(b){var c=this.getAttribute("href");a.gotoCfi(c),b.preventDefault()},!1),c.appendChild(d),g++,c};return this.settings.bookmarks.forEach(function(a){var b=h(a);d.appendChild(b)}),c.append(d),this.on("reader:bookmarked",function(a){var b=h(a);c.append(b)}),this.on("reader:unbookmarked",function(a){var b=$("#bookmark-"+a);b.remove()}),{show:e,hide:f}},EPUBJS.reader.ControlsController=function(a){var b=this,c=($("#store"),$("#fullscreen")),d=($("#fullscreenicon"),$("#cancelfullscreenicon"),$("#slider")),e=($("#main"),$("#sidebar"),$("#setting")),f=$("#bookmark"),g=function(){b.offline=!1},h=function(){b.offline=!0},i=!1;return a.on("book:online",g),a.on("book:offline",h),d.on("click",function(){b.sidebarOpen?(b.SidebarController.hide(),d.addClass("icon-menu"),d.removeClass("icon-right")):(b.SidebarController.show(),d.addClass("icon-right"),d.removeClass("icon-menu"))}),"undefined"!=typeof screenfull&&(c.on("click",function(){screenfull.toggle($("#container")[0])}),screenfull.raw&&document.addEventListener(screenfull.raw.fullscreenchange,function(){i=screenfull.isFullscreen,i?c.addClass("icon-resize-small").removeClass("icon-resize-full"):c.addClass("icon-resize-full").removeClass("icon-resize-small")})),e.on("click",function(){b.SettingsController.show()}),f.on("click",function(){var a=b.book.getCurrentLocationCfi(),c=b.isBookmarked(a);c===-1?(b.addBookmark(a),f.addClass("icon-bookmark").removeClass("icon-bookmark-empty")):(b.removeBookmark(a),f.removeClass("icon-bookmark").addClass("icon-bookmark-empty"))}),a.on("renderer:locationChanged",function(a){var c="#"+a,d=b.isBookmarked(a);d===-1?f.removeClass("icon-bookmark").addClass("icon-bookmark-empty"):f.addClass("icon-bookmark").removeClass("icon-bookmark-empty"),b.currentLocationCfi=a,b.settings.history&&window.location.hash!=c&&history.pushState({},"",c)}),a.on("book:pageChanged",function(a){}),{}},EPUBJS.reader.MetaController=function(a){var b=a.bookTitle,c=a.creator,d=$("#book-title"),e=$("#chapter-title"),f=$("#title-seperator");document.title=b+" – "+c,d.html(b),e.html(c),f.show()},EPUBJS.reader.NotesController=function(){var a=this.book,b=this,c=$("#notesView"),d=$("#notes"),e=$("#note-text"),f=$("#note-anchor"),g=b.settings.annotations,h=a.renderer,i=[],j=new EPUBJS.EpubCFI,k=function(){c.show()},l=function(){c.hide()},m=function(c){var d,g,h,i,k,l=a.renderer.doc;if(l.caretPositionFromPoint?(d=l.caretPositionFromPoint(c.clientX,c.clientY),g=d.offsetNode,h=d.offset):l.caretRangeFromPoint&&(d=l.caretRangeFromPoint(c.clientX,c.clientY),g=d.startContainer,h=d.startOffset),3!==g.nodeType)for(var p=0;pm/2.5&&(o=m/2.5,pop_content.style.maxHeight=o+"px"),popRect.height+l>=m-25?(b.style.top=l-popRect.height+"px",b.classList.add("above")):b.classList.remove("above"),k-popRect.width<=0?(b.style.left=k+"px",b.classList.add("left")):b.classList.remove("left"),k+popRect.width/2>=n?(b.style.left=k-300+"px",popRect=b.getBoundingClientRect(),b.style.left=k-popRect.width+"px",popRect.height+l>=m-25?(b.style.top=l-popRect.height+"px",b.classList.add("above")):b.classList.remove("above"),b.classList.add("right")):b.classList.remove("right")},f=function(){i[d].classList.add("on")},g=function(){i[d].classList.remove("on")},j=function(){setTimeout(function(){i[d].classList.remove("show")},100)},l=function(){b.ReaderController.slideOut(),k()};a.addEventListener("mouseover",e,!1),a.addEventListener("mouseout",j,!1),a.addEventListener("click",l,!1)};return f.on("click",function(b){f.text("Cancel"),e.prop("disabled","true"),a.on("renderer:click",m)}),g.forEach(function(a){n(a)}),h.registerHook("beforeChapterDisplay",function(a,b){var c=b.currentChapter;g.forEach(function(a){var b=j.parse(a.anchor);if(b.spinePos===c.spinePos)try{o(a)}catch(b){console.log("anchoring failed",a.anchor)}}),a()},!0),{show:k,hide:l}},EPUBJS.reader.ReaderController=function(a){var b=$("#main"),c=$("#divider"),d=$("#loader"),e=$("#next"),f=$("#prev"),g=this,a=this.book,h=function(){var c=a.getCurrentLocationCfi();g.settings.sidebarReflow?(b.removeClass("single"),b.one("transitionend",function(){a.gotoCfi(c)})):b.removeClass("closed")},i=function(){var c=a.getCurrentLocationCfi();g.settings.sidebarReflow?(b.addClass("single"),b.one("transitionend",function(){a.gotoCfi(c)})):b.addClass("closed")},j=function(){d.show(),m()},k=function(){d.hide()},l=function(){c.addClass("show")},m=function(){c.removeClass("show")},n=!1,o=function(b){37==b.keyCode&&("rtl"===a.metadata.direction?a.nextPage():a.prevPage(),f.addClass("active"),n=!0,setTimeout(function(){n=!1,f.removeClass("active")},100),b.preventDefault()),39==b.keyCode&&("rtl"===a.metadata.direction?a.prevPage():a.nextPage(),e.addClass("active"),n=!0,setTimeout(function(){n=!1,e.removeClass("active")},100),b.preventDefault())};return document.addEventListener("keydown",o,!1),e.on("click",function(b){"rtl"===a.metadata.direction?a.prevPage():a.nextPage(),b.preventDefault()}),f.on("click",function(b){"rtl"===a.metadata.direction?a.nextPage():a.prevPage(),b.preventDefault()}),a.on("renderer:spreads",function(a){a?l():m()}),{slideOut:i,slideIn:h,showLoader:j,hideLoader:k,showDivider:l,hideDivider:m,arrowKeys:o}},EPUBJS.reader.SettingsController=function(){var a=(this.book,this),b=$("#settings-modal"),c=$(".overlay"),d=function(){b.addClass("md-show")},e=function(){b.removeClass("md-show")},f=$("#sidebarReflow");return f.on("click",function(){a.settings.sidebarReflow=!a.settings.sidebarReflow}),b.find(".closer").on("click",function(){e()}),c.on("click",function(){e()}),{show:d,hide:e}},EPUBJS.reader.SidebarController=function(a){var b=this,c=$("#sidebar"),d=$("#panels"),e="Toc",f=function(a){var c=a+"Controller";e!=a&&"undefined"!=typeof b[c]&&(b[e+"Controller"].hide(),b[c].show(),e=a,d.find(".active").removeClass("active"),d.find("#show-"+a).addClass("active"))},g=function(){return e},h=function(){b.sidebarOpen=!0,b.ReaderController.slideOut(),c.addClass("open")},i=function(){b.sidebarOpen=!1,b.ReaderController.slideIn(),c.removeClass("open")};return d.find(".show_view").on("click",function(a){var b=$(this).data("view");f(b),a.preventDefault()}),{show:h,hide:i,getActivePanel:g,changePanelTo:f}},EPUBJS.reader.TocController=function(a){var b=this.book,c=$("#tocView"),d=document.createDocumentFragment(),e=!1,f=function(a,b){var c=document.createElement("ul");return b||(b=1),a.forEach(function(a){var d=document.createElement("li"),e=document.createElement("a");toggle=document.createElement("a");var g;d.id="toc-"+a.id,d.classList.add("list_item"),e.textContent=a.label,e.href=a.href,e.classList.add("toc_link"),d.appendChild(e),a.subitems.length>0&&(b++,g=f(a.subitems,b),toggle.classList.add("toc_toggle"),d.insertBefore(toggle,e),d.appendChild(g)),c.appendChild(d)}),c},g=function(){c.show()},h=function(){c.hide()},i=function(a){var b=a.id,d=c.find("#toc-"+b),f=c.find(".currentChapter");c.find(".openChapter");d.length&&(d!=f&&d.has(e).length>0&&f.removeClass("currentChapter"),d.addClass("currentChapter"),d.parents("li").addClass("openChapter"))};b.on("renderer:chapterDisplayed",i);var j=f(a);return d.appendChild(j),c.append(d),c.find(".toc_link").on("click",function(a){var d=this.getAttribute("href");a.preventDefault(),b.goto(d),c.find(".currentChapter").addClass("openChapter").removeClass("currentChapter"),$(this).parent("li").addClass("currentChapter")}),c.find(".toc_toggle").on("click",function(a){var b=$(this).parent("li"),c=b.hasClass("openChapter");a.preventDefault(),c?b.removeClass("openChapter"):b.addClass("openChapter")}),{show:g,hide:h}}; \ No newline at end of file +EPUBJS.reader={},EPUBJS.reader.plugins={},function(a,b){var c=(a.ePubReader||{},a.ePubReader=function(a,b){return new EPUBJS.Reader(a,b)});"function"==typeof define&&define.amd?define(function(){return Reader}):"undefined"!=typeof module&&module.exports&&(module.exports=c)}(window,jQuery),EPUBJS.Reader=function(a,b){var c,d,e,f,g=this,h=$("#viewer"),i=window.location.search;this.settings=EPUBJS.core.defaults(b||{},{bookPath:a,contained:void 0,sidebarReflow:!1,generatePagination:!1,history:!0,keyboard:{32:"next",34:"next",39:"next",33:"previous",37:"previous",36:"first",35:"last",65:"annotate",66:"bookmark",82:"reflow",83:"toggleSidebar",84:"toolbar",68:"toggleDay",78:"toggleNight",70:"toggleFullscreen",27:"closeSidebar"},nightMode:!1,dayMode:!1,maxWidth:72,pageArrows:!1,annotations:{},customStyles:{},activeStyles:{},session:{}}),this.Annotation=function(a,b,c,d){this.id=d||EPUBJS.core.uuid(),this.type=a,this.date=Date.now(),this.edited=this.date,this.anchor=b,this.body=c},this.Style=function(a,b,c,d){this.name=a,this.selector=b,this.rules=c,this.extra=d||null},i&&(f=i.slice(1).split("&"),f.forEach(function(a){var b=a.split("="),c=b[0],d=b[1]||"";g.settings[c]=decodeURIComponent(d)})),this.restoreDefaults(this.settings.session.defaults),this.restorePreferences(this.settings.session.preferences),this.restoreAnnotations(this.settings.session.annotations),this.book=c=new EPUBJS.Book(this.settings),this.offline=!1,this.sidebarOpen=!1,this.viewerResized=!1,this.settings.generatePagination&&c.generatePagination(h.width(),h.height()),c.renderTo("viewer").then(function(a){this.renderer=d=a,g.StyleController=EPUBJS.reader.StylesController.call(g,d)}),g.ReaderController=EPUBJS.reader.ReaderController.call(g,c),g.SettingsController=EPUBJS.reader.SettingsController.call(g,c),g.ControlsController=EPUBJS.reader.ControlsController.call(g,c),g.SidebarController=EPUBJS.reader.SidebarController.call(g,c),g.NotesController=EPUBJS.reader.NotesController.call(g,c),g.BookmarksController=EPUBJS.reader.BookmarksController.call(g,c),g.SearchController=EPUBJS.reader.SearchController.call(g,c);for(e in EPUBJS.reader.plugins)EPUBJS.reader.plugins.hasOwnProperty(e)&&(g[e]=EPUBJS.reader.plugins[e].call(g,c));return c.ready.all.then(function(){g.ReaderController.hideLoader(),g.settings.session.cursor!=={}&&g.trigger("reader:gotobookmark",g.settings.session.cursor)}),c.getMetadata().then(function(a){g.MetaController=EPUBJS.reader.MetaController.call(g,a)}),c.getToc().then(function(a){g.TocController=EPUBJS.reader.TocController.call(g,a)}),window.addEventListener("beforeunload",this.unload.bind(this),!1),window.addEventListener("hashchange",this.hashChanged.bind(this),!1),c.on("renderer:keydown",g.ReaderController.keyCommands.bind(this)),c.on("renderer:selected",this.selectedRange.bind(this)),this},EPUBJS.Reader.prototype.cfiToId=function(a){return a.replace(/\W/g,"")},EPUBJS.Reader.prototype.getBookmark=function(a){var b=this.cfiToId(a);return this.settings.annotations[b]},EPUBJS.Reader.prototype.addBookmark=function(a){var b,c,d,e,f=this.cfiToId(a),g=new EPUBJS.EpubCFI,h="";return d=g.generateRangeFromCfi(a,this.book.renderer.doc),b=d.startOffset,h=d.startContainer.wholeText,c=/\S/.test(h)?b>0&&" "!=h.charAt(b-1)?EPUBJS.core.ellipsize(h.substr(h.indexOf(" ",b))):EPUBJS.core.ellipsize(h.substr(b)):a,this.isBookmarked(f)?(e=this.getAnnotation(f),this.updateAnnotation(e)):(e=new this.Annotation("bookmark",a,c,this.cfiToId(a)),this.addAnnotation(e)),this.trigger("reader:bookmarkcreated",e),e},EPUBJS.Reader.prototype.updateBookmark=function(a){this.updateAnnotation(a)},EPUBJS.Reader.prototype.removeBookmark=function(a){var b=this.cfiToId(a);this.removeAnnotation(b)},EPUBJS.Reader.prototype.isBookmarked=function(a){var b=this.cfiToId(a);return void 0!==this.settings.annotations[b]},EPUBJS.Reader.prototype.clearBookmarks=function(){this.clearAnnotations("bookmark")},EPUBJS.Reader.prototype.getAnnotation=function(a){return this.settings.annotations[a]},EPUBJS.Reader.prototype.addAnnotation=function(a){this.settings.annotations[a.id]=a,this.settings.session.setBookmark(a.id,a.anchor,a.type,a)},EPUBJS.Reader.prototype.removeAnnotation=function(a){if(void 0!==this.settings.annotations[a]){var b=this.settings.annotations[a].type;this.trigger("reader:"+b+"removed",a),this.settings.session.deleteBookmark(a),delete this.settings.annotations[a]}},EPUBJS.Reader.prototype.updateAnnotation=function(a){a.edited=Date.now(),this.settings.annotations[a.id]=a,this.settings.session.setBookmark(a.id,a.anchor,a.type,a)},EPUBJS.Reader.prototype.clearAnnotations=function(a){if(a)for(var b in this.settings.annotations)this.settings.annotations.hasOwnProperty(b)&&this.settings.annotations[b].type===a&&(this.trigger("reader:"+a+"removed",b),this.settings.session.deleteBookmark(b),delete this.settings.annotations[b])},EPUBJS.Reader.prototype.addStyleSheet=function(a,b){var c=a,d=b||document.head,e=document.createElement("style");return console,e.appendChild(document.createTextNode("")),e.setAttribute("id",c),d.appendChild(e),e.sheet},EPUBJS.Reader.prototype.getStyleSheet=function(a,b){if(void 0!==a){var c=b||document.head,d=$(c).find("style#"+a);if(d.length)return d[0]}},EPUBJS.Reader.prototype.addCSSRule=function(a,b,c,d){void 0===d&&(d=0),"insertRule"in a?a.insertRule(b+"{"+c+"}",d):"addRule"in a&&a.addRule(b,c,d)},EPUBJS.Reader.prototype.addStyle=function(a,b,c,d){void 0===this.settings.customStyles[a]&&(this.settings.customStyles[a]=new this.Style(a,b,c,d),this.settings.session.setDefault("customStyles",this.settings.customStyles))},EPUBJS.Reader.prototype.enableStyle=function(a){var b=this.getStyleSheet(a.name,document.head),c=this.getStyleSheet(a.name,renderer.doc.head);c&&$(c).remove(),b&&$(b).remove();var d="",e=this.addStyleSheet(a.name,renderer.doc.head),f=this.addStyleSheet(a.name,document.head);for(var g in a.rules)d+=g+":"+a.rules[g]+"!important;";this.addCSSRule(e,a.selector,d,0),this.addCSSRule(f,"*"===a.selector?"#main":a.selector,d,0),this.settings.activeStyles[a.name]=!0,this.settings.session.setDefault("activeStyles",this.settings.activeStyles)},EPUBJS.Reader.prototype.disableStyle=function(a){var b=this.getStyleSheet(a.name,document.head),c=this.getStyleSheet(a.name,renderer.doc.head);c&&$(c).remove(),b&&$(b).remove(),this.settings.activeStyles[a.name]&&(delete this.settings.activeStyles[a.name],this.settings.session.setDefault("activeStyles",this.settings.activeStyles))},EPUBJS.Reader.prototype.updateStyle=function(a){var b=this.getStyleSheet(a.name,renderer.doc.head);b&&this.enableStyle(a)},EPUBJS.Reader.prototype.deleteStyle=function(a){this.disableStyle(a),delete this.customStyles[a.name],this.settings.session.setDefault("customStyles",this.settings.customStyles)},EPUBJS.Reader.prototype.refreshStyles=function(a,b){var c=this.settings.activeStyles,d=this.settings.customStyles;for(var e in c)if(c.hasOwnProperty(e)){var f="",g=this.addStyleSheet(e,b.doc.head);for(var h in d[e].rules)d[e].rules.hasOwnProperty(h)&&(f+=h+":"+d[e].rules[h]+"!important;");this.addCSSRule(g,d[e].selector,f,0)}a&&a()},EPUBJS.Reader.prototype.restoreDefaults=function(a){for(var b=0;bm/2.5&&(o=m/2.5,pop_content.style.maxHeight=o+"px"),popRect.height+l>=m-25?(b.style.top=l-popRect.height+"px",b.classList.add("above")):b.classList.remove("above"),i-popRect.width<=0?(b.style.left=i+"px",b.classList.add("left")):b.classList.remove("left"),i+popRect.width/2>=n?(b.style.left=i-300+"px",popRect=b.getBoundingClientRect(),b.style.left=i-popRect.width+"px",popRect.height+l>=m-25?(b.style.top=l-popRect.height+"px",b.classList.add("above")):b.classList.remove("above"),b.classList.add("right")):b.classList.remove("right")},f=function(){k[d].classList.add("on")},g=function(){k[d].classList.remove("on")},h=function(){setTimeout(function(){k[d].classList.remove("show")},100)},i=function(){b.SidebarController.changePanelTo("Notes"),b.SidebarController.show()};a.addEventListener("mouseover",e,!1),a.addEventListener("mouseout",h,!1),a.addEventListener("click",i,!1)};f.on("click",function(b){f[0].classList.contains("icon-room")?(f.removeClass("icon-room"),f.addClass("icon-location_off"),e.prop("disabled",!0),h.hasClass("touch_nav")&&(h.removeClass("touch_nav"),g.removeClass("touch_nav"),h.addClass("restore_touch_nav")),a.on("renderer:click",o)):(e.prop("disabled",!1),f.removeClass("icon-location_off"),f.addClass("icon-room"),h.hasClass("restore_touch_nav")&&(h.removeClass("restore_touch_nav"),h.addClass("touch_nav"),g.addClass("touch_nav")),a.off("renderer:click",o))});for(var z in i)i.hasOwnProperty(z)&&"annotation"===i[z].type&&p(i[z]);return this.on("reader:annotationcreated",function(a){p(a)}),this.on("reader:annotationremoved",function(a){q(a)}),j.registerHook("beforeChapterDisplay",function(a,b){var c=b.currentChapter;for(var d in i)if(i.hasOwnProperty(d)&&"annotation"===i[d].type){var e=l.parse(i[d].anchor);if(e.spinePos===c.spinePos)try{w(i[d])}catch(a){console.log("anchoring failed",i[d].anchor)}}a()},!0),{show:m,hide:n,createItem:r}},EPUBJS.reader.ReaderController=function(a){var b=$("#main"),c=$("#divider"),d=$("#loader"),e=$("#next"),f=$("#prev"),g=$("#sidebarReflow"),h=$("#metainfo"),i=$("#use_custom_colors"),j=($("#container"),$("#fullscreen")),k=$("#bookmark"),l=$("#note"),m=this,a=this.book,n=m.settings,o=function(){if(m.viewerResized){var c=a.getCurrentLocationCfi();m.viewerResized=!1,b.removeClass("single"),b.one("transitionend",function(){a.gotoCfi(c)})}},p=function(){var c=a.getCurrentLocationCfi();m.viewerResized=!0,b.addClass("single"),b.one("transitionend",function(){a.gotoCfi(c)})},q=function(){d.show(),t()},r=function(){d.hide()},s=function(){c.addClass("show")},t=function(){c.removeClass("show")},u=!1,v=function(a){u=!0,a.addClass("active"),setTimeout(function(){u=!1,a.removeClass("active")},100)},w=function(a){var b=!1;switch(n.keyboard[a.keyCode]){case"previous":f.click();break;case"next":e.click();break;case"first":b=1;break;case"last":break;case"annotate":l.click();break;case"bookmark":k.click();break;case"reflow":g.click();break;case"toggleSidebar":m.SidebarController.toggle();break;case"closeSidebar":m.SidebarController.hide();break;case"toggleFullscreen":j.click();break;case"toggleNight":h.click();break;case"toggleDay":i.click();break;default:console.log("unsupported keyCode: "+a.keyCode)}};return document.addEventListener("keydown",w,!1),e.on("click",function(b){"rtl"===a.metadata.direction?a.prevPage():a.nextPage(),v(e),b.preventDefault()}),f.on("click",function(b){"rtl"===a.metadata.direction?a.nextPage():a.prevPage(),v(f),b.preventDefault()}),a.on("renderer:spreads",function(a){a?s():t()}),{slideOut:p,slideIn:o,showLoader:q,hideLoader:r,showDivider:s,hideDivider:t,keyCommands:w}},EPUBJS.reader.SearchController=function(){var a=this,b=this.book,c="",d=$("#searchBox"),e=$("#searchBox").next(),f=$("#clear_search"),g=$("#searchResults"),h=$("#searchView"),i=$("#viewer iframe").contents().find("body"),j=($("#sidebar"),function(){h.addClass("open"),d.focus()}),k=function(){o(),h.removeClass("open")},l=function(b){return void 0===b&&(b=d.val()),""==b?void m():(a.SidebarController.changePanelTo("Search"),g.empty(),g.append("
  • Searching...

  • "),a.SearchController.query=b,void p(b,g[0]))};d.on("keydown",function(a){e.css("visibility",this.value.length?"visible":"hidden"),13===a.keyCode&&l(),a.stopPropagation()}),e.on("click",function(){$(this).css("visibility","hidden"),d.val("")}),f.on("click",function(){o(),g.empty()});var m=function(){o(),g.empty(),"Search"==a.SidebarController.getActivePanel()&&a.SidebarController.changePanelTo("Toc")},n=function(b){$("#viewer iframe").contents().find("body").highlight(a.SearchController.query,{element:"span"})},o=function(a){i=$("#viewer iframe").contents().find("body"),i.unhighlight(),b.off("renderer:chapterDisplayed",n)},p=function(a,c){return new Promise(function(d,e){for(var f=[],h=0;h0&&(b++,g=f(a.subitems,b),toggle.classList.add("toc_toggle"),d.insertBefore(toggle,e),d.appendChild(g)),c.appendChild(d)}),c},g=function(){c.addClass("open")},h=function(){c.removeClass("open")},i=function(a){var b=a.id,d=c.find("#toc-"+b),f=c.find(".currentChapter");c.find(".openChapter");d.length&&(d!=f&&d.has(e).length>0&&f.removeClass("currentChapter"),d.addClass("currentChapter"),d.parents("li").addClass("openChapter"))};b.on("renderer:chapterDisplayed",i);var j=f(a);return d.appendChild(j),c.append(d),c.find(".toc_link").on("click",function(a){var d=this.getAttribute("href");a.preventDefault(),b.goto(d),c.find(".currentChapter").addClass("openChapter").removeClass("currentChapter"),$(this).parent("li").addClass("currentChapter")}),c.find(".toc_toggle").on("click",function(a){var b=$(this).parent("li"),c=b.hasClass("openChapter");a.preventDefault(),c?b.removeClass("openChapter"):b.addClass("openChapter")}),{show:g,hide:h}}; \ No newline at end of file diff --git a/files_reader/vendor/icomoon/fonts/icomoon.eot b/files_reader/vendor/icomoon/fonts/icomoon.eot new file mode 100644 index 0000000..b900d5c Binary files /dev/null and b/files_reader/vendor/icomoon/fonts/icomoon.eot differ diff --git a/files_reader/vendor/icomoon/fonts/icomoon.svg b/files_reader/vendor/icomoon/fonts/icomoon.svg new file mode 100644 index 0000000..381c46a --- /dev/null +++ b/files_reader/vendor/icomoon/fonts/icomoon.svg @@ -0,0 +1,121 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/files_reader/vendor/icomoon/fonts/icomoon.ttf b/files_reader/vendor/icomoon/fonts/icomoon.ttf new file mode 100644 index 0000000..d1633ef Binary files /dev/null and b/files_reader/vendor/icomoon/fonts/icomoon.ttf differ diff --git a/files_reader/vendor/icomoon/fonts/icomoon.woff b/files_reader/vendor/icomoon/fonts/icomoon.woff new file mode 100644 index 0000000..8cf9f65 Binary files /dev/null and b/files_reader/vendor/icomoon/fonts/icomoon.woff differ diff --git a/files_reader/vendor/icomoon/style.css b/files_reader/vendor/icomoon/style.css new file mode 100644 index 0000000..c2ad7e0 --- /dev/null +++ b/files_reader/vendor/icomoon/style.css @@ -0,0 +1,369 @@ +@font-face { + font-family: 'icomoon'; + src: url('fonts/icomoon.eot?jhgf4g'); + src: url('fonts/icomoon.eot?jhgf4g#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?jhgf4g') format('truetype'), + url('fonts/icomoon.woff?jhgf4g') format('woff'), + url('fonts/icomoon.svg?jhgf4g#icomoon') format('svg'); + font-weight: normal; + font-style: normal; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* slightly bigger icons */ + font-size: 1.2em; +} + +.icon-anchor:before { + content: "\e901"; +} +.icon-link:before { + content: "\e901"; +} +.icon-reference:before { + content: "\e901"; +} +.icon-arrow_back2:before { + content: "\e5c5"; +} +.icon-flag:before { + content: "\e153"; +} +.icon-attach_file:before { + content: "\e226"; +} +.icon-attachment:before { + content: "\e2bc"; +} +.icon-class:before { + content: "\e86f"; +} +.icon-turned_in2:before { + content: "\e8e8"; +} +.icon-turned_in_not2:before { + content: "\e8e9"; +} +.icon-brightness_1:before { + content: "\e3a6"; +} +.icon-brightness_2:before { + content: "\e3a7"; +} +.icon-brightness_3:before { + content: "\e3a8"; +} +.icon-brightness_4:before { + content: "\e3a9"; +} +.icon-brightness_low2:before { + content: "\e1ae"; +} +.icon-brightness_medium:before { + content: "\e1af"; +} +.icon-brightness_high:before { + content: "\e1ac"; +} +.icon-chat:before { + content: "\e0b7"; +} +.icon-chat_bubble:before { + content: "\e0ca"; +} +.icon-chat_bubble_outline:before { + content: "\e0cb"; +} +.icon-check:before { + content: "\e5ca"; +} +.icon-check_box:before { + content: "\e834"; +} +.icon-double_page_mode:before { + content: "\e86d"; +} +.icon-single_page_mode:before { + content: "\e86e"; +} +.icon-comment:before { + content: "\e0b9"; +} +.icon-delete:before { + content: "\e872"; +} +.icon-find_in_page:before { + content: "\e880"; +} +.icon-format_size:before { + content: "\e245"; +} +.icon-fullscreen2:before { + content: "\e5d3"; +} +.icon-fullscreen_exit2:before { + content: "\e5d4"; +} +.icon-star:before { + content: "\e838"; +} +.icon-link2:before { + content: "\e157"; +} +.icon-keyboard_arrow_left2:before { + content: "\e316"; +} +.icon-label_outline:before { + content: "\e893"; +} +.icon-local_library:before { + content: "\e54b"; +} +.icon-location_off:before { + content: "\e0c7"; +} +.icon-room:before { + content: "\e8b4"; +} +.icon-menu3:before { + content: "\e5d5"; +} +.icon-message:before { + content: "\e0c9"; +} +.icon-rate_review:before { + content: "\e560"; +} +.icon-save:before { + content: "\e161"; +} +.icon-search3:before { + content: "\e8b7"; +} +.icon-star_border:before { + content: "\e83a"; +} +.icon-star_half:before { + content: "\e839"; +} +.icon-stars:before { + content: "\e8d0"; +} +.icon-zoom_in2:before { + content: "\e902"; +} +.icon-zoom_out2:before { + content: "\e903"; +} +.icon-arrow_back:before { + content: "\e5c4"; +} +.icon-arrow_forward:before { + content: "\e5c8"; +} +.icon-icon-fit-window:before { + content: "\e85b"; +} +.icon-icon-fit-width:before { + content: "\e85c"; +} +.icon-turned_in:before { + content: "\e8e6"; +} +.icon-turned_in_not:before { + content: "\e8e7"; +} +.icon-brightness_low:before { + content: "\e1ad"; +} +.icon-build:before { + content: "\e869"; +} +.icon-cancel:before { + content: "\e5c9"; +} +.icon-navigate_before:before { + content: "\e408"; +} +.icon-navigate_next:before { + content: "\e409"; +} +.icon-close:before { + content: "\e5cd"; +} +.icon-cloud_download:before { + content: "\e2c0"; +} +.icon-content_copy:before { + content: "\e14d"; +} +.icon-mode_edit:before { + content: "\e254"; +} +.icon-crop_free:before { + content: "\e3c2"; +} +.icon-crop_portrait:before { + content: "\e3c5"; +} +.icon-dehaze:before { + content: "\e3c7"; +} +.icon-dns:before { + content: "\e875"; +} +.icon-exit_to_app:before { + content: "\e879"; +} +.icon-featured_play_list:before { + content: "\e06d"; +} +.icon-format_list_bulleted:before { + content: "\e241"; +} +.icon-format_list_numbered:before { + content: "\e242"; +} +.icon-format_textdirection_l_to_r:before { + content: "\e247"; +} +.icon-format_textdirection_r_to_l:before { + content: "\e248"; +} +.icon-fullscreen:before { + content: "\e5d0"; +} +.icon-fullscreen_exit:before { + content: "\e5d1"; +} +.icon-keyboard_arrow_left:before { + content: "\e314"; +} +.icon-keyboard_arrow_right:before { + content: "\e315"; +} +.icon-list:before { + content: "\e896"; +} +.icon-sync:before { + content: "\e627"; +} +.icon-menu:before { + content: "\e5d2"; +} +.icon-open_with:before { + content: "\e89f"; +} +.icon-pages:before { + content: "\e7f9"; +} +.icon-search:before { + content: "\e8b6"; +} +.icon-settings:before { + content: "\e8b8"; +} +.icon-settings_overscan:before { + content: "\e8c4"; +} +.icon-tune:before { + content: "\e429"; +} +.icon-unarchive:before { + content: "\e169"; +} +.icon-wb_sunny:before { + content: "\e430"; +} +.icon-zoom_in:before { + content: "\e8ff"; +} +.icon-zoom_out:before { + content: "\e900"; +} +.icon-zoom_out_map:before { + content: "\e56b"; +} +.icon-droplet:before { + content: "\e90b"; +} +.icon-box-remove:before { + content: "\e95f"; +} +.icon-download:before { + content: "\e960"; +} +.icon-spinner:before { + content: "\e97a"; +} +.icon-spinner3:before { + content: "\e97c"; +} +.icon-spinner6:before { + content: "\e97f"; +} +.icon-spinner9:before { + content: "\e982"; +} +.icon-spinner11:before { + content: "\e984"; +} +.icon-search2:before { + content: "\e986"; +} +.icon-enlarge:before { + content: "\e989"; +} +.icon-enlarge2:before { + content: "\e98b"; +} +.icon-equalizer:before { + content: "\e992"; +} +.icon-cog:before { + content: "\e994"; +} +.icon-menu2:before { + content: "\e9bd"; +} +.icon-contrast:before { + content: "\e9d5"; +} +.icon-cancel-circle:before { + content: "\ea0d"; +} +.icon-exit:before { + content: "\ea14"; +} +.icon-arrow-right:before { + content: "\ea34"; +} +.icon-arrow-left:before { + content: "\ea38"; +} +.icon-arrow-right2:before { + content: "\ea3c"; +} +.icon-arrow-left2:before { + content: "\ea40"; +} +.icon-circle-right:before { + content: "\ea42"; +} +.icon-circle-left:before { + content: "\ea44"; +} + diff --git a/files_reader/vendor/jquery/put-delete.js b/files_reader/vendor/jquery/put-delete.js new file mode 100644 index 0000000..61dc0e6 --- /dev/null +++ b/files_reader/vendor/jquery/put-delete.js @@ -0,0 +1,18 @@ +jQuery.each( [ "put", "delete" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + url: url, + type: method, + dataType: type, + data: data, + success: callback + }); + }; +}); + diff --git a/files_reader/vendor/pixastic/pixastic.effects.js b/files_reader/vendor/pixastic/pixastic.effects.js new file mode 100644 index 0000000..a114716 --- /dev/null +++ b/files_reader/vendor/pixastic/pixastic.effects.js @@ -0,0 +1,1276 @@ +Pixastic.Effects = (function() { + + function defaultOptions(options, defaults) { + var O = {}; + for (var opt in defaults) { + if (typeof options[opt] == "undefined") { + O[opt] = defaults[opt]; + } else { + O[opt] = options[opt]; + } + } + return O; + } + + function clamp(val, min, max) { + return Math.min(max, Math.max(min, val)); + } + + function convolve3x3(inData, outData, width, height, kernel, progress, alpha, invert, mono) { + var idx, r, g, b, a, + pyc, pyp, pyn, + pxc, pxp, pxn, + x, y, + + prog, lastProg = 0, + n = width * height * 4, + + k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], + k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], + k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], + + p00, p01, p02, + p10, p11, p12, + p20, p21, p22; + + for (y=0;y= width-1) pyn = pyc; + + for (x=0;x= width-1) pxn = pxc; + + p00 = pyp + pxp; p01 = pyp + pxc; p02 = pyp + pxn; + p10 = pyc + pxp; p11 = pyc + pxc; p12 = pyc + pxn; + p20 = pyn + pxp; p21 = pyn + pxc; p22 = pyn + pxn; + + r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + + inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + + inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22; + + g = inData[p00 + 1] * k00 + inData[p01 + 1] * k01 + inData[p02 + 1] * k02 + + inData[p10 + 1] * k10 + inData[p11 + 1] * k11 + inData[p12 + 1] * k12 + + inData[p20 + 1] * k20 + inData[p21 + 1] * k21 + inData[p22 + 1] * k22; + + b = inData[p00 + 2] * k00 + inData[p01 + 2] * k01 + inData[p02 + 2] * k02 + + inData[p10 + 2] * k10 + inData[p11 + 2] * k11 + inData[p12 + 2] * k12 + + inData[p20 + 2] * k20 + inData[p21 + 2] * k21 + inData[p22 + 2] * k22; + + if (alpha) { + a = inData[p00 + 3] * k00 + inData[p01 + 3] * k01 + inData[p02 + 3] * k02 + + inData[p10 + 3] * k10 + inData[p11 + 3] * k11 + inData[p12 + 3] * k12 + + inData[p20 + 3] * k20 + inData[p21 + 3] * k21 + inData[p22 + 3] * k22; + } else { + a = inData[idx+3]; + } + + if (mono) { + r = g = b = (r + g + b) / 3; + } + if (invert) { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + function convolve5x5(inData, outData, width, height, kernel, progress, alpha, invert, mono) { + var idx, r, g, b, a, + pyc, pyp, pyn, pypp, pynn, + pxc, pxp, pxn, pxpp, pxnn, + x, y, + + prog, lastProg = 0, + n = width * height * 4, + + k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], k03 = kernel[0][3], k04 = kernel[0][4], + k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], k13 = kernel[1][3], k14 = kernel[1][4], + k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], k23 = kernel[2][3], k24 = kernel[2][4], + k30 = kernel[3][0], k31 = kernel[3][1], k32 = kernel[3][2], k33 = kernel[3][3], k34 = kernel[3][4], + k40 = kernel[4][0], k41 = kernel[4][1], k42 = kernel[4][2], k43 = kernel[4][3], k44 = kernel[4][4], + + p00, p01, p02, p03, p04, + p10, p11, p12, p13, p14, + p20, p21, p22, p23, p24, + p30, p31, p32, p33, p34, + p40, p41, p42, p43, p44; + + for (y=0;y= width-1) pyn = pyc; + if (y < 2) pypp = pyp; + if (y >= width-2) pynn = pyn; + + for (x=0;x= width-1) pxn = pxc; + if (x < 2) pxpp = pxp; + if (x >= width-2) pxnn = pxn; + + p00 = pypp + pxpp; p01 = pypp + pxp; p02 = pypp + pxc; p03 = pypp + pxn; p04 = pypp + pxnn; + p10 = pyp + pxpp; p11 = pyp + pxp; p12 = pyp + pxc; p13 = pyp + pxn; p14 = pyp + pxnn; + p20 = pyc + pxpp; p21 = pyc + pxp; p22 = pyc + pxc; p23 = pyc + pxn; p24 = pyc + pxnn; + p30 = pyn + pxpp; p31 = pyn + pxp; p32 = pyn + pxc; p33 = pyn + pxn; p34 = pyn + pxnn; + p40 = pynn + pxpp; p41 = pynn + pxp; p42 = pynn + pxc; p43 = pynn + pxn; p44 = pynn + pxnn; + + r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + inData[p03] * k04 + inData[p02] * k04 + + inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + inData[p13] * k14 + inData[p12] * k14 + + inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22 + inData[p23] * k24 + inData[p22] * k24 + + inData[p30] * k30 + inData[p31] * k31 + inData[p32] * k32 + inData[p33] * k34 + inData[p32] * k34 + + inData[p40] * k40 + inData[p41] * k41 + inData[p42] * k42 + inData[p43] * k44 + inData[p42] * k44; + + g = inData[p00+1] * k00 + inData[p01+1] * k01 + inData[p02+1] * k02 + inData[p03+1] * k04 + inData[p02+1] * k04 + + inData[p10+1] * k10 + inData[p11+1] * k11 + inData[p12+1] * k12 + inData[p13+1] * k14 + inData[p12+1] * k14 + + inData[p20+1] * k20 + inData[p21+1] * k21 + inData[p22+1] * k22 + inData[p23+1] * k24 + inData[p22+1] * k24 + + inData[p30+1] * k30 + inData[p31+1] * k31 + inData[p32+1] * k32 + inData[p33+1] * k34 + inData[p32+1] * k34 + + inData[p40+1] * k40 + inData[p41+1] * k41 + inData[p42+1] * k42 + inData[p43+1] * k44 + inData[p42+1] * k44; + + b = inData[p00+2] * k00 + inData[p01+2] * k01 + inData[p02+2] * k02 + inData[p03+2] * k04 + inData[p02+2] * k04 + + inData[p10+2] * k10 + inData[p11+2] * k11 + inData[p12+2] * k12 + inData[p13+2] * k14 + inData[p12+2] * k14 + + inData[p20+2] * k20 + inData[p21+2] * k21 + inData[p22+2] * k22 + inData[p23+2] * k24 + inData[p22+2] * k24 + + inData[p30+2] * k30 + inData[p31+2] * k31 + inData[p32+2] * k32 + inData[p33+2] * k34 + inData[p32+2] * k34 + + inData[p40+2] * k40 + inData[p41+2] * k41 + inData[p42+2] * k42 + inData[p43+2] * k44 + inData[p42+2] * k44; + + if (alpha) { + a = inData[p00+3] * k00 + inData[p01+3] * k01 + inData[p02+3] * k02 + inData[p03+3] * k04 + inData[p02+3] * k04 + + inData[p10+3] * k10 + inData[p11+3] * k11 + inData[p12+3] * k12 + inData[p13+3] * k14 + inData[p12+3] * k14 + + inData[p20+3] * k20 + inData[p21+3] * k21 + inData[p22+3] * k22 + inData[p23+3] * k24 + inData[p22+3] * k24 + + inData[p30+3] * k30 + inData[p31+3] * k31 + inData[p32+3] * k32 + inData[p33+3] * k34 + inData[p32+3] * k34 + + inData[p40+3] * k40 + inData[p41+3] * k41 + inData[p42+3] * k42 + inData[p43+3] * k44 + inData[p42+3] * k44; + } else { + a = inData[idx+3]; + } + + if (mono) { + r = g = b = (r + g + b) / 3; + } + + if (invert) { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + function gaussian(inData, outData, width, height, kernelSize, progress) { + var r, g, b, a, idx, + n = width * height * 4, + x, y, i, j, + inx, iny, w, + tmpData = [], + maxKernelSize = 13, + kernelSize = clamp(kernelSize, 3, maxKernelSize), + k1 = -kernelSize / 2 + (kernelSize % 2 ? 0.5 : 0), + k2 = kernelSize + k1, + weights, + kernels = [[1]], + prog, lastProg = 0; + + + for (i=1;i= width) { + inx = width - 1; + } + + idx = (iny * width + inx) * 4; + + r += inData[idx] * w; + g += inData[idx + 1] * w; + b += inData[idx + 2] * w; + a += inData[idx + 3] * w; + + } + + idx = (y * width + x) * 4; + + tmpData[idx] = r; + tmpData[idx+1] = g; + tmpData[idx+2] = b; + tmpData[idx+3] = a; + + if (progress) { + prog = (idx/n*50 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + + lastProg = 0; + + // pass 2 + for (y=0;y= height) { + iny = height - 1; + } + + idx = (iny * width + inx) * 4; + + r += tmpData[idx] * w; + g += tmpData[idx + 1] * w; + b += tmpData[idx + 2] * w; + a += tmpData[idx + 3] * w; + } + + idx = (y * width + x) * 4; + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = 0.5 + (idx/n*50 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + + return { + + invert : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0; + + for (i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + sepia : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + r, g, b; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + solarize : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + r, g, b; + + for (i=0;i 127 ? 255 - r : r; + outData[i+1] = g > 127 ? 255 - g : g; + outData[i+2] = b > 127 ? 255 - b : b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + brightness : function(inData, outData, width, height, options, progress) { + options = defaultOptions(options, { + brightness : 0, + contrast : 0 + }); + + var contrast = clamp(options.contrast, -1, 1) / 2, + brightness = 1 + clamp(options.brightness, -1, 1), + prog, lastProg = 0, + r, g, b, + n = width * height * 4; + + var brightMul = brightness < 0 ? - brightness : brightness; + var brightAdd = brightness < 0 ? 0 : brightness; + + contrast = 0.5 * Math.tan((contrast + 1) * Math.PI/4); + contrastAdd = - (contrast - 0.5) * 255; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + desaturate : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + level; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + lighten : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + mul = 1 + clamp(options.amount, 0, 1); + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + noise : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + amount = clamp(options.amount, 0, 1), + strength = clamp(options.strength, 0, 1), + mono = !!options.mono, + random = Math.random, + rnd, r, g, b; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + flipv : function(inData, outData, width, height, options, progress) { + var inPix, outPix, + n = width * height * 4, + prog, lastProg = 0, + x, y; + for (y=0;y> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + fliph : function(inData, outData, width, height, options, progress) { + var inPix, outPix, + n = width * height * 4, + prog, lastProg = 0, + x, y; + for (y=0;y> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + blur : function(inData, outData, width, height, options, progress) { + gaussian(inData, outData, width, height, options.kernelSize, progress); + }, + + glow : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, r, g, b, + amount = options.amount, + tmpData = [], + gaussProgress, + prog, lastProg = 0; + + if (progress) { + gaussProgress = function(p) { + progress(p * 0.8); + return p; + } + } + + gaussian(inData, tmpData, width, height, options.kernelSize, gaussProgress); + + for (i=0;i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = 0.8 + (i/n*100 >> 0) / 100 * 0.2; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + convolve3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3(inData, outData, width, height, options.kernel, progress); + }, + + convolve5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5(inData, outData, width, height, options.kernel, progress); + }, + + // A 3x3 high-pass filter + sharpen3x3 : function(inData, outData, width, height, options, progress) { + var a = - clamp(options.strength, 0, 1); + convolve3x3( + inData, outData, width, height, + [[a, a, a], + [a, 1-a*8, a], + [a, a, a]], + progress + ); + }, + + // A 5x5 high-pass filter + sharpen5x5 : function(inData, outData, width, height, options, progress) { + var a = - clamp(options.strength, 0, 1); + convolve5x5( + inData, outData, width, height, + [[a, a, a, a, a], + [a, a, a, a, a], + [a, a, 1-a*24, a, a], + [a, a, a, a, a], + [a, a, a, a, a]], + progress + ); + }, + + // A 3x3 low-pass mean filter + soften3x3 : function(inData, outData, width, height, options, progress) { + var c = 1/9; + convolve3x3( + inData, outData, width, height, + [[c, c, c], + [c, c, c], + [c, c, c]], + progress + ); + }, + + // A 5x5 low-pass mean filter + soften5x5 : function(inData, outData, width, height, options, progress) { + var c = 1/25; + convolve5x5( + inData, outData, width, height, + [[c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c]], + progress + ); + }, + + // A 3x3 Cross edge-detect + crossedges : function(inData, outData, width, height, options, progress) { + var a = clamp(options.strength, 0, 1) * 5 + convolve3x3( + inData, outData, width, height, + [[ 0, -a, 0], + [-a, 0, a], + [ 0, a, 0]], + progress, + false, true + ); + }, + + // 3x3 directional emboss + emboss : function(inData, outData, width, height, options, progress) { + var amount = options.amount, + angle = options.angle, + x = Math.cos(-angle) * amount, + y = Math.sin(-angle) * amount, + n = width * height * 4, + + a00 = -x - y, + a10 = -x, + a20 = y - x, + a01 = -y, + a21 = y, + a02 = -y + x, + a12 = x, + a22 = y + x, + + tmpData = [], + + prog, lastProg = 0, + convProgress; + + if (progress) { + convProgress = function(p) { + progress(p * 0.5) + return p; + }; + } + + convolve3x3( + inData, tmpData, width, height, + [[a00, a01, a02], + [a10, 0, a12], + [a20, a21, a22]] + ); + + for (var i=0;i> 0) / 100 * 0.5; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + + // A 3x3 Sobel edge detect (similar to Photoshop's) + findedges : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, + data1 = [], + data2 = [], + gr1, gr2, gg1, gg2, gb1, gb2, + prog, lastProg = 0, + convProgress1, convProgress2; + + if (progress) { + convProgress1 = function(p) { + progress(p * 0.4); + return p; + }; + convProgress2 = function(p) { + progress(0.4 + p * 0.4); + return p; + }; + } + + convolve3x3(inData, data1, width, height, + [[-1, 0, 1], + [-2, 0, 2], + [-1, 0, 1]] + ); + convolve3x3(inData, data2, width, height, + [[-1, -2, -1], + [ 0, 0, 0], + [ 1, 2, 1]] + ); + + for (i=0;i> 0) / 100 * 0.2; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + // A 3x3 edge enhance + edgeenhance3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3( + inData, outData, width, height, + [[-1/9, -1/9, -1/9], + [-1/9, 17/9, -1/9], + [-1/9, -1/9, -1/9]], + progress + ); + }, + + // A 5x5 edge enhance + edgeenhance5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5( + inData, outData, width, height, + [[-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, 49/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25]], + progress + ); + }, + + // A 3x3 Laplacian edge-detect + laplace3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3( + inData, outData, width, height, + [[-1, -1, -1], + [-1, 8, -1], + [-1, -1, -1]], + progress, + false, true, true + ); + }, + + // A 5x5 Laplacian edge-detect + laplace5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5( + inData, outData, width, height, + [[-1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1], + [-1, -1, 24, -1, -1], + [-1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1]], + progress, + false, true, true + ); + }, + + coloradjust : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + r, g, b, + prog, lastProg = 0, + ar = clamp(options.r, -1, 1) * 255, + ag = clamp(options.g, -1, 1) * 255, + ab = clamp(options.b, -1, 1) * 255; + + for (var i=0;i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + colorfilter : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, r, g, b, + luminosity = !!options.luminosity, + prog, lastProg = 0, + min, max, h, l, h1, chroma, tmp, m, + ar = clamp(options.r, 0, 1), + ag = clamp(options.g, 0, 1), + ab = clamp(options.b, 0, 1); + + for (i=0;i max) max = g; + if (b > max) max = b; + if (g < min) min = g; + if (b < min) min = b; + chroma = (max - min); + + if (r == max) { + h = ((g - b) / chroma) % 6; + } else if (g == max) { + h = ((b - r) / chroma) + 2; + } else { + h = ((r - g) / chroma) + 4; + } + + h1 = h >> 0; + tmp = chroma * (h - h1); + r = g = b = l - (r * 0.3 + g * 0.59 + b * 0.11); + + if (h1 == 0) { + r += chroma; + g += tmp; + } else if (h1 == 1) { + r += chroma - tmp; + g += chroma; + } else if (h1 == 2) { + g += chroma; + b += tmp; + } else if (h1 == 3) { + g += chroma - tmp; + b += chroma; + } else if (h1 == 4) { + r += tmp; + b += chroma; + } else if (h1 == 5) { + r += chroma; + b += chroma - tmp; + } + } + + outData[i] = r * 255; + outData[i+1] = g * 255; + outData[i+2] = b * 255; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + hsl : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + hue = clamp(options.hue, -1, 1), + saturation = clamp(options.saturation, -1, 1), + lightness = clamp(options.lightness, -1, 1), + satMul = 1 + saturation * (saturation < 0 ? 1 : 2), + lightMul = lightness < 0 ? 1 + lightness : 1 - lightness, + lightAdd = lightness < 0 ? 0 : lightness * 255, + vs, ms, vm, h, s, l, v, m, vmh, sextant, + prog, lastProg = 0; + + hue = (hue * 6) % 6; + + for (var i=0;i vs) vs = g; + if (b > vs) vs = b; + ms = r; + if (g < ms) ms = g; + if (b < ms) ms = b; + vm = (vs-ms); + l = (ms+vs)/510; + + if (l > 0 && vm > 0) { + if (l <= 0.5) { + s = vm / (vs+ms) * satMul; + if (s > 1) s = 1; + v = (l * (1+s)); + } else { + s = vm / (510-vs-ms) * satMul; + if (s > 1) s = 1; + v = (l+s - l*s); + } + if (r == vs) { + if (g == ms) { + h = 5 + ((vs-b)/vm) + hue; + } else { + h = 1 - ((vs-g)/vm) + hue; + } + } else if (g == vs) { + if (b == ms) { + h = 1 + ((vs-r)/vm) + hue; + } else { + h = 3 - ((vs-b)/vm) + hue; + } + } else { + if (r == ms) { + h = 3 + ((vs-g)/vm) + hue; + } else { + h = 5 - ((vs-r)/vm) + hue; + } + } + if (h < 0) h += 6; + if (h >= 6) h -= 6; + m = (l + l - v); + sextant = h >> 0; + vmh = (v - m) * (h - sextant); + if (sextant == 0) { + r = v; + g = m + vmh; + b = m; + } else if (sextant == 1) { + r = v - vmh; + g = v; + b = m; + } else if (sextant == 2) { + r = m; + g = v; + b = m + vmh; + } else if (sextant == 3) { + r = m; + g = v - vmh; + b = v; + } else if (sextant == 4) { + r = m + vmh; + g = m; + b = v; + } else if (sextant == 5) { + r = v; + g = m; + b = v - vmh; + } + + r *= 255; + g *= 255; + b *= 255; + } + } + + r = r * lightMul + lightAdd; + g = g * lightMul + lightAdd; + b = b * lightMul + lightAdd; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + posterize : function(inData, outData, width, height, options, progress) { + var numLevels = clamp(options.levels, 2, 256), + numAreas = 256 / numLevels, + numValues = 256 / (numLevels-1), + r, g, b, + n = width * height * 4, + prog, lastProg = 0; + + for (i=0;i>0); + outData[i+1] = numValues * ((inData[i+1] / numAreas)>>0); + outData[i+2] = numValues * ((inData[i+2] / numAreas)>>0); + + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + + }, + + removenoise : function(inData, outData, width, height, options, progress) { + var r, g, b, c, y, x, idx, + pyc, pyp, pyn, + pxc, pxp, pxn, + minR, minG, minB, maxR, maxG, maxB, + n, prog, lastProg = 0; + + n = width * height * 4; + + for (y=0;y= width-1) pyn = pyc; + + for (x=0;x= width-1) pxn = pxc; + + minR = maxR = inData[pyc + pxp]; + c = inData[pyc + pxn]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + c = inData[pyp + pxc]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + c = inData[pyn + pxc]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + + minG = inData[pyc + pxp + 1]; + c = inData[pyc + pxn + 1]; + if (c < minG) minG = c; + c = inData[pyp + pxc + 1]; + if (c < minG) minG = c; + c = inData[pyn + pxc + 1]; + if (c < minG) minG = c; + + minB = inData[pyc + pxp + 2]; + c = inData[pyc + pxn + 2]; + if (c < minB) minB = c; + c = inData[pyp + pxc + 2]; + if (c < minB) minB = c; + c = inData[pyn + pxc + 2]; + if (c < minB) minB = c; + + r = inData[idx] + g = inData[idx + 1] + b = inData[idx + 2] + + if (r < minR) r = minR; + if (r > maxR) r = maxR; + if (g < minG) g = minG; + if (g > maxG) g = maxG; + if (b < minB) b = minB; + if (b > maxB) b = maxB; + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = inData[idx+3]; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + mosaic : function(inData, outData, width, height, options, progress) { + + var blockSize = clamp(options.blockSize, 1, Math.max(width, height)), + yBlocks = Math.ceil(height / blockSize), + xBlocks = Math.ceil(width / blockSize), + y0, y1, x0, x1, idx, pidx, + n = yBlocks * xBlocks, + prog, lastProg = 0; + + for (i=0, y0=0, bidx=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + y0 = y1; + } + }, + + equalize : function(inData, outData, width, height, options, progress) { + var n = width * height, p, i, level, ratio, + prog, lastProg; + var round = Math.round; + // build histogram + var pdf = new Array(256); + for (i=0;i<256;i++) { + pdf[i] = 0; + } + + for (i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }; + +})(); diff --git a/files_reader/vendor/pixastic/pixastic.js b/files_reader/vendor/pixastic/pixastic.js new file mode 100644 index 0000000..c9fb188 --- /dev/null +++ b/files_reader/vendor/pixastic/pixastic.js @@ -0,0 +1,286 @@ +/*! + * Pixastic - JavaScript Image Processing + * http://pixastic.com/ + * Copyright 2012, Jacob Seidelin + * + * Dual licensed under the MPL 1.1 or GPLv3 licenses. + * http://pixastic.com/license-mpl.txt + * http://pixastic.com/license-gpl-3.0.txt + * + */ + + var Pixastic = (function() { + + var worker; + + function createImageData(ctx, width, height) { + if (ctx.createImageData) { + return ctx.createImageData(width, height); + } else { + return ctx.getImageData(0, 0, width, height); + } + } + + function Pixastic(ctx, workerControlPath) { + + var P = {}, + width = ctx.canvas.width, + height = ctx.canvas.height, + queue = [], + workerControlPath = workerControlPath || ""; + + if (!worker) { + if (typeof window.Worker != "undefined") { + try { + worker = new window.Worker(workerControlPath + "pixastic.worker.control.js"); + } catch(e) { + if (location.protocol == "file:") { + Pixastic.log("Could not create native worker, running from file://") + } else { + Pixastic.log("Could not create native worker.") + } + } + } + if (!worker) { + worker = new Pixastic.Worker(); + } + } + + for (var e in Pixastic.Effects) { + if (Pixastic.Effects.hasOwnProperty(e)) { + (function(e) { + P[e] = function(options) { + queue.push({ + effect : e, + options : options + }); + return P; + } + + P.done = function(callback, progress) { + var inData, outData; + + try { + inData = ctx.getImageData(0, 0, width, height); + } catch(e) { + if (location.protocol == "file:") { + throw new Error("Could not access image data, running from file://"); + } else { + throw new Error("Could not access image data, is canvas tainted by cross-origin data?"); + } + } + + outData = createImageData(ctx, width, height); + + worker.postMessage({ + queue : queue, + inData : inData, + outData : outData, + width : width, + height : height + }); + + worker.onmessage = function(message) { + var d = message.data; + switch (d.event) { + case "done" : + ctx.putImageData(d.data, 0, 0); + if (callback) { + callback(); + } + if (progress) { + progress(1); + } + break; + case "progress" : + if (progress) { + progress(d.data); + } + break; + case "error" : + break; + } + } + + if (progress) { + progress(0); + } + } + })(e); + } + } + return P; + } + + + Pixastic.Worker = function() { + var me = this; + function processMessage(data) { + var queue = data.queue, + inData = data.inData, + outData = data.outData, + width = data.width, + height = data.height, + tmpData; + + for (var i=0;i 0) { + tmpData = inData; + inData = outData; + outData = tmpData; + } + + if (typeof importScripts == "function") { + progressCallback = function(p) { + me.onmessage({ + data : { + event : "progress", + data : (i + p) / queue.length + } + }); + return p; + } + } + + Pixastic.Effects[e](inData.data, outData.data, width, height, options, progressCallback); + + me.onmessage({ + data : { + event : "progress", + data : (i+1) / queue.length + } + }); + } + + me.onmessage({ + data : { + event : "done", + data : outData + } + }); + } + + this.postMessage = function(data) { + setTimeout(function() { + processMessage(data) + }, 0); + }; + + this.onmessage = function() {}; + + } + + + + Pixastic.log = function(str) { + if (typeof console != "undefined" && console.log) { + console.log("Pixastic: " + str); + } + }; + + function toCanvas(o) { + var canvas; + if (typeof o == "object") { + if (typeof o.tagName == "string") { + if (o.tagName.toLowerCase() == "canvas" || o.tagName.toLowerCase() == "img") { + canvas = document.createElement("canvas"); + canvas.width = o.width; + canvas.height = o.height; + canvas.getContext("2d").drawImage(o, 0,0); + } + } else if ((window.ImageData && o instanceof window.ImageData) + || (typeof o.width == "number" && typeof o.height == "number" && typeof o.data == "object")) { + canvas = document.createElement("canvas"); + canvas.width = o.width; + canvas.height = o.height; + canvas.getContext("2d").putImageData(o, 0, 0); + } + } + return canvas; + }; + + function toImage(o) { + var canvas = toCanvas(o), + image = new Image(); + image.width = canvas.width; + image.height = canvas.height; + image.src = canvas.toDataURL(); + return image; + }; + + function toImageData(o) { + var canvas = toCanvas(o), + ctx = canvas.getContext("2d"); + return ctx.getImageData(0, 0, canvas.width, canvas.height); + }; + + function histogram(imageData) { + var values = [], + i, p, + data = imageData.data, + round = Math.round, + maxValue, + n = imageData.width * imageData.height; + + for (i=0;i<256;i++) { + values[i] = 0; + } + + for (i=0;i maxValue) { + maxValue = values[i]; + } + } + + return { + maxValue : maxValue, + values : values + }; + } + + Pixastic.toCanvas = toCanvas; + Pixastic.toImage = toImage; + Pixastic.toImageData = toImageData; + Pixastic.histogram = histogram; + + Pixastic.Color = { + rgb2hsl : function(r, g, b) { + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 0; + if (g > 255) g = 0; + if (b > 255) b = 0; + }, + + rgb2hsv : function(r, g, b) { + }, + + rgb2hex : function(r, g, b) { + }, + + hsl2rgb : function(h, s, l) { + }, + + hsv2rgb : function(h, s, v) { + } + + } + + return Pixastic; + +})(); + + + + diff --git a/files_reader/vendor/pixastic/pixastic.worker.control.js b/files_reader/vendor/pixastic/pixastic.worker.control.js new file mode 100644 index 0000000..39edbdd --- /dev/null +++ b/files_reader/vendor/pixastic/pixastic.worker.control.js @@ -0,0 +1,1580 @@ +//importScripts("pixastic.js"); +// ... but Chromium refuses to import pixastic.js when CSP2 nonce is active, so inline it instead... + +/*! + * Pixastic - JavaScript Image Processing + * http://pixastic.com/ + * Copyright 2012, Jacob Seidelin + * + * Dual licensed under the MPL 1.1 or GPLv3 licenses. + * http://pixastic.com/license-mpl.txt + * http://pixastic.com/license-gpl-3.0.txt + * + */ + + var Pixastic = (function() { + + var worker; + + function createImageData(ctx, width, height) { + if (ctx.createImageData) { + return ctx.createImageData(width, height); + } else { + return ctx.getImageData(0, 0, width, height); + } + } + + function Pixastic(ctx, workerControlPath) { + + var P = {}, + width = ctx.canvas.width, + height = ctx.canvas.height, + queue = [], + workerControlPath = workerControlPath || ""; + + if (!worker) { + if (typeof window.Worker != "undefined") { + try { + worker = new window.Worker(workerControlPath + "pixastic.worker.control.js"); + } catch(e) { + if (location.protocol == "file:") { + Pixastic.log("Could not create native worker, running from file://") + } else { + Pixastic.log("Could not create native worker.") + } + } + } + if (!worker) { + worker = new Pixastic.Worker(); + } + } + + for (var e in Pixastic.Effects) { + if (Pixastic.Effects.hasOwnProperty(e)) { + (function(e) { + P[e] = function(options) { + queue.push({ + effect : e, + options : options + }); + return P; + } + + P.done = function(callback, progress) { + var inData, outData; + + try { + inData = ctx.getImageData(0, 0, width, height); + } catch(e) { + if (location.protocol == "file:") { + throw new Error("Could not access image data, running from file://"); + } else { + throw new Error("Could not access image data, is canvas tainted by cross-origin data?"); + } + } + + outData = createImageData(ctx, width, height); + + worker.postMessage({ + queue : queue, + inData : inData, + outData : outData, + width : width, + height : height + }); + + worker.onmessage = function(message) { + var d = message.data; + switch (d.event) { + case "done" : + ctx.putImageData(d.data, 0, 0); + if (callback) { + callback(); + } + if (progress) { + progress(1); + } + break; + case "progress" : + if (progress) { + progress(d.data); + } + break; + case "error" : + break; + } + } + + if (progress) { + progress(0); + } + } + })(e); + } + } + return P; + } + + + Pixastic.Worker = function() { + var me = this; + function processMessage(data) { + var queue = data.queue, + inData = data.inData, + outData = data.outData, + width = data.width, + height = data.height, + tmpData; + + for (var i=0;i 0) { + tmpData = inData; + inData = outData; + outData = tmpData; + } + + if (typeof importScripts == "function") { + progressCallback = function(p) { + me.onmessage({ + data : { + event : "progress", + data : (i + p) / queue.length + } + }); + return p; + } + } + + Pixastic.Effects[e](inData.data, outData.data, width, height, options, progressCallback); + + me.onmessage({ + data : { + event : "progress", + data : (i+1) / queue.length + } + }); + } + + me.onmessage({ + data : { + event : "done", + data : outData + } + }); + } + + this.postMessage = function(data) { + setTimeout(function() { + processMessage(data) + }, 0); + }; + + this.onmessage = function() {}; + + } + + + + Pixastic.log = function(str) { + if (typeof console != "undefined" && console.log) { + console.log("Pixastic: " + str); + } + }; + + function toCanvas(o) { + var canvas; + if (typeof o == "object") { + if (typeof o.tagName == "string") { + if (o.tagName.toLowerCase() == "canvas" || o.tagName.toLowerCase() == "img") { + canvas = document.createElement("canvas"); + canvas.width = o.width; + canvas.height = o.height; + canvas.getContext("2d").drawImage(o, 0,0); + } + } else if ((window.ImageData && o instanceof window.ImageData) + || (typeof o.width == "number" && typeof o.height == "number" && typeof o.data == "object")) { + canvas = document.createElement("canvas"); + canvas.width = o.width; + canvas.height = o.height; + canvas.getContext("2d").putImageData(o, 0, 0); + } + } + return canvas; + }; + + function toImage(o) { + var canvas = toCanvas(o), + image = new Image(); + image.width = canvas.width; + image.height = canvas.height; + image.src = canvas.toDataURL(); + return image; + }; + + function toImageData(o) { + var canvas = toCanvas(o), + ctx = canvas.getContext("2d"); + return ctx.getImageData(0, 0, canvas.width, canvas.height); + }; + + function histogram(imageData) { + var values = [], + i, p, + data = imageData.data, + round = Math.round, + maxValue, + n = imageData.width * imageData.height; + + for (i=0;i<256;i++) { + values[i] = 0; + } + + for (i=0;i maxValue) { + maxValue = values[i]; + } + } + + return { + maxValue : maxValue, + values : values + }; + } + + Pixastic.toCanvas = toCanvas; + Pixastic.toImage = toImage; + Pixastic.toImageData = toImageData; + Pixastic.histogram = histogram; + + Pixastic.Color = { + rgb2hsl : function(r, g, b) { + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 0; + if (g > 255) g = 0; + if (b > 255) b = 0; + }, + + rgb2hsv : function(r, g, b) { + }, + + rgb2hex : function(r, g, b) { + }, + + hsl2rgb : function(h, s, l) { + }, + + hsv2rgb : function(h, s, v) { + } + + } + + return Pixastic; + +})(); + +// END pixastic.js + +//importScripts("pixastic.effects.js"); +// ... but Chromium refuses to import pixastic.effects.js when CSP2 nonce is active, so inline it instead... + +Pixastic.Effects = (function() { + + function defaultOptions(options, defaults) { + var O = {}; + for (var opt in defaults) { + if (typeof options[opt] == "undefined") { + O[opt] = defaults[opt]; + } else { + O[opt] = options[opt]; + } + } + return O; + } + + function clamp(val, min, max) { + return Math.min(max, Math.max(min, val)); + } + + function convolve3x3(inData, outData, width, height, kernel, progress, alpha, invert, mono) { + var idx, r, g, b, a, + pyc, pyp, pyn, + pxc, pxp, pxn, + x, y, + + prog, lastProg = 0, + n = width * height * 4, + + k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], + k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], + k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], + + p00, p01, p02, + p10, p11, p12, + p20, p21, p22; + + for (y=0;y= width-1) pyn = pyc; + + for (x=0;x= width-1) pxn = pxc; + + p00 = pyp + pxp; p01 = pyp + pxc; p02 = pyp + pxn; + p10 = pyc + pxp; p11 = pyc + pxc; p12 = pyc + pxn; + p20 = pyn + pxp; p21 = pyn + pxc; p22 = pyn + pxn; + + r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + + inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + + inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22; + + g = inData[p00 + 1] * k00 + inData[p01 + 1] * k01 + inData[p02 + 1] * k02 + + inData[p10 + 1] * k10 + inData[p11 + 1] * k11 + inData[p12 + 1] * k12 + + inData[p20 + 1] * k20 + inData[p21 + 1] * k21 + inData[p22 + 1] * k22; + + b = inData[p00 + 2] * k00 + inData[p01 + 2] * k01 + inData[p02 + 2] * k02 + + inData[p10 + 2] * k10 + inData[p11 + 2] * k11 + inData[p12 + 2] * k12 + + inData[p20 + 2] * k20 + inData[p21 + 2] * k21 + inData[p22 + 2] * k22; + + if (alpha) { + a = inData[p00 + 3] * k00 + inData[p01 + 3] * k01 + inData[p02 + 3] * k02 + + inData[p10 + 3] * k10 + inData[p11 + 3] * k11 + inData[p12 + 3] * k12 + + inData[p20 + 3] * k20 + inData[p21 + 3] * k21 + inData[p22 + 3] * k22; + } else { + a = inData[idx+3]; + } + + if (mono) { + r = g = b = (r + g + b) / 3; + } + if (invert) { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + function convolve5x5(inData, outData, width, height, kernel, progress, alpha, invert, mono) { + var idx, r, g, b, a, + pyc, pyp, pyn, pypp, pynn, + pxc, pxp, pxn, pxpp, pxnn, + x, y, + + prog, lastProg = 0, + n = width * height * 4, + + k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], k03 = kernel[0][3], k04 = kernel[0][4], + k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], k13 = kernel[1][3], k14 = kernel[1][4], + k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], k23 = kernel[2][3], k24 = kernel[2][4], + k30 = kernel[3][0], k31 = kernel[3][1], k32 = kernel[3][2], k33 = kernel[3][3], k34 = kernel[3][4], + k40 = kernel[4][0], k41 = kernel[4][1], k42 = kernel[4][2], k43 = kernel[4][3], k44 = kernel[4][4], + + p00, p01, p02, p03, p04, + p10, p11, p12, p13, p14, + p20, p21, p22, p23, p24, + p30, p31, p32, p33, p34, + p40, p41, p42, p43, p44; + + for (y=0;y= width-1) pyn = pyc; + if (y < 2) pypp = pyp; + if (y >= width-2) pynn = pyn; + + for (x=0;x= width-1) pxn = pxc; + if (x < 2) pxpp = pxp; + if (x >= width-2) pxnn = pxn; + + p00 = pypp + pxpp; p01 = pypp + pxp; p02 = pypp + pxc; p03 = pypp + pxn; p04 = pypp + pxnn; + p10 = pyp + pxpp; p11 = pyp + pxp; p12 = pyp + pxc; p13 = pyp + pxn; p14 = pyp + pxnn; + p20 = pyc + pxpp; p21 = pyc + pxp; p22 = pyc + pxc; p23 = pyc + pxn; p24 = pyc + pxnn; + p30 = pyn + pxpp; p31 = pyn + pxp; p32 = pyn + pxc; p33 = pyn + pxn; p34 = pyn + pxnn; + p40 = pynn + pxpp; p41 = pynn + pxp; p42 = pynn + pxc; p43 = pynn + pxn; p44 = pynn + pxnn; + + r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + inData[p03] * k04 + inData[p02] * k04 + + inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + inData[p13] * k14 + inData[p12] * k14 + + inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22 + inData[p23] * k24 + inData[p22] * k24 + + inData[p30] * k30 + inData[p31] * k31 + inData[p32] * k32 + inData[p33] * k34 + inData[p32] * k34 + + inData[p40] * k40 + inData[p41] * k41 + inData[p42] * k42 + inData[p43] * k44 + inData[p42] * k44; + + g = inData[p00+1] * k00 + inData[p01+1] * k01 + inData[p02+1] * k02 + inData[p03+1] * k04 + inData[p02+1] * k04 + + inData[p10+1] * k10 + inData[p11+1] * k11 + inData[p12+1] * k12 + inData[p13+1] * k14 + inData[p12+1] * k14 + + inData[p20+1] * k20 + inData[p21+1] * k21 + inData[p22+1] * k22 + inData[p23+1] * k24 + inData[p22+1] * k24 + + inData[p30+1] * k30 + inData[p31+1] * k31 + inData[p32+1] * k32 + inData[p33+1] * k34 + inData[p32+1] * k34 + + inData[p40+1] * k40 + inData[p41+1] * k41 + inData[p42+1] * k42 + inData[p43+1] * k44 + inData[p42+1] * k44; + + b = inData[p00+2] * k00 + inData[p01+2] * k01 + inData[p02+2] * k02 + inData[p03+2] * k04 + inData[p02+2] * k04 + + inData[p10+2] * k10 + inData[p11+2] * k11 + inData[p12+2] * k12 + inData[p13+2] * k14 + inData[p12+2] * k14 + + inData[p20+2] * k20 + inData[p21+2] * k21 + inData[p22+2] * k22 + inData[p23+2] * k24 + inData[p22+2] * k24 + + inData[p30+2] * k30 + inData[p31+2] * k31 + inData[p32+2] * k32 + inData[p33+2] * k34 + inData[p32+2] * k34 + + inData[p40+2] * k40 + inData[p41+2] * k41 + inData[p42+2] * k42 + inData[p43+2] * k44 + inData[p42+2] * k44; + + if (alpha) { + a = inData[p00+3] * k00 + inData[p01+3] * k01 + inData[p02+3] * k02 + inData[p03+3] * k04 + inData[p02+3] * k04 + + inData[p10+3] * k10 + inData[p11+3] * k11 + inData[p12+3] * k12 + inData[p13+3] * k14 + inData[p12+3] * k14 + + inData[p20+3] * k20 + inData[p21+3] * k21 + inData[p22+3] * k22 + inData[p23+3] * k24 + inData[p22+3] * k24 + + inData[p30+3] * k30 + inData[p31+3] * k31 + inData[p32+3] * k32 + inData[p33+3] * k34 + inData[p32+3] * k34 + + inData[p40+3] * k40 + inData[p41+3] * k41 + inData[p42+3] * k42 + inData[p43+3] * k44 + inData[p42+3] * k44; + } else { + a = inData[idx+3]; + } + + if (mono) { + r = g = b = (r + g + b) / 3; + } + + if (invert) { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + function gaussian(inData, outData, width, height, kernelSize, progress) { + var r, g, b, a, idx, + n = width * height * 4, + x, y, i, j, + inx, iny, w, + tmpData = [], + maxKernelSize = 13, + kernelSize = clamp(kernelSize, 3, maxKernelSize), + k1 = -kernelSize / 2 + (kernelSize % 2 ? 0.5 : 0), + k2 = kernelSize + k1, + weights, + kernels = [[1]], + prog, lastProg = 0; + + + for (i=1;i= width) { + inx = width - 1; + } + + idx = (iny * width + inx) * 4; + + r += inData[idx] * w; + g += inData[idx + 1] * w; + b += inData[idx + 2] * w; + a += inData[idx + 3] * w; + + } + + idx = (y * width + x) * 4; + + tmpData[idx] = r; + tmpData[idx+1] = g; + tmpData[idx+2] = b; + tmpData[idx+3] = a; + + if (progress) { + prog = (idx/n*50 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + + lastProg = 0; + + // pass 2 + for (y=0;y= height) { + iny = height - 1; + } + + idx = (iny * width + inx) * 4; + + r += tmpData[idx] * w; + g += tmpData[idx + 1] * w; + b += tmpData[idx + 2] * w; + a += tmpData[idx + 3] * w; + } + + idx = (y * width + x) * 4; + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = 0.5 + (idx/n*50 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + + return { + + invert : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0; + + for (i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + sepia : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + r, g, b; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + solarize : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + r, g, b; + + for (i=0;i 127 ? 255 - r : r; + outData[i+1] = g > 127 ? 255 - g : g; + outData[i+2] = b > 127 ? 255 - b : b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + brightness : function(inData, outData, width, height, options, progress) { + options = defaultOptions(options, { + brightness : 0, + contrast : 0 + }); + + var contrast = clamp(options.contrast, -1, 1) / 2, + brightness = 1 + clamp(options.brightness, -1, 1), + prog, lastProg = 0, + r, g, b, + n = width * height * 4; + + var brightMul = brightness < 0 ? - brightness : brightness; + var brightAdd = brightness < 0 ? 0 : brightness; + + contrast = 0.5 * Math.tan((contrast + 1) * Math.PI/4); + contrastAdd = - (contrast - 0.5) * 255; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + desaturate : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + level; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + lighten : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + mul = 1 + clamp(options.amount, 0, 1); + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + noise : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + amount = clamp(options.amount, 0, 1), + strength = clamp(options.strength, 0, 1), + mono = !!options.mono, + random = Math.random, + rnd, r, g, b; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + flipv : function(inData, outData, width, height, options, progress) { + var inPix, outPix, + n = width * height * 4, + prog, lastProg = 0, + x, y; + for (y=0;y> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + fliph : function(inData, outData, width, height, options, progress) { + var inPix, outPix, + n = width * height * 4, + prog, lastProg = 0, + x, y; + for (y=0;y> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + blur : function(inData, outData, width, height, options, progress) { + gaussian(inData, outData, width, height, options.kernelSize, progress); + }, + + glow : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, r, g, b, + amount = options.amount, + tmpData = [], + gaussProgress, + prog, lastProg = 0; + + if (progress) { + gaussProgress = function(p) { + progress(p * 0.8); + return p; + } + } + + gaussian(inData, tmpData, width, height, options.kernelSize, gaussProgress); + + for (i=0;i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = 0.8 + (i/n*100 >> 0) / 100 * 0.2; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + convolve3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3(inData, outData, width, height, options.kernel, progress); + }, + + convolve5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5(inData, outData, width, height, options.kernel, progress); + }, + + // A 3x3 high-pass filter + sharpen3x3 : function(inData, outData, width, height, options, progress) { + var a = - clamp(options.strength, 0, 1); + convolve3x3( + inData, outData, width, height, + [[a, a, a], + [a, 1-a*8, a], + [a, a, a]], + progress + ); + }, + + // A 5x5 high-pass filter + sharpen5x5 : function(inData, outData, width, height, options, progress) { + var a = - clamp(options.strength, 0, 1); + convolve5x5( + inData, outData, width, height, + [[a, a, a, a, a], + [a, a, a, a, a], + [a, a, 1-a*24, a, a], + [a, a, a, a, a], + [a, a, a, a, a]], + progress + ); + }, + + // A 3x3 low-pass mean filter + soften3x3 : function(inData, outData, width, height, options, progress) { + var c = 1/9; + convolve3x3( + inData, outData, width, height, + [[c, c, c], + [c, c, c], + [c, c, c]], + progress + ); + }, + + // A 5x5 low-pass mean filter + soften5x5 : function(inData, outData, width, height, options, progress) { + var c = 1/25; + convolve5x5( + inData, outData, width, height, + [[c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c]], + progress + ); + }, + + // A 3x3 Cross edge-detect + crossedges : function(inData, outData, width, height, options, progress) { + var a = clamp(options.strength, 0, 1) * 5 + convolve3x3( + inData, outData, width, height, + [[ 0, -a, 0], + [-a, 0, a], + [ 0, a, 0]], + progress, + false, true + ); + }, + + // 3x3 directional emboss + emboss : function(inData, outData, width, height, options, progress) { + var amount = options.amount, + angle = options.angle, + x = Math.cos(-angle) * amount, + y = Math.sin(-angle) * amount, + n = width * height * 4, + + a00 = -x - y, + a10 = -x, + a20 = y - x, + a01 = -y, + a21 = y, + a02 = -y + x, + a12 = x, + a22 = y + x, + + tmpData = [], + + prog, lastProg = 0, + convProgress; + + if (progress) { + convProgress = function(p) { + progress(p * 0.5) + return p; + }; + } + + convolve3x3( + inData, tmpData, width, height, + [[a00, a01, a02], + [a10, 0, a12], + [a20, a21, a22]] + ); + + for (var i=0;i> 0) / 100 * 0.5; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + + // A 3x3 Sobel edge detect (similar to Photoshop's) + findedges : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, + data1 = [], + data2 = [], + gr1, gr2, gg1, gg2, gb1, gb2, + prog, lastProg = 0, + convProgress1, convProgress2; + + if (progress) { + convProgress1 = function(p) { + progress(p * 0.4); + return p; + }; + convProgress2 = function(p) { + progress(0.4 + p * 0.4); + return p; + }; + } + + convolve3x3(inData, data1, width, height, + [[-1, 0, 1], + [-2, 0, 2], + [-1, 0, 1]] + ); + convolve3x3(inData, data2, width, height, + [[-1, -2, -1], + [ 0, 0, 0], + [ 1, 2, 1]] + ); + + for (i=0;i> 0) / 100 * 0.2; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + // A 3x3 edge enhance + edgeenhance3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3( + inData, outData, width, height, + [[-1/9, -1/9, -1/9], + [-1/9, 17/9, -1/9], + [-1/9, -1/9, -1/9]], + progress + ); + }, + + // A 5x5 edge enhance + edgeenhance5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5( + inData, outData, width, height, + [[-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, 49/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25]], + progress + ); + }, + + // A 3x3 Laplacian edge-detect + laplace3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3( + inData, outData, width, height, + [[-1, -1, -1], + [-1, 8, -1], + [-1, -1, -1]], + progress, + false, true, true + ); + }, + + // A 5x5 Laplacian edge-detect + laplace5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5( + inData, outData, width, height, + [[-1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1], + [-1, -1, 24, -1, -1], + [-1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1]], + progress, + false, true, true + ); + }, + + coloradjust : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + r, g, b, + prog, lastProg = 0, + ar = clamp(options.r, -1, 1) * 255, + ag = clamp(options.g, -1, 1) * 255, + ab = clamp(options.b, -1, 1) * 255; + + for (var i=0;i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + colorfilter : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, r, g, b, + luminosity = !!options.luminosity, + prog, lastProg = 0, + min, max, h, l, h1, chroma, tmp, m, + ar = clamp(options.r, 0, 1), + ag = clamp(options.g, 0, 1), + ab = clamp(options.b, 0, 1); + + for (i=0;i max) max = g; + if (b > max) max = b; + if (g < min) min = g; + if (b < min) min = b; + chroma = (max - min); + + if (r == max) { + h = ((g - b) / chroma) % 6; + } else if (g == max) { + h = ((b - r) / chroma) + 2; + } else { + h = ((r - g) / chroma) + 4; + } + + h1 = h >> 0; + tmp = chroma * (h - h1); + r = g = b = l - (r * 0.3 + g * 0.59 + b * 0.11); + + if (h1 == 0) { + r += chroma; + g += tmp; + } else if (h1 == 1) { + r += chroma - tmp; + g += chroma; + } else if (h1 == 2) { + g += chroma; + b += tmp; + } else if (h1 == 3) { + g += chroma - tmp; + b += chroma; + } else if (h1 == 4) { + r += tmp; + b += chroma; + } else if (h1 == 5) { + r += chroma; + b += chroma - tmp; + } + } + + outData[i] = r * 255; + outData[i+1] = g * 255; + outData[i+2] = b * 255; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + hsl : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + hue = clamp(options.hue, -1, 1), + saturation = clamp(options.saturation, -1, 1), + lightness = clamp(options.lightness, -1, 1), + satMul = 1 + saturation * (saturation < 0 ? 1 : 2), + lightMul = lightness < 0 ? 1 + lightness : 1 - lightness, + lightAdd = lightness < 0 ? 0 : lightness * 255, + vs, ms, vm, h, s, l, v, m, vmh, sextant, + prog, lastProg = 0; + + hue = (hue * 6) % 6; + + for (var i=0;i vs) vs = g; + if (b > vs) vs = b; + ms = r; + if (g < ms) ms = g; + if (b < ms) ms = b; + vm = (vs-ms); + l = (ms+vs)/510; + + if (l > 0 && vm > 0) { + if (l <= 0.5) { + s = vm / (vs+ms) * satMul; + if (s > 1) s = 1; + v = (l * (1+s)); + } else { + s = vm / (510-vs-ms) * satMul; + if (s > 1) s = 1; + v = (l+s - l*s); + } + if (r == vs) { + if (g == ms) { + h = 5 + ((vs-b)/vm) + hue; + } else { + h = 1 - ((vs-g)/vm) + hue; + } + } else if (g == vs) { + if (b == ms) { + h = 1 + ((vs-r)/vm) + hue; + } else { + h = 3 - ((vs-b)/vm) + hue; + } + } else { + if (r == ms) { + h = 3 + ((vs-g)/vm) + hue; + } else { + h = 5 - ((vs-r)/vm) + hue; + } + } + if (h < 0) h += 6; + if (h >= 6) h -= 6; + m = (l + l - v); + sextant = h >> 0; + vmh = (v - m) * (h - sextant); + if (sextant == 0) { + r = v; + g = m + vmh; + b = m; + } else if (sextant == 1) { + r = v - vmh; + g = v; + b = m; + } else if (sextant == 2) { + r = m; + g = v; + b = m + vmh; + } else if (sextant == 3) { + r = m; + g = v - vmh; + b = v; + } else if (sextant == 4) { + r = m + vmh; + g = m; + b = v; + } else if (sextant == 5) { + r = v; + g = m; + b = v - vmh; + } + + r *= 255; + g *= 255; + b *= 255; + } + } + + r = r * lightMul + lightAdd; + g = g * lightMul + lightAdd; + b = b * lightMul + lightAdd; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + posterize : function(inData, outData, width, height, options, progress) { + var numLevels = clamp(options.levels, 2, 256), + numAreas = 256 / numLevels, + numValues = 256 / (numLevels-1), + r, g, b, + n = width * height * 4, + prog, lastProg = 0; + + for (i=0;i>0); + outData[i+1] = numValues * ((inData[i+1] / numAreas)>>0); + outData[i+2] = numValues * ((inData[i+2] / numAreas)>>0); + + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + + }, + + removenoise : function(inData, outData, width, height, options, progress) { + var r, g, b, c, y, x, idx, + pyc, pyp, pyn, + pxc, pxp, pxn, + minR, minG, minB, maxR, maxG, maxB, + n, prog, lastProg = 0; + + n = width * height * 4; + + for (y=0;y= width-1) pyn = pyc; + + for (x=0;x= width-1) pxn = pxc; + + minR = maxR = inData[pyc + pxp]; + c = inData[pyc + pxn]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + c = inData[pyp + pxc]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + c = inData[pyn + pxc]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + + minG = inData[pyc + pxp + 1]; + c = inData[pyc + pxn + 1]; + if (c < minG) minG = c; + c = inData[pyp + pxc + 1]; + if (c < minG) minG = c; + c = inData[pyn + pxc + 1]; + if (c < minG) minG = c; + + minB = inData[pyc + pxp + 2]; + c = inData[pyc + pxn + 2]; + if (c < minB) minB = c; + c = inData[pyp + pxc + 2]; + if (c < minB) minB = c; + c = inData[pyn + pxc + 2]; + if (c < minB) minB = c; + + r = inData[idx] + g = inData[idx + 1] + b = inData[idx + 2] + + if (r < minR) r = minR; + if (r > maxR) r = maxR; + if (g < minG) g = minG; + if (g > maxG) g = maxG; + if (b < minB) b = minB; + if (b > maxB) b = maxB; + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = inData[idx+3]; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + mosaic : function(inData, outData, width, height, options, progress) { + + var blockSize = clamp(options.blockSize, 1, Math.max(width, height)), + yBlocks = Math.ceil(height / blockSize), + xBlocks = Math.ceil(width / blockSize), + y0, y1, x0, x1, idx, pidx, + n = yBlocks * xBlocks, + prog, lastProg = 0; + + for (i=0, y0=0, bidx=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + y0 = y1; + } + }, + + equalize : function(inData, outData, width, height, options, progress) { + var n = width * height, p, i, level, ratio, + prog, lastProg; + var round = Math.round; + // build histogram + var pdf = new Array(256); + for (i=0;i<256;i++) { + pdf[i] = 0; + } + + for (i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }; + +})(); + +// END pixastic.effects.js + +var worker = new Pixastic.Worker(); + +worker.onmessage = function(message) { + postMessage(message.data); +} + +onmessage = function(message) { + worker.postMessage(message.data); +} + diff --git a/files_reader/vendor/pixastic/pixastic.worker.js b/files_reader/vendor/pixastic/pixastic.worker.js new file mode 100644 index 0000000..733d79a --- /dev/null +++ b/files_reader/vendor/pixastic/pixastic.worker.js @@ -0,0 +1,47 @@ +Pixastic.Worker = function() { + var me = this; + function processMessage(data) { + var queue = data.queue, + inData = data.inData, + outData = data.outData, + width = data.width, + height = data.height, + tmpData; + + for (var i=0;i 0) { + tmpData = inData; + inData = outData; + outData = tmpData; + } + + Pixastic.Effects[e](inData.data, outData.data, width, height, options); + + me.onmessage({ + data : { + event : "progress", + data : (i+1) / queue.length + } + }); + } + + me.onmessage({ + data : { + event : "done", + data : outData + } + }); + } + + this.postMessage = function(data) { + setTimeout(function() { + processMessage(data) + }, 0); + } + + this.onmessage = function() {}; + +} diff --git a/files_reader/vendor/pixastic/pixastic_combined.js b/files_reader/vendor/pixastic/pixastic_combined.js new file mode 100644 index 0000000..0c7c5d4 --- /dev/null +++ b/files_reader/vendor/pixastic/pixastic_combined.js @@ -0,0 +1,1610 @@ +/* pixastic */ + +/*! + * Pixastic - JavaScript Image Processing + * http://pixastic.com/ + * Copyright 2012, Jacob Seidelin + * + * Dual licensed under the MPL 1.1 or GPLv3 licenses. + * http://pixastic.com/license-mpl.txt + * http://pixastic.com/license-gpl-3.0.txt + * + */ + + var Pixastic = (function() { + + var worker; + + function createImageData(ctx, width, height) { + if (ctx.createImageData) { + return ctx.createImageData(width, height); + } else { + return ctx.getImageData(0, 0, width, height); + } + } + + function Pixastic(ctx, workerControlPath) { + + var P = {}, + width = ctx.canvas.width, + height = ctx.canvas.height, + queue = [], + workerControlPath = workerControlPath || "vendor/pixastic/"; + + if (!worker) { + if (typeof window.Worker != "undefined") { + try { + worker = new window.Worker(workerControlPath + "pixastic.worker.control.js"); + } catch(e) { + if (location.protocol == "file:") { + Pixastic.log("Could not create native worker, running from file://") + } else { + Pixastic.log("Could not create native worker.") + } + } + } + if (!worker) { + worker = new Pixastic.Worker(); + } + } + + for (var e in Pixastic.Effects) { + if (Pixastic.Effects.hasOwnProperty(e)) { + (function(e) { + P[e] = function(options) { + queue.push({ + effect : e, + options : options + }); + return P; + }; + + P.done = function(callback, progress) { + var inData, outData; + + try { + inData = ctx.getImageData(0, 0, width, height); + } catch(e) { + if (location.protocol == "file:") { + throw new Error("Could not access image data, running from file://"); + } else { + throw new Error("Could not access image data, is canvas tainted by cross-origin data?"); + } + } + + outData = createImageData(ctx, width, height); + + worker.postMessage({ + queue : queue, + inData : inData, + outData : outData, + width : width, + height : height + }); + + worker.onmessage = function(message) { + var d = message.data; + switch (d.event) { + case "done" : + ctx.putImageData(d.data, 0, 0); + if (callback) { + callback(); + } + if (progress) { + progress(1); + } + break; + case "progress" : + if (progress) { + progress(d.data); + } + break; + case "error" : + break; + } + }; + + if (progress) { + progress(0); + } + } + })(e); + } + } + return P; + } + + + Pixastic.Worker = function() { + var me = this; + function processMessage(data) { + var queue = data.queue, + inData = data.inData, + outData = data.outData, + width = data.width, + height = data.height, + tmpData; + + for (var i=0;i 0) { + tmpData = inData; + inData = outData; + outData = tmpData; + } + + if (typeof importScripts == "function") { + progressCallback = function(p) { + me.onmessage({ + data : { + event : "progress", + data : (i + p) / queue.length + } + }); + return p; + } + } + + Pixastic.Effects[e](inData.data, outData.data, width, height, options, progressCallback); + + me.onmessage({ + data : { + event : "progress", + data : (i+1) / queue.length + } + }); + } + + me.onmessage({ + data : { + event : "done", + data : outData + } + }); + } + + this.postMessage = function(data) { + setTimeout(function() { + processMessage(data) + }, 0); + }; + + this.onmessage = function() {}; + + }; + + + + Pixastic.log = function(str) { + if (typeof console != "undefined" && console.log) { + console.log("Pixastic: " + str); + } + }; + + function toCanvas(o) { + var canvas; + if (typeof o == "object") { + if (typeof o.tagName == "string") { + if (o.tagName.toLowerCase() == "canvas" || o.tagName.toLowerCase() == "img") { + canvas = document.createElement("canvas"); + canvas.width = o.width; + canvas.height = o.height; + canvas.getContext("2d").drawImage(o, 0,0); + } + } else if ((window.ImageData && o instanceof window.ImageData) + || (typeof o.width == "number" && typeof o.height == "number" && typeof o.data == "object")) { + canvas = document.createElement("canvas"); + canvas.width = o.width; + canvas.height = o.height; + canvas.getContext("2d").putImageData(o, 0, 0); + } + } + return canvas; + } + function toImage(o) { + var canvas = toCanvas(o), + image = new Image(); + image.width = canvas.width; + image.height = canvas.height; + image.src = canvas.toDataURL(); + return image; + } + function toImageData(o) { + var canvas = toCanvas(o), + ctx = canvas.getContext("2d"); + return ctx.getImageData(0, 0, canvas.width, canvas.height); + } + function histogram(imageData) { + var values = [], + i, p, + data = imageData.data, + round = Math.round, + maxValue, + n = imageData.width * imageData.height; + + for (i=0;i<256;i++) { + values[i] = 0; + } + + for (i=0;i maxValue) { + maxValue = values[i]; + } + } + + return { + maxValue : maxValue, + values : values + }; + } + + Pixastic.toCanvas = toCanvas; + Pixastic.toImage = toImage; + Pixastic.toImageData = toImageData; + Pixastic.histogram = histogram; + + Pixastic.Color = { + rgb2hsl : function(r, g, b) { + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 0; + if (g > 255) g = 0; + if (b > 255) b = 0; + }, + + rgb2hsv : function(r, g, b) { + }, + + rgb2hex : function(r, g, b) { + }, + + hsl2rgb : function(h, s, l) { + }, + + hsv2rgb : function(h, s, v) { + } + + }; + + return Pixastic; + +})(); + + +Pixastic.Effects = (function() { + + function defaultOptions(options, defaults) { + var O = {}; + for (var opt in defaults) { + if (typeof options[opt] == "undefined") { + O[opt] = defaults[opt]; + } else { + O[opt] = options[opt]; + } + } + return O; + } + + function clamp(val, min, max) { + return Math.min(max, Math.max(min, val)); + } + + function convolve3x3(inData, outData, width, height, kernel, progress, alpha, invert, mono) { + var idx, r, g, b, a, + pyc, pyp, pyn, + pxc, pxp, pxn, + x, y, + + prog, lastProg = 0, + n = width * height * 4, + + k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], + k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], + k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], + + p00, p01, p02, + p10, p11, p12, + p20, p21, p22; + + for (y=0;y= width-1) pyn = pyc; + + for (x=0;x= width-1) pxn = pxc; + + p00 = pyp + pxp; p01 = pyp + pxc; p02 = pyp + pxn; + p10 = pyc + pxp; p11 = pyc + pxc; p12 = pyc + pxn; + p20 = pyn + pxp; p21 = pyn + pxc; p22 = pyn + pxn; + + r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + + inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + + inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22; + + g = inData[p00 + 1] * k00 + inData[p01 + 1] * k01 + inData[p02 + 1] * k02 + + inData[p10 + 1] * k10 + inData[p11 + 1] * k11 + inData[p12 + 1] * k12 + + inData[p20 + 1] * k20 + inData[p21 + 1] * k21 + inData[p22 + 1] * k22; + + b = inData[p00 + 2] * k00 + inData[p01 + 2] * k01 + inData[p02 + 2] * k02 + + inData[p10 + 2] * k10 + inData[p11 + 2] * k11 + inData[p12 + 2] * k12 + + inData[p20 + 2] * k20 + inData[p21 + 2] * k21 + inData[p22 + 2] * k22; + + if (alpha) { + a = inData[p00 + 3] * k00 + inData[p01 + 3] * k01 + inData[p02 + 3] * k02 + + inData[p10 + 3] * k10 + inData[p11 + 3] * k11 + inData[p12 + 3] * k12 + + inData[p20 + 3] * k20 + inData[p21 + 3] * k21 + inData[p22 + 3] * k22; + } else { + a = inData[idx+3]; + } + + if (mono) { + r = g = b = (r + g + b) / 3; + } + if (invert) { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + function convolve5x5(inData, outData, width, height, kernel, progress, alpha, invert, mono) { + var idx, r, g, b, a, + pyc, pyp, pyn, pypp, pynn, + pxc, pxp, pxn, pxpp, pxnn, + x, y, + + prog, lastProg = 0, + n = width * height * 4, + + k00 = kernel[0][0], k01 = kernel[0][1], k02 = kernel[0][2], k03 = kernel[0][3], k04 = kernel[0][4], + k10 = kernel[1][0], k11 = kernel[1][1], k12 = kernel[1][2], k13 = kernel[1][3], k14 = kernel[1][4], + k20 = kernel[2][0], k21 = kernel[2][1], k22 = kernel[2][2], k23 = kernel[2][3], k24 = kernel[2][4], + k30 = kernel[3][0], k31 = kernel[3][1], k32 = kernel[3][2], k33 = kernel[3][3], k34 = kernel[3][4], + k40 = kernel[4][0], k41 = kernel[4][1], k42 = kernel[4][2], k43 = kernel[4][3], k44 = kernel[4][4], + + p00, p01, p02, p03, p04, + p10, p11, p12, p13, p14, + p20, p21, p22, p23, p24, + p30, p31, p32, p33, p34, + p40, p41, p42, p43, p44; + + for (y=0;y= width-1) pyn = pyc; + if (y < 2) pypp = pyp; + if (y >= width-2) pynn = pyn; + + for (x=0;x= width-1) pxn = pxc; + if (x < 2) pxpp = pxp; + if (x >= width-2) pxnn = pxn; + + p00 = pypp + pxpp; p01 = pypp + pxp; p02 = pypp + pxc; p03 = pypp + pxn; p04 = pypp + pxnn; + p10 = pyp + pxpp; p11 = pyp + pxp; p12 = pyp + pxc; p13 = pyp + pxn; p14 = pyp + pxnn; + p20 = pyc + pxpp; p21 = pyc + pxp; p22 = pyc + pxc; p23 = pyc + pxn; p24 = pyc + pxnn; + p30 = pyn + pxpp; p31 = pyn + pxp; p32 = pyn + pxc; p33 = pyn + pxn; p34 = pyn + pxnn; + p40 = pynn + pxpp; p41 = pynn + pxp; p42 = pynn + pxc; p43 = pynn + pxn; p44 = pynn + pxnn; + + r = inData[p00] * k00 + inData[p01] * k01 + inData[p02] * k02 + inData[p03] * k04 + inData[p02] * k04 + + inData[p10] * k10 + inData[p11] * k11 + inData[p12] * k12 + inData[p13] * k14 + inData[p12] * k14 + + inData[p20] * k20 + inData[p21] * k21 + inData[p22] * k22 + inData[p23] * k24 + inData[p22] * k24 + + inData[p30] * k30 + inData[p31] * k31 + inData[p32] * k32 + inData[p33] * k34 + inData[p32] * k34 + + inData[p40] * k40 + inData[p41] * k41 + inData[p42] * k42 + inData[p43] * k44 + inData[p42] * k44; + + g = inData[p00+1] * k00 + inData[p01+1] * k01 + inData[p02+1] * k02 + inData[p03+1] * k04 + inData[p02+1] * k04 + + inData[p10+1] * k10 + inData[p11+1] * k11 + inData[p12+1] * k12 + inData[p13+1] * k14 + inData[p12+1] * k14 + + inData[p20+1] * k20 + inData[p21+1] * k21 + inData[p22+1] * k22 + inData[p23+1] * k24 + inData[p22+1] * k24 + + inData[p30+1] * k30 + inData[p31+1] * k31 + inData[p32+1] * k32 + inData[p33+1] * k34 + inData[p32+1] * k34 + + inData[p40+1] * k40 + inData[p41+1] * k41 + inData[p42+1] * k42 + inData[p43+1] * k44 + inData[p42+1] * k44; + + b = inData[p00+2] * k00 + inData[p01+2] * k01 + inData[p02+2] * k02 + inData[p03+2] * k04 + inData[p02+2] * k04 + + inData[p10+2] * k10 + inData[p11+2] * k11 + inData[p12+2] * k12 + inData[p13+2] * k14 + inData[p12+2] * k14 + + inData[p20+2] * k20 + inData[p21+2] * k21 + inData[p22+2] * k22 + inData[p23+2] * k24 + inData[p22+2] * k24 + + inData[p30+2] * k30 + inData[p31+2] * k31 + inData[p32+2] * k32 + inData[p33+2] * k34 + inData[p32+2] * k34 + + inData[p40+2] * k40 + inData[p41+2] * k41 + inData[p42+2] * k42 + inData[p43+2] * k44 + inData[p42+2] * k44; + + if (alpha) { + a = inData[p00+3] * k00 + inData[p01+3] * k01 + inData[p02+3] * k02 + inData[p03+3] * k04 + inData[p02+3] * k04 + + inData[p10+3] * k10 + inData[p11+3] * k11 + inData[p12+3] * k12 + inData[p13+3] * k14 + inData[p12+3] * k14 + + inData[p20+3] * k20 + inData[p21+3] * k21 + inData[p22+3] * k22 + inData[p23+3] * k24 + inData[p22+3] * k24 + + inData[p30+3] * k30 + inData[p31+3] * k31 + inData[p32+3] * k32 + inData[p33+3] * k34 + inData[p32+3] * k34 + + inData[p40+3] * k40 + inData[p41+3] * k41 + inData[p42+3] * k42 + inData[p43+3] * k44 + inData[p42+3] * k44; + } else { + a = inData[idx+3]; + } + + if (mono) { + r = g = b = (r + g + b) / 3; + } + + if (invert) { + r = 255 - r; + g = 255 - g; + b = 255 - b; + } + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + function gaussian(inData, outData, width, height, kernelSize, progress) { + var r, g, b, a, idx, + n = width * height * 4, + x, y, i, j, + inx, iny, w, + tmpData = [], + maxKernelSize = 13, + kernelSize = clamp(kernelSize, 3, maxKernelSize), + k1 = -kernelSize / 2 + (kernelSize % 2 ? 0.5 : 0), + k2 = kernelSize + k1, + weights, + kernels = [[1]], + prog, lastProg = 0; + + + for (i=1;i= width) { + inx = width - 1; + } + + idx = (iny * width + inx) * 4; + + r += inData[idx] * w; + g += inData[idx + 1] * w; + b += inData[idx + 2] * w; + a += inData[idx + 3] * w; + + } + + idx = (y * width + x) * 4; + + tmpData[idx] = r; + tmpData[idx+1] = g; + tmpData[idx+2] = b; + tmpData[idx+3] = a; + + if (progress) { + prog = (idx/n*50 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + + lastProg = 0; + + // pass 2 + for (y=0;y= height) { + iny = height - 1; + } + + idx = (iny * width + inx) * 4; + + r += tmpData[idx] * w; + g += tmpData[idx + 1] * w; + b += tmpData[idx + 2] * w; + a += tmpData[idx + 3] * w; + } + + idx = (y * width + x) * 4; + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = a; + + if (progress) { + prog = 0.5 + (idx/n*50 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + } + + + return { + + invert : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0; + + for (i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + sepia : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + r, g, b; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + solarize : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + r, g, b; + + for (i=0;i 127 ? 255 - r : r; + outData[i+1] = g > 127 ? 255 - g : g; + outData[i+2] = b > 127 ? 255 - b : b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + brightness : function(inData, outData, width, height, options, progress) { + options = defaultOptions(options, { + brightness : 0, + contrast : 0 + }); + + var contrast = clamp(options.contrast, -1, 1) / 2, + brightness = 1 + clamp(options.brightness, -1, 1), + prog, lastProg = 0, + r, g, b, + n = width * height * 4; + + var brightMul = brightness < 0 ? - brightness : brightness; + var brightAdd = brightness < 0 ? 0 : brightness; + + contrast = 0.5 * Math.tan((contrast + 1) * Math.PI/4); + contrastAdd = - (contrast - 0.5) * 255; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + desaturate : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + level; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + lighten : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + mul = 1 + clamp(options.amount, 0, 1); + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + noise : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + prog, lastProg = 0, + amount = clamp(options.amount, 0, 1), + strength = clamp(options.strength, 0, 1), + mono = !!options.mono, + random = Math.random, + rnd, r, g, b; + + for (var i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + flipv : function(inData, outData, width, height, options, progress) { + var inPix, outPix, + n = width * height * 4, + prog, lastProg = 0, + x, y; + for (y=0;y> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + fliph : function(inData, outData, width, height, options, progress) { + var inPix, outPix, + n = width * height * 4, + prog, lastProg = 0, + x, y; + for (y=0;y> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + blur : function(inData, outData, width, height, options, progress) { + gaussian(inData, outData, width, height, options.kernelSize, progress); + }, + + glow : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, r, g, b, + amount = options.amount, + tmpData = [], + gaussProgress, + prog, lastProg = 0; + + if (progress) { + gaussProgress = function(p) { + progress(p * 0.8); + return p; + } + } + + gaussian(inData, tmpData, width, height, options.kernelSize, gaussProgress); + + for (i=0;i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = 0.8 + (i/n*100 >> 0) / 100 * 0.2; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + convolve3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3(inData, outData, width, height, options.kernel, progress); + }, + + convolve5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5(inData, outData, width, height, options.kernel, progress); + }, + + // A 3x3 high-pass filter + sharpen3x3 : function(inData, outData, width, height, options, progress) { + var a = - clamp(options.strength, 0, 1); + convolve3x3( + inData, outData, width, height, + [[a, a, a], + [a, 1-a*8, a], + [a, a, a]], + progress + ); + }, + + // A 5x5 high-pass filter + sharpen5x5 : function(inData, outData, width, height, options, progress) { + var a = - clamp(options.strength, 0, 1); + convolve5x5( + inData, outData, width, height, + [[a, a, a, a, a], + [a, a, a, a, a], + [a, a, 1-a*24, a, a], + [a, a, a, a, a], + [a, a, a, a, a]], + progress + ); + }, + + // A 3x3 low-pass mean filter + soften3x3 : function(inData, outData, width, height, options, progress) { + var c = 1/9; + convolve3x3( + inData, outData, width, height, + [[c, c, c], + [c, c, c], + [c, c, c]], + progress + ); + }, + + // A 5x5 low-pass mean filter + soften5x5 : function(inData, outData, width, height, options, progress) { + var c = 1/25; + convolve5x5( + inData, outData, width, height, + [[c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c], + [c, c, c, c, c]], + progress + ); + }, + + // A 3x3 Cross edge-detect + crossedges : function(inData, outData, width, height, options, progress) { + var a = clamp(options.strength, 0, 1) * 5; + convolve3x3( + inData, outData, width, height, + [[ 0, -a, 0], + [-a, 0, a], + [ 0, a, 0]], + progress, + false, true + ); + }, + + // 3x3 directional emboss + emboss : function(inData, outData, width, height, options, progress) { + var amount = options.amount, + angle = options.angle, + x = Math.cos(-angle) * amount, + y = Math.sin(-angle) * amount, + n = width * height * 4, + + a00 = -x - y, + a10 = -x, + a20 = y - x, + a01 = -y, + a21 = y, + a02 = -y + x, + a12 = x, + a22 = y + x, + + tmpData = [], + + prog, lastProg = 0, + convProgress; + + if (progress) { + convProgress = function(p) { + progress(p * 0.5); + return p; + }; + } + + convolve3x3( + inData, tmpData, width, height, + [[a00, a01, a02], + [a10, 0, a12], + [a20, a21, a22]] + ); + + for (var i=0;i> 0) / 100 * 0.5; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + + // A 3x3 Sobel edge detect (similar to Photoshop's) + findedges : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, + data1 = [], + data2 = [], + gr1, gr2, gg1, gg2, gb1, gb2, + prog, lastProg = 0, + convProgress1, convProgress2; + + if (progress) { + convProgress1 = function(p) { + progress(p * 0.4); + return p; + }; + convProgress2 = function(p) { + progress(0.4 + p * 0.4); + return p; + }; + } + + convolve3x3(inData, data1, width, height, + [[-1, 0, 1], + [-2, 0, 2], + [-1, 0, 1]] + ); + convolve3x3(inData, data2, width, height, + [[-1, -2, -1], + [ 0, 0, 0], + [ 1, 2, 1]] + ); + + for (i=0;i> 0) / 100 * 0.2; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + // A 3x3 edge enhance + edgeenhance3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3( + inData, outData, width, height, + [[-1/9, -1/9, -1/9], + [-1/9, 17/9, -1/9], + [-1/9, -1/9, -1/9]], + progress + ); + }, + + // A 5x5 edge enhance + edgeenhance5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5( + inData, outData, width, height, + [[-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, 49/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25], + [-1/25, -1/25, -1/25, -1/25, -1/25]], + progress + ); + }, + + // A 3x3 Laplacian edge-detect + laplace3x3 : function(inData, outData, width, height, options, progress) { + convolve3x3( + inData, outData, width, height, + [[-1, -1, -1], + [-1, 8, -1], + [-1, -1, -1]], + progress, + false, true, true + ); + }, + + // A 5x5 Laplacian edge-detect + laplace5x5 : function(inData, outData, width, height, options, progress) { + convolve5x5( + inData, outData, width, height, + [[-1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1], + [-1, -1, 24, -1, -1], + [-1, -1, -1, -1, -1], + [-1, -1, -1, -1, -1]], + progress, + false, true, true + ); + }, + + coloradjust : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + r, g, b, + prog, lastProg = 0, + ar = clamp(options.r, -1, 1) * 255, + ag = clamp(options.g, -1, 1) * 255, + ab = clamp(options.b, -1, 1) * 255; + + for (var i=0;i 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + colorfilter : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + i, r, g, b, + luminosity = !!options.luminosity, + prog, lastProg = 0, + min, max, h, l, h1, chroma, tmp, m, + ar = clamp(options.r, 0, 1), + ag = clamp(options.g, 0, 1), + ab = clamp(options.b, 0, 1); + + for (i=0;i max) max = g; + if (b > max) max = b; + if (g < min) min = g; + if (b < min) min = b; + chroma = (max - min); + + if (r == max) { + h = ((g - b) / chroma) % 6; + } else if (g == max) { + h = ((b - r) / chroma) + 2; + } else { + h = ((r - g) / chroma) + 4; + } + + h1 = h >> 0; + tmp = chroma * (h - h1); + r = g = b = l - (r * 0.3 + g * 0.59 + b * 0.11); + + if (h1 == 0) { + r += chroma; + g += tmp; + } else if (h1 == 1) { + r += chroma - tmp; + g += chroma; + } else if (h1 == 2) { + g += chroma; + b += tmp; + } else if (h1 == 3) { + g += chroma - tmp; + b += chroma; + } else if (h1 == 4) { + r += tmp; + b += chroma; + } else if (h1 == 5) { + r += chroma; + b += chroma - tmp; + } + } + + outData[i] = r * 255; + outData[i+1] = g * 255; + outData[i+2] = b * 255; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + hsl : function(inData, outData, width, height, options, progress) { + var n = width * height * 4, + hue = clamp(options.hue, -1, 1), + saturation = clamp(options.saturation, -1, 1), + lightness = clamp(options.lightness, -1, 1), + satMul = 1 + saturation * (saturation < 0 ? 1 : 2), + lightMul = lightness < 0 ? 1 + lightness : 1 - lightness, + lightAdd = lightness < 0 ? 0 : lightness * 255, + vs, ms, vm, h, s, l, v, m, vmh, sextant, + prog, lastProg = 0; + + hue = (hue * 6) % 6; + + for (var i=0;i vs) vs = g; + if (b > vs) vs = b; + ms = r; + if (g < ms) ms = g; + if (b < ms) ms = b; + vm = (vs-ms); + l = (ms+vs)/510; + + if (l > 0 && vm > 0) { + if (l <= 0.5) { + s = vm / (vs+ms) * satMul; + if (s > 1) s = 1; + v = (l * (1+s)); + } else { + s = vm / (510-vs-ms) * satMul; + if (s > 1) s = 1; + v = (l+s - l*s); + } + if (r == vs) { + if (g == ms) { + h = 5 + ((vs-b)/vm) + hue; + } else { + h = 1 - ((vs-g)/vm) + hue; + } + } else if (g == vs) { + if (b == ms) { + h = 1 + ((vs-r)/vm) + hue; + } else { + h = 3 - ((vs-b)/vm) + hue; + } + } else { + if (r == ms) { + h = 3 + ((vs-g)/vm) + hue; + } else { + h = 5 - ((vs-r)/vm) + hue; + } + } + if (h < 0) h += 6; + if (h >= 6) h -= 6; + m = (l + l - v); + sextant = h >> 0; + vmh = (v - m) * (h - sextant); + if (sextant == 0) { + r = v; + g = m + vmh; + b = m; + } else if (sextant == 1) { + r = v - vmh; + g = v; + b = m; + } else if (sextant == 2) { + r = m; + g = v; + b = m + vmh; + } else if (sextant == 3) { + r = m; + g = v - vmh; + b = v; + } else if (sextant == 4) { + r = m + vmh; + g = m; + b = v; + } else if (sextant == 5) { + r = v; + g = m; + b = v - vmh; + } + + r *= 255; + g *= 255; + b *= 255; + } + } + + r = r * lightMul + lightAdd; + g = g * lightMul + lightAdd; + b = b * lightMul + lightAdd; + + if (r < 0) r = 0; + if (g < 0) g = 0; + if (b < 0) b = 0; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + + outData[i] = r; + outData[i+1] = g; + outData[i+2] = b; + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + }, + + posterize : function(inData, outData, width, height, options, progress) { + var numLevels = clamp(options.levels, 2, 256), + numAreas = 256 / numLevels, + numValues = 256 / (numLevels-1), + r, g, b, + n = width * height * 4, + prog, lastProg = 0; + + for (i=0;i>0); + outData[i+1] = numValues * ((inData[i+1] / numAreas)>>0); + outData[i+2] = numValues * ((inData[i+2] / numAreas)>>0); + + outData[i+3] = inData[i+3]; + + if (progress) { + prog = (i/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + + }, + + removenoise : function(inData, outData, width, height, options, progress) { + var r, g, b, c, y, x, idx, + pyc, pyp, pyn, + pxc, pxp, pxn, + minR, minG, minB, maxR, maxG, maxB, + n, prog, lastProg = 0; + + n = width * height * 4; + + for (y=0;y= width-1) pyn = pyc; + + for (x=0;x= width-1) pxn = pxc; + + minR = maxR = inData[pyc + pxp]; + c = inData[pyc + pxn]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + c = inData[pyp + pxc]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + c = inData[pyn + pxc]; + if (c < minR) minR = c; + if (c > maxR) maxR = c; + + minG = inData[pyc + pxp + 1]; + c = inData[pyc + pxn + 1]; + if (c < minG) minG = c; + c = inData[pyp + pxc + 1]; + if (c < minG) minG = c; + c = inData[pyn + pxc + 1]; + if (c < minG) minG = c; + + minB = inData[pyc + pxp + 2]; + c = inData[pyc + pxn + 2]; + if (c < minB) minB = c; + c = inData[pyp + pxc + 2]; + if (c < minB) minB = c; + c = inData[pyn + pxc + 2]; + if (c < minB) minB = c; + + r = inData[idx]; + g = inData[idx + 1]; + b = inData[idx + 2]; + + if (r < minR) r = minR; + if (r > maxR) r = maxR; + if (g < minG) g = minG; + if (g > maxG) g = maxG; + if (b < minB) b = minB; + if (b > maxB) b = maxB; + + outData[idx] = r; + outData[idx+1] = g; + outData[idx+2] = b; + outData[idx+3] = inData[idx+3]; + + if (progress) { + prog = (idx/n*100 >> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }, + + mosaic : function(inData, outData, width, height, options, progress) { + + var blockSize = clamp(options.blockSize, 1, Math.max(width, height)), + yBlocks = Math.ceil(height / blockSize), + xBlocks = Math.ceil(width / blockSize), + y0, y1, x0, x1, idx, pidx, + n = yBlocks * xBlocks, + prog, lastProg = 0; + + for (i=0, y0=0, bidx=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + y0 = y1; + } + }, + + equalize : function(inData, outData, width, height, options, progress) { + var n = width * height, p, i, level, ratio, + prog, lastProg; + var round = Math.round; + // build histogram + var pdf = new Array(256); + for (i=0;i<256;i++) { + pdf[i] = 0; + } + + for (i=0;i> 0) / 100; + if (prog > lastProg) { + lastProg = progress(prog); + } + } + } + } + }; + +})(); + + +Pixastic.Worker = function() { + var me = this; + function processMessage(data) { + var queue = data.queue, + inData = data.inData, + outData = data.outData, + width = data.width, + height = data.height, + tmpData; + + for (var i=0;i 0) { + tmpData = inData; + inData = outData; + outData = tmpData; + } + + Pixastic.Effects[e](inData.data, outData.data, width, height, options); + + me.onmessage({ + data : { + event : "progress", + data : (i+1) / queue.length + } + }); + } + + me.onmessage({ + data : { + event : "done", + data : outData + } + }); + } + + this.postMessage = function(data) { + setTimeout(function() { + processMessage(data) + }, 0); + }; + + this.onmessage = function() {}; + +}; + +/* END pixastic */ diff --git a/files_reader/vendor/sindresorhus/screenfull.js b/files_reader/vendor/sindresorhus/screenfull.js new file mode 100644 index 0000000..ceeb465 --- /dev/null +++ b/files_reader/vendor/sindresorhus/screenfull.js @@ -0,0 +1,145 @@ +/*! +* screenfull +* v3.0.0 - 2015-11-24 +* (c) Sindre Sorhus; MIT License +*/ +(function () { + 'use strict'; + + var isCommonjs = typeof module !== 'undefined' && module.exports; + var keyboardAllowed = typeof Element !== 'undefined' && 'ALLOW_KEYBOARD_INPUT' in Element; + + var fn = (function () { + var val; + var valLength; + + var fnMap = [ + [ + 'requestFullscreen', + 'exitFullscreen', + 'fullscreenElement', + 'fullscreenEnabled', + 'fullscreenchange', + 'fullscreenerror' + ], + // new WebKit + [ + 'webkitRequestFullscreen', + 'webkitExitFullscreen', + 'webkitFullscreenElement', + 'webkitFullscreenEnabled', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + // old WebKit (Safari 5.1) + [ + 'webkitRequestFullScreen', + 'webkitCancelFullScreen', + 'webkitCurrentFullScreenElement', + 'webkitCancelFullScreen', + 'webkitfullscreenchange', + 'webkitfullscreenerror' + + ], + [ + 'mozRequestFullScreen', + 'mozCancelFullScreen', + 'mozFullScreenElement', + 'mozFullScreenEnabled', + 'mozfullscreenchange', + 'mozfullscreenerror' + ], + [ + 'msRequestFullscreen', + 'msExitFullscreen', + 'msFullscreenElement', + 'msFullscreenEnabled', + 'MSFullscreenChange', + 'MSFullscreenError' + ] + ]; + + var i = 0; + var l = fnMap.length; + var ret = {}; + + for (; i < l; i++) { + val = fnMap[i]; + if (val && val[1] in document) { + for (i = 0, valLength = val.length; i < valLength; i++) { + ret[fnMap[0][i]] = val[i]; + } + return ret; + } + } + + return false; + })(); + + var screenfull = { + request: function (elem) { + var request = fn.requestFullscreen; + + elem = elem || document.documentElement; + + // Work around Safari 5.1 bug: reports support for + // keyboard in fullscreen even though it doesn't. + // Browser sniffing, since the alternative with + // setTimeout is even worse. + if (/5\.1[\.\d]* Safari/.test(navigator.userAgent)) { + elem[request](); + } else { + elem[request](keyboardAllowed && Element.ALLOW_KEYBOARD_INPUT); + } + }, + exit: function () { + document[fn.exitFullscreen](); + }, + toggle: function (elem) { + if (this.isFullscreen) { + this.exit(); + } else { + this.request(elem); + } + }, + raw: fn + }; + + if (!fn) { + if (isCommonjs) { + module.exports = false; + } else { + window.screenfull = false; + } + + return; + } + + Object.defineProperties(screenfull, { + isFullscreen: { + get: function () { + return Boolean(document[fn.fullscreenElement]); + } + }, + element: { + enumerable: true, + get: function () { + return document[fn.fullscreenElement]; + } + }, + enabled: { + enumerable: true, + get: function () { + // Coerce to boolean in case of old WebKit + return Boolean(document[fn.fullscreenEnabled]); + } + } + }); + + if (isCommonjs) { + module.exports = screenfull; + } else { + window.screenfull = screenfull; + } +})();