mirror of
https://github.com/futurepress/epub.js.git
synced 2025-10-03 14:59:18 +02:00
Add Storage
This commit is contained in:
parent
14af4539f5
commit
363be16fd9
11 changed files with 590 additions and 12 deletions
97
examples/offline.html
Normal file
97
examples/offline.html
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>EPUB.js Storage Example</title>
|
||||||
|
|
||||||
|
<script src="../node_modules/jszip/dist/jszip.min.js"></script>
|
||||||
|
<script src="../node_modules/localforage/dist/localforage.min.js"></script>
|
||||||
|
<script src="../dist/epub.js"></script>
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="examples.css">
|
||||||
|
|
||||||
|
<style type="text/css">
|
||||||
|
|
||||||
|
#offline {
|
||||||
|
position: fixed;
|
||||||
|
top: -40px;
|
||||||
|
left: 0;
|
||||||
|
background-color: yellow;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
transition: top .5s;
|
||||||
|
z-index: 99;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="offline">You are offline. Loading from Storage.</div>
|
||||||
|
<div id="viewer" class="spreads"></div>
|
||||||
|
<div id="prev" class="arrow">‹</div>
|
||||||
|
<div id="next" class="arrow">›</div>
|
||||||
|
<script>
|
||||||
|
var book = ePub("https://s3.amazonaws.com/moby-dick/", {
|
||||||
|
store: "epubjs-test"
|
||||||
|
});
|
||||||
|
|
||||||
|
var rendition = book.renderTo("viewer", {
|
||||||
|
width: "100%",
|
||||||
|
height: 600
|
||||||
|
});
|
||||||
|
|
||||||
|
var displayed = rendition.display();
|
||||||
|
|
||||||
|
displayed.then(function(renderer){
|
||||||
|
// Add all resources to the store
|
||||||
|
// Add `true` to force re-saving resources
|
||||||
|
book.storage.add(book.resources, true).then(() => {
|
||||||
|
console.log("stored");
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var next = document.getElementById("next");
|
||||||
|
next.addEventListener("click", function(){
|
||||||
|
rendition.next();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
var prev = document.getElementById("prev");
|
||||||
|
prev.addEventListener("click", function(){
|
||||||
|
rendition.prev();
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
var keyListener = function(e){
|
||||||
|
|
||||||
|
// Left Key
|
||||||
|
if ((e.keyCode || e.which) == 37) {
|
||||||
|
rendition.prev();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Right Key
|
||||||
|
if ((e.keyCode || e.which) == 39) {
|
||||||
|
rendition.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
rendition.on("keyup", keyListener);
|
||||||
|
document.addEventListener("keyup", keyListener, false);
|
||||||
|
|
||||||
|
var msg = document.getElementById('offline');
|
||||||
|
book.storage.on("online", function () {
|
||||||
|
console.log("online");
|
||||||
|
msg.style.top = "-40px";
|
||||||
|
});
|
||||||
|
|
||||||
|
book.storage.on("offline", function () {
|
||||||
|
console.log("offline");
|
||||||
|
msg.style.top = "0px";
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
18
package-lock.json
generated
18
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "epubjs",
|
"name": "epubjs",
|
||||||
"version": "0.3.74",
|
"version": "0.3.75",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -49,6 +49,14 @@
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/localforage": {
|
||||||
|
"version": "0.0.34",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/localforage/-/localforage-0.0.34.tgz",
|
||||||
|
"integrity": "sha1-XjHDLdh5HsS5/z70fJy1Wy0NlDg=",
|
||||||
|
"requires": {
|
||||||
|
"localforage": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "10.12.0",
|
"version": "10.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.0.tgz",
|
||||||
|
@ -10804,6 +10812,14 @@
|
||||||
"json5": "^0.5.0"
|
"json5": "^0.5.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"localforage": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/localforage/-/localforage-1.7.2.tgz",
|
||||||
|
"integrity": "sha1-+kRCYC+Abt0rympUq05lbwMfEhw=",
|
||||||
|
"requires": {
|
||||||
|
"lie": "3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"locate-path": {
|
"locate-path": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
|
||||||
|
|
|
@ -75,13 +75,15 @@
|
||||||
"webpack-dev-server": "^2.11.2"
|
"webpack-dev-server": "^2.11.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/jszip": "^3.1.4",
|
||||||
|
"@types/localforage": "0.0.34",
|
||||||
"event-emitter": "^0.3.5",
|
"event-emitter": "^0.3.5",
|
||||||
"jszip": "^3.1.5",
|
"jszip": "^3.1.5",
|
||||||
|
"localforage": "^1.7.2",
|
||||||
"lodash": "^4.17.10",
|
"lodash": "^4.17.10",
|
||||||
"marks-pane": "^1.0.9",
|
"marks-pane": "^1.0.9",
|
||||||
"path-webpack": "0.0.3",
|
"path-webpack": "0.0.3",
|
||||||
"stream-browserify": "^2.0.1",
|
"stream-browserify": "^2.0.1",
|
||||||
"xmldom": "^0.1.27",
|
"xmldom": "^0.1.27"
|
||||||
"@types/jszip": "^3.1.4"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
77
src/book.js
77
src/book.js
|
@ -13,6 +13,7 @@ import Rendition from "./rendition";
|
||||||
import Archive from "./archive";
|
import Archive from "./archive";
|
||||||
import request from "./utils/request";
|
import request from "./utils/request";
|
||||||
import EpubCFI from "./epubcfi";
|
import EpubCFI from "./epubcfi";
|
||||||
|
import Store from "./store";
|
||||||
import { EPUBJS_VERSION, EVENTS } from "./utils/constants";
|
import { EPUBJS_VERSION, EVENTS } from "./utils/constants";
|
||||||
|
|
||||||
const CONTAINER_PATH = "META-INF/container.xml";
|
const CONTAINER_PATH = "META-INF/container.xml";
|
||||||
|
@ -39,6 +40,7 @@ const INPUT_TYPE = {
|
||||||
* @param {string} [options.replacements=none] use base64, blobUrl, or none for replacing assets in archived Epubs
|
* @param {string} [options.replacements=none] use base64, blobUrl, or none for replacing assets in archived Epubs
|
||||||
* @param {method} [options.canonical] optional function to determine canonical urls for a path
|
* @param {method} [options.canonical] optional function to determine canonical urls for a path
|
||||||
* @param {string} [options.openAs] optional string to determine the input type
|
* @param {string} [options.openAs] optional string to determine the input type
|
||||||
|
* @param {string} [options.store=false] cache the contents in local storage, value should be the name of the reader
|
||||||
* @returns {Book}
|
* @returns {Book}
|
||||||
* @example new Book("/path/to/book.epub", {})
|
* @example new Book("/path/to/book.epub", {})
|
||||||
* @example new Book({ replacements: "blobUrl" })
|
* @example new Book({ replacements: "blobUrl" })
|
||||||
|
@ -60,7 +62,8 @@ class Book {
|
||||||
encoding: undefined,
|
encoding: undefined,
|
||||||
replacements: undefined,
|
replacements: undefined,
|
||||||
canonical: undefined,
|
canonical: undefined,
|
||||||
openAs: undefined
|
openAs: undefined,
|
||||||
|
store: undefined
|
||||||
});
|
});
|
||||||
|
|
||||||
extend(this.settings, options);
|
extend(this.settings, options);
|
||||||
|
@ -173,6 +176,13 @@ class Book {
|
||||||
*/
|
*/
|
||||||
this.archive = undefined;
|
this.archive = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @member {Store} storage
|
||||||
|
* @memberof Book
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.storage = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @member {Resources} resources
|
* @member {Resources} resources
|
||||||
* @memberof Book
|
* @memberof Book
|
||||||
|
@ -202,6 +212,9 @@ class Book {
|
||||||
this.packaging = undefined;
|
this.packaging = undefined;
|
||||||
|
|
||||||
// this.toc = undefined;
|
// this.toc = undefined;
|
||||||
|
if (this.settings.store) {
|
||||||
|
this.store();
|
||||||
|
}
|
||||||
|
|
||||||
if(url) {
|
if(url) {
|
||||||
this.open(url, this.settings.openAs).catch((error) => {
|
this.open(url, this.settings.openAs).catch((error) => {
|
||||||
|
@ -233,7 +246,7 @@ class Book {
|
||||||
} else if (type === INPUT_TYPE.EPUB) {
|
} else if (type === INPUT_TYPE.EPUB) {
|
||||||
this.archived = true;
|
this.archived = true;
|
||||||
this.url = new Url("/", "");
|
this.url = new Url("/", "");
|
||||||
opening = this.request(input, "binary",this.settings.requestCredentials)
|
opening = this.request(input, "binary", this.settings.requestCredentials)
|
||||||
.then(this.openEpub.bind(this));
|
.then(this.openEpub.bind(this));
|
||||||
} else if(type == INPUT_TYPE.OPF) {
|
} else if(type == INPUT_TYPE.OPF) {
|
||||||
this.url = new Url(input);
|
this.url = new Url(input);
|
||||||
|
@ -318,13 +331,10 @@ class Book {
|
||||||
* @return {Promise} returns a promise with the requested resource
|
* @return {Promise} returns a promise with the requested resource
|
||||||
*/
|
*/
|
||||||
load(path) {
|
load(path) {
|
||||||
var resolved;
|
var resolved = this.resolve(path);
|
||||||
|
|
||||||
if(this.archived) {
|
if(this.archived) {
|
||||||
resolved = this.resolve(path);
|
|
||||||
return this.archive.request(resolved);
|
return this.archive.request(resolved);
|
||||||
} else {
|
} else {
|
||||||
resolved = this.resolve(path);
|
|
||||||
return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders);
|
return this.request(resolved, null, this.settings.requestCredentials, this.settings.requestHeaders);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -558,6 +568,61 @@ class Book {
|
||||||
return this.archive.open(input, encoding);
|
return this.archive.open(input, encoding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the epubs contents
|
||||||
|
* @private
|
||||||
|
* @param {binary} input epub data
|
||||||
|
* @param {string} [encoding]
|
||||||
|
* @return {Store}
|
||||||
|
*/
|
||||||
|
store() {
|
||||||
|
// Use "blobUrl" or "base64" for replacements
|
||||||
|
let replacementsSetting = this.settings.replacements && this.settings.replacements !== "none";
|
||||||
|
// Save original url
|
||||||
|
let originalUrl = this.url;
|
||||||
|
// Save original request method
|
||||||
|
let requester = this.settings.requestMethod || request.bind(this);
|
||||||
|
// Create new Store
|
||||||
|
this.storage = new Store(this.settings.store, requester, this.resolve.bind(this));
|
||||||
|
// Replace request method to go through store
|
||||||
|
this.request = this.storage.request.bind(this.storage);
|
||||||
|
|
||||||
|
this.opened.then(() => {
|
||||||
|
if (this.archived) {
|
||||||
|
this.storage.requester = this.archive.request.bind(this.archive);
|
||||||
|
}
|
||||||
|
// Substitute hook
|
||||||
|
let substituteResources = (output, section) => {
|
||||||
|
section.output = this.resources.substitute(output, section.url);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set to use replacements
|
||||||
|
this.resources.settings.replacements = replacementsSetting || "blobUrl";
|
||||||
|
// Create replacement urls
|
||||||
|
this.resources.replacements().
|
||||||
|
then(() => {
|
||||||
|
return this.resources.replaceCss();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.storage.on("offline", () => {
|
||||||
|
// Remove url to use relative resolving for hrefs
|
||||||
|
this.url = new Url("/", "");
|
||||||
|
// Add hook to replace resources in contents
|
||||||
|
this.spine.hooks.serialize.register(substituteResources);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.storage.on("online", () => {
|
||||||
|
// Restore original url
|
||||||
|
this.url = originalUrl;
|
||||||
|
// Remove hook
|
||||||
|
this.spine.hooks.serialize.deregister(substituteResources);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.storage;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the cover url
|
* Get the cover url
|
||||||
* @return {string} coverUrl
|
* @return {string} coverUrl
|
||||||
|
|
|
@ -457,7 +457,7 @@ class DefaultViewManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.bind(this), (err) => {
|
}.bind(this), (err) => {
|
||||||
displaying.reject(err);
|
return err;
|
||||||
})
|
})
|
||||||
.then(function(){
|
.then(function(){
|
||||||
this.views.show();
|
this.views.show();
|
||||||
|
@ -529,7 +529,7 @@ class DefaultViewManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}.bind(this), (err) => {
|
}.bind(this), (err) => {
|
||||||
displaying.reject(err);
|
return err;
|
||||||
})
|
})
|
||||||
.then(function(){
|
.then(function(){
|
||||||
if(this.isPaginated && this.settings.axis === "horizontal") {
|
if(this.isPaginated && this.settings.axis === "horizontal") {
|
||||||
|
|
349
src/store.js
Normal file
349
src/store.js
Normal file
|
@ -0,0 +1,349 @@
|
||||||
|
import {defer, isXml, parse} from "./utils/core";
|
||||||
|
import httpRequest from "./utils/request";
|
||||||
|
import mime from "../libs/mime/mime";
|
||||||
|
import Path from "./utils/path";
|
||||||
|
import EventEmitter from "event-emitter";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles saving and requesting files from local storage
|
||||||
|
* @class
|
||||||
|
*/
|
||||||
|
class Store {
|
||||||
|
|
||||||
|
constructor(name, requester, resolver) {
|
||||||
|
this.urlCache = {};
|
||||||
|
|
||||||
|
this.requester = requester || httpRequest;
|
||||||
|
this.resolver = resolver;
|
||||||
|
|
||||||
|
this.online = true;
|
||||||
|
|
||||||
|
this.checkRequirements();
|
||||||
|
|
||||||
|
// This should be the name of the application for modals
|
||||||
|
localforage.config({
|
||||||
|
name: name || 'epubjs'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.addListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks to see if localForage exists in global namspace,
|
||||||
|
* Requires localForage if it isn't there
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
checkRequirements(){
|
||||||
|
try {
|
||||||
|
if (typeof localForage === "undefined") {
|
||||||
|
let localForage = require("localforage");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error("localForage lib not loaded");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add online and offline event listeners
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
addListeners() {
|
||||||
|
window.addEventListener('online', this.status.bind(this));
|
||||||
|
window.addEventListener('offline', this.status.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove online and offline event listeners
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
removeListeners() {
|
||||||
|
window.removeEventListener('online', this.status.bind(this));
|
||||||
|
window.removeEventListener('offline', this.status.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the online / offline status
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
status(event) {
|
||||||
|
let online = navigator.onLine;
|
||||||
|
this.online = online;
|
||||||
|
if (online) {
|
||||||
|
this.emit("online", this);
|
||||||
|
} else {
|
||||||
|
this.emit("offline", this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all of a book resources to the store
|
||||||
|
* @param {Resources} resources book resources
|
||||||
|
* @param {boolean} [force] force resaving resources
|
||||||
|
* @return {Promise<object>} store objects
|
||||||
|
*/
|
||||||
|
add(resources, force) {
|
||||||
|
let mapped = resources.resources.map((item) => {
|
||||||
|
let { href } = item;
|
||||||
|
let url = this.resolver(href);
|
||||||
|
let encodedUrl = window.encodeURIComponent(url);
|
||||||
|
|
||||||
|
return localforage.getItem(encodedUrl).then((item) => {
|
||||||
|
if (!item || force) {
|
||||||
|
return this.requester(url, "binary")
|
||||||
|
.then((data) => {
|
||||||
|
return localforage.setItem(encodedUrl, data);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
return Promise.all(mapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a url from storage
|
||||||
|
* @param {string} url a url to request from storage
|
||||||
|
* @param {string} [type] specify the type of the returned result
|
||||||
|
* @param {boolean} [withCredentials]
|
||||||
|
* @param {object} [headers]
|
||||||
|
* @return {Promise<Blob | string | JSON | Document | XMLDocument>}
|
||||||
|
*/
|
||||||
|
request(url, type, withCredentials, headers){
|
||||||
|
var deferred = new defer();
|
||||||
|
var response;
|
||||||
|
var path = new Path(url);
|
||||||
|
|
||||||
|
if (this.online) {
|
||||||
|
return this.requester(url, type, withCredentials, headers).then((data) => {
|
||||||
|
// from network
|
||||||
|
let encodedUrl = window.encodeURIComponent(url);
|
||||||
|
|
||||||
|
localforage.getItem(encodedUrl).then((result) => {
|
||||||
|
if (!result) {
|
||||||
|
this.requester(url, "binary", withCredentials, headers).then((data) => {
|
||||||
|
localforage.setItem(encodedUrl, data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// If type isn't set, determine it from the file extension
|
||||||
|
if(!type) {
|
||||||
|
type = path.extension;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(type == "blob"){
|
||||||
|
response = this.getBlob(url);
|
||||||
|
} else {
|
||||||
|
response = this.getText(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return response.then((r) => {
|
||||||
|
var deferred = new defer();
|
||||||
|
var result;
|
||||||
|
if (r) {
|
||||||
|
result = this.handleResponse(r, type);
|
||||||
|
deferred.resolve(result);
|
||||||
|
} else {
|
||||||
|
deferred.reject({
|
||||||
|
message : "File not found in storage: " + url,
|
||||||
|
stack : new Error().stack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return deferred.promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the response from request
|
||||||
|
* @private
|
||||||
|
* @param {any} response
|
||||||
|
* @param {string} [type]
|
||||||
|
* @return {any} the parsed result
|
||||||
|
*/
|
||||||
|
handleResponse(response, type){
|
||||||
|
var r;
|
||||||
|
|
||||||
|
if(type == "json") {
|
||||||
|
r = JSON.parse(response);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(isXml(type)) {
|
||||||
|
r = parse(response, "text/xml");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(type == "xhtml") {
|
||||||
|
r = parse(response, "application/xhtml+xml");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if(type == "html" || type == "htm") {
|
||||||
|
r = parse(response, "text/html");
|
||||||
|
} else {
|
||||||
|
r = response;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a Blob from Storage by Url
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string} [mimeType]
|
||||||
|
* @return {Blob}
|
||||||
|
*/
|
||||||
|
getBlob(url, mimeType){
|
||||||
|
let encodedUrl = window.encodeURIComponent(url);
|
||||||
|
|
||||||
|
return localforage.getItem(encodedUrl).then(function(uint8array) {
|
||||||
|
if(!uint8array) return;
|
||||||
|
|
||||||
|
mimeType = mimeType || mime.lookup(url);
|
||||||
|
|
||||||
|
return new Blob([uint8array], {type : mimeType});
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get Text from Storage by Url
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string} [mimeType]
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
getText(url, mimeType){
|
||||||
|
let encodedUrl = window.encodeURIComponent(url);
|
||||||
|
|
||||||
|
mimeType = mimeType || mime.lookup(url);
|
||||||
|
|
||||||
|
return localforage.getItem(encodedUrl).then(function(uint8array) {
|
||||||
|
var deferred = new defer();
|
||||||
|
var reader = new FileReader();
|
||||||
|
var blob = new Blob([uint8array], {type : mimeType});
|
||||||
|
|
||||||
|
if(!blob) return;
|
||||||
|
|
||||||
|
reader.addEventListener("loadend", () => {
|
||||||
|
deferred.resolve(reader.result);
|
||||||
|
});
|
||||||
|
|
||||||
|
reader.readAsText(blob, mimeType);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a base64 encoded result from Storage by Url
|
||||||
|
* @param {string} url
|
||||||
|
* @param {string} [mimeType]
|
||||||
|
* @return {string} base64 encoded
|
||||||
|
*/
|
||||||
|
getBase64(url, mimeType){
|
||||||
|
let encodedUrl = window.encodeURIComponent(url);
|
||||||
|
|
||||||
|
mimeType = mimeType || mime.lookup(url);
|
||||||
|
|
||||||
|
return localforage.getItem(encodedUrl).then((uint8array) => {
|
||||||
|
var deferred = new defer();
|
||||||
|
var reader = new FileReader();
|
||||||
|
var blob = new Blob([uint8array], {type : mimeType});
|
||||||
|
|
||||||
|
if(!blob) return;
|
||||||
|
|
||||||
|
reader.addEventListener("loadend", () => {
|
||||||
|
deferred.resolve(reader.result);
|
||||||
|
});
|
||||||
|
reader.readAsDataURL(blob, mimeType);
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Url from a stored item
|
||||||
|
* @param {string} url
|
||||||
|
* @param {object} [options.base64] use base64 encoding or blob url
|
||||||
|
* @return {Promise} url promise with Url string
|
||||||
|
*/
|
||||||
|
createUrl(url, options){
|
||||||
|
var deferred = new defer();
|
||||||
|
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||||
|
var tempUrl;
|
||||||
|
var response;
|
||||||
|
var useBase64 = options && options.base64;
|
||||||
|
|
||||||
|
if(url in this.urlCache) {
|
||||||
|
deferred.resolve(this.urlCache[url]);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useBase64) {
|
||||||
|
response = this.getBase64(url);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
response.then(function(tempUrl) {
|
||||||
|
|
||||||
|
this.urlCache[url] = tempUrl;
|
||||||
|
deferred.resolve(tempUrl);
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
response = this.getBlob(url);
|
||||||
|
|
||||||
|
if (response) {
|
||||||
|
response.then(function(blob) {
|
||||||
|
|
||||||
|
tempUrl = _URL.createObjectURL(blob);
|
||||||
|
this.urlCache[url] = tempUrl;
|
||||||
|
deferred.resolve(tempUrl);
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
deferred.reject({
|
||||||
|
message : "File not found in storage: " + url,
|
||||||
|
stack : new Error().stack
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Revoke Temp Url for a achive item
|
||||||
|
* @param {string} url url of the item in the store
|
||||||
|
*/
|
||||||
|
revokeUrl(url){
|
||||||
|
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||||
|
var fromCache = this.urlCache[url];
|
||||||
|
if(fromCache) _URL.revokeObjectURL(fromCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
var _URL = window.URL || window.webkitURL || window.mozURL;
|
||||||
|
for (let fromCache in this.urlCache) {
|
||||||
|
_URL.revokeObjectURL(fromCache);
|
||||||
|
}
|
||||||
|
this.urlCache = {};
|
||||||
|
this.removeListeners();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EventEmitter(Store.prototype);
|
||||||
|
|
||||||
|
export default Store;
|
|
@ -28,6 +28,21 @@ class Hook {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a function
|
||||||
|
* @example this.content.deregister(function(){...});
|
||||||
|
*/
|
||||||
|
deregister(func){
|
||||||
|
let hook;
|
||||||
|
for (let i = 0; i < this.hooks.length; i++) {
|
||||||
|
hook = this.hooks[i];
|
||||||
|
if (hook === func) {
|
||||||
|
this.hooks.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Triggers a hook to run all functions
|
* Triggers a hook to run all functions
|
||||||
* @example this.content.trigger(args).then(function(){...});
|
* @example this.content.trigger(args).then(function(){...});
|
||||||
|
|
|
@ -77,6 +77,12 @@ class Path {
|
||||||
* @returns {string} relative
|
* @returns {string} relative
|
||||||
*/
|
*/
|
||||||
relative (what) {
|
relative (what) {
|
||||||
|
var isAbsolute = what && (what.indexOf("://") > -1);
|
||||||
|
|
||||||
|
if (isAbsolute) {
|
||||||
|
return what;
|
||||||
|
}
|
||||||
|
|
||||||
return path.relative(this.directory, what);
|
return path.relative(this.directory, what);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
types/store.d.ts
vendored
Normal file
26
types/store.d.ts
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import localForage = require('localforage');
|
||||||
|
import Resources from "./resources";
|
||||||
|
|
||||||
|
export default class Store {
|
||||||
|
constructor();
|
||||||
|
|
||||||
|
add(resources: Resources, force?: boolean): Promise<Array<object>>;
|
||||||
|
|
||||||
|
request(url: string, type?: string, withCredentials?: boolean, headers?: object): Promise<Blob | string | JSON | Document | XMLDocument>;
|
||||||
|
|
||||||
|
getBlob(url: string, mimeType?: string): Promise<Blob>;
|
||||||
|
|
||||||
|
getText(url: string): Promise<string>;
|
||||||
|
|
||||||
|
getBase64(url: string, mimeType?: string): Promise<string>;
|
||||||
|
|
||||||
|
createUrl(url: string, options: { base64: boolean }): Promise<string>;
|
||||||
|
|
||||||
|
revokeUrl(url: string): void;
|
||||||
|
|
||||||
|
destroy(): void;
|
||||||
|
|
||||||
|
private checkRequirements(): void;
|
||||||
|
|
||||||
|
private handleResponse(response: any, type?: string): Blob | string | JSON | Document | XMLDocument;
|
||||||
|
}
|
2
types/utils/hook.d.ts
vendored
2
types/utils/hook.d.ts
vendored
|
@ -8,6 +8,8 @@ export default class Hook {
|
||||||
register(func: Function): void;
|
register(func: Function): void;
|
||||||
register(arr: Array<Function>): void;
|
register(arr: Array<Function>): void;
|
||||||
|
|
||||||
|
deregister(func: Function): void;
|
||||||
|
|
||||||
trigger(...args: any[]): Promise<any>;
|
trigger(...args: any[]): Promise<any>;
|
||||||
|
|
||||||
list(): Array<any>;
|
list(): Array<any>;
|
||||||
|
|
2
types/utils/request.d.ts
vendored
2
types/utils/request.d.ts
vendored
|
@ -1 +1 @@
|
||||||
export default function request(url: string, type: string, withCredentials: boolean, headers: object): Promise<Blob | string | JSON | Document | XMLDocument>;
|
export default function request(url: string, type?: string, withCredentials?: boolean, headers?: object): Promise<Blob | string | JSON | Document | XMLDocument>;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue