cordova-app-loader
Advanced tools
Comparing version 0.5.1 to 0.5.2
@@ -112,2 +112,3 @@ (function(){ | ||
//--------------------------------------------------------------------- | ||
window.Manifest = {}; | ||
// Step 1: Load manifest from localStorage | ||
@@ -114,0 +115,0 @@ var manifest = JSON.parse(localStorage.getItem('manifest')); |
{ | ||
"name": "cordova-app-loadaer", | ||
"main": "www/lib/CordovaAppLoader.js", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"homepage": "https://github.com/markmarijnissen/cordova-file-cache", | ||
@@ -6,0 +6,0 @@ "authors": [ |
@@ -29,2 +29,3 @@ var CordovaFileCache = require('cordova-file-cache'); | ||
// initialize a file cache | ||
if(options.mode) options.mode = 'mirror'; | ||
this.cache = new CordovaFileCache(options); | ||
@@ -31,0 +32,0 @@ |
{ | ||
"name": "cordova-app-loader", | ||
"version": "0.5.1", | ||
"version": "0.5.2", | ||
"description": "Cordova App Loader - remote update your cordova app", | ||
@@ -10,3 +10,3 @@ "main": "index.js", | ||
"bootstrap": "uglifyjs -m --screw-ie8 bootstrap.js > www/bootstrap.js", | ||
"autoupdate": "webpack autoupdate.js www/autoupdate.js -p", | ||
"autoupdate": "webpack autoupdate.js www/autoupdate.js", | ||
"prepublish": "npm run autoupdate && npm run bootstrap && npm run cordova-promise-fs && npm run cordova-app-loader", | ||
@@ -26,6 +26,3 @@ "test": "echo \"Error: no test specified\" && exit 1" | ||
"cordova-file-cache": "^0.5.0" | ||
}, | ||
"devDependencies": { | ||
"promiscuous": "^0.6.0" | ||
} | ||
} |
124
README.md
@@ -18,3 +18,2 @@ cordova-app-loader | ||
Based on [cordova-promise-fs](https://github.com/markmarijnissen/cordova-promise-fs) and [cordova-file-cache](https://github.com/markmarijnissen/cordova-file-cache). | ||
@@ -40,6 +39,23 @@ ## Demo Time! | ||
### Setup Cordova: | ||
```bash | ||
cordova platform add ios@3.7.0 | ||
cordova plugin add org.apache.cordova.file | ||
cordova plugin add org.apache.cordova.file-transfer | ||
``` | ||
**IMPORTANT:** For iOS, use Cordova 3.7.0 or higher (due to a [bug](https://github.com/AppGyver/steroids/issues/534) that affects requestFileSystem). | ||
### Get javascript: | ||
You can download the files (see "Quickstart" or "Usage") or use NPM or bower: | ||
You need: | ||
* bootstrap.js ([github](https://github.com/markmarijnissen/cordova-app-loader/), [file](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/bootstrap.js)): reads the manifest.json to start your app. | ||
* cordova-app-loader ([github](https://github.com/markmarijnissen/cordova-app-loader/), [file]((https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/lib/CordovaAppLoader.js))): to check,download and update a **manifest.json** | ||
* cordova-promise-fs ([github](https://github.com/markmarijnissen/cordova-promise-fs), [file](https://github.com/markmarijnissen/cordova-app-loader/blob/master/www/lib/CordovaPromiseFS.js)): to deal with the FileSystem | ||
* Promise libary such as bluebird ([github](https://github.com/petkaantonov/bluebird), [file](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/lib/bluebird.js)) or promiscuous ([github](https://github.com/RubenVerborgh/promiscuous),[file](https://raw.githubusercontent.com/RubenVerborgh/promiscuous/master/promiscuous.js)) | ||
CordovaAppLoader uses [cordova-file-cache](https://github.com/markmarijnissen/cordova-file-cache) to download and cache the files. | ||
```bash | ||
@@ -50,40 +66,67 @@ bower install cordova-app-loader cordova-promise-fs bluebird | ||
### Setup Cordova: | ||
### Getting started | ||
```bash | ||
cordova platform add ios@3.7.0 | ||
cordova plugin add org.apache.cordova.file | ||
cordova plugin add org.apache.cordova.file-transfer | ||
1. Write a **manifest.json** (see below) | ||
2. The included **bootstrap.js** script will load your CSS and JS. | ||
3. Use **CordovaAppLoader** to `check()`, `download()` and `apply()` updates: | ||
```javascript | ||
// When using NPM, require these first. | ||
// When using the ready-made files, these are available as global variables. | ||
var CordovaPromiseFS = require('cordova-promise-fs'); | ||
var CordovaAppLoader = require('cordova-app-loader'); | ||
var Promise = require('bluebird'); | ||
// Initialize a FileSystem | ||
var fs = new CordovaPromiseFS({ | ||
Promise: Promise | ||
}); | ||
// Initialize a CordovaAppLoader | ||
var loader = new CordovaAppLoader({ | ||
fs: fs, | ||
serverRoot: 'http://data.madebymark.nl/cordova-app-loader/' | ||
}); | ||
// Write your own "check", "download" and "update" logic: | ||
loader.check() | ||
.then(function(){ | ||
return loader.download(); | ||
}).then(function(){ | ||
return loader.update(); | ||
}) | ||
``` | ||
**IMPORTANT:** For iOS, use Cordova 3.7.0 or higher (due to a [bug](https://github.com/AppGyver/steroids/issues/534) that affects requestFileSystem). | ||
See [an example](https://github.com/markmarijnissen/cordova-app-loader/blob/master/autoupdate.js). | ||
## Quickstart: A simple example | ||
### Quickstart | ||
Download [index.html](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/autoupdate.html) and [autoupdate.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/autoupdate.js). | ||
If you don't need full control, you can use a drop-in solution: [autoupdate.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/autoupdate.js) | ||
1. Write a **manifest.json** (see "step 1" below) | ||
2. Copy **index.html** and **autoupdate.js** to your `www` folder. | ||
3. Launch your app. | ||
4. Upload a new **manifest.json** (+ files) to your server. | ||
5. Reopen your app to download and apply the update. | ||
[autoupdate.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/autoupdate.js) includes all four files ([bootstrap.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/bootstrap.js), [a Promise library](https://raw.githubusercontent.com/RubenVerborgh/promiscuous/master/promiscuous.js),[CordovaPromiseFS.js](https://github.com/markmarijnissen/cordova-app-loader/blob/master/www/lib/CordovaPromiseFS.js) and [CordovaAppLoader.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/lib/CordovaAppLoader.js)), initializes everything, and then implements very simple update logic: | ||
Make sure you set the correct options in `index.html`: | ||
```html | ||
<script | ||
type="text/javascript" | ||
server="http://data.madebymark.nl/cordova-app-loader/" | ||
manifest="manifest.json" | ||
src="autoupdate.js"></script> | ||
``` | ||
1. When you open or resume the app (or when you switch tabs in Chrome) | ||
2. Check for updates | ||
3. Download files in the background | ||
3. Update automatically when download is ready. | ||
**autoupdate.js** is a very simple usage of the `CordovaAppLoader` ([see code](https://github.com/markmarijnissen/cordova-app-loader/blob/master/autoupdate.js)). It includes all nessecary files (see "Usage") and implementes very simple update logic. | ||
Here is how to get started: | ||
* Whenever you launch or resume the app, | ||
* `check()` for a new manifest | ||
* `download()` files in the background | ||
* `update()` app (reloads page) | ||
1. Download [index.html](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/autoupdate.html) to your `www` directory. | ||
2. Download [autoupdate.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/autoupdate.js) to your `www` directory. | ||
3. Make sure you set the correct options in `index.html`: | ||
```html | ||
<script | ||
type="text/javascript" | ||
server="http://data.madebymark.nl/cordova-app-loader/" | ||
manifest="manifest.json" | ||
src="autoupdate.js"></script> | ||
``` | ||
This approach is **not** recommended because: | ||
4. Launch your app. | ||
5. Upload a new **manifest.json** (+ files) to your server. | ||
6. Reopen your app to download and apply the update. | ||
This implementation is **not** recommended because: | ||
* Downloading files in the background can slow down performance (sluggish UI). | ||
@@ -94,9 +137,2 @@ * The update (reload) can interrupt an important user action. | ||
Download and include: | ||
* [bootstrap.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/bootstrap.js) to launch your app. | ||
* A [Promise](https://github.com/petkaantonov/bluebird) library. | ||
* [CordovaPromiseFS.js](https://raw.githubusercontent.com/markmarijnissen/cordova-promise-fs/master/dist/CordovaPromiseFS.js) to handle the filesystem. | ||
* [CordovaAppLoader.js](https://raw.githubusercontent.com/markmarijnissen/cordova-app-loader/master/www/lib/CordovaAppLoader.js) to handle check/download/update logic. | ||
### Overview | ||
@@ -188,8 +224,18 @@ | ||
```javascript | ||
var fs = new CordovaPromiseFS({}); | ||
var loader = window.loader = new CordovaAppLoader({ | ||
// When using NPM, require these first. | ||
// Otherwise these are available as global variables. | ||
var CordovaPromiseFS = require('cordova-promise-fs'); | ||
var CordovaAppLoader = require('cordova-app-loader'); | ||
var Promise = require('bluebird'); | ||
// Initialize a FileSystem | ||
var fs = new CordovaPromiseFS({ | ||
Promise: Promise | ||
}); | ||
// Initialize a CordovaAppLoader | ||
var loader = new CordovaAppLoader({ | ||
fs: fs, | ||
serverRoot: 'http://data.madebymark.nl/cordova-app-loader/', | ||
localRoot: 'app', | ||
mode: 'mirror', // use same directories and filenames as in manifest (instead of using a hash) | ||
cacheBuster: true // make sure we're not downloading cached files. | ||
@@ -196,0 +242,0 @@ checkTimeout: 10000 // timeout for the "check" function - when you loose internet connection |
@@ -1,2 +0,1325 @@ | ||
!function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={exports:{},id:o,loaded:!1};return t[o].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){function o(){c.check().then(function(){return c.download()}).then(function(){return c.update()},function(t){console.error("Auto-update error:",t)})}function r(){document.webkitHidden||o()}n(1);var i=n(2),s=n(5),a=n(6);window.setImmediate=window.setTimeout;var u,c,f,l,d="undefined"!=typeof cordova;if(f=document.querySelector("script[manifest]"),f&&(l=f.getAttribute("server"),!l))throw new Error('Add a "server" attribute to the autoupdate.js script!');u=new s({persistent:d,Promise:a}),c=new i({fs:u,localRoot:"app",serverRoot:l,mode:"mirror",cacheBuster:!0}),o(),u.deviceready.then(function(){document.addEventListener("resume",o)}),document.addEventListener("webkitvisibilitychange",r,!1)},function(){!function(){function t(t,e,n){function o(){var e=s.shift();e&&("/"===e[0]&&(e=e.substr(1)),e=t.root+e,".js"===e.substr(-3)?(r=document.createElement("script"),r.type="text/javascript",r.src=e,r.onload=o):(r=document.createElement("link"),r.rel="stylesheet",r.href=e,r.type="text/css",setTimeout(o,0)),i.appendChild(r))}if(!t.load)return void console.error("Manifest has nothing to load (manifest.load is empty).",t);var r,i=document.getElementsByTagName("head")[0],s=t.load.concat();t.root=t.root||"./",t.root.length>0&&"/"!==t.root[t.root.length-1]&&(t.root+="/"),e||localStorage.setItem("manifest",JSON.stringify(t)),"cdvfile"===t.root.substr(0,7)?document.addEventListener("deviceready",o,!1):o(),window.Manifest=t,e&&setTimeout(function(){window.BOOTSTRAP_OK||(localStorage.removeItem("manifest"),location.reload())},n)}window.pegasus=function(t,e){return e=new XMLHttpRequest,e.open("GET",t),t=[],e.onreadystatechange=e.then=function(n,o,r){n.call&&(t=[n,o]),4==e.readyState&&(r=t[0|e.status/400],r&&r(200===e.status||0===e.status?JSON.parse(e.responseText):e))},e.send(),e};var e=JSON.parse(localStorage.getItem("manifest")),n=document.querySelector("script[manifest]");if(e)t(e,!0,n.getAttribute("timeout")||1e4);else{var o=(n?n.getAttribute("manifest"):null)||"manifest.json";pegasus(o).then(t,function(t){console.error("Could not download "+o+": "+t.status)})}}()},function(t,e,n){function o(t){var e={};return Object.keys(t).forEach(function(n){e[t[n].filename]=t[n]}),e}function r(t){if(!t)throw new Error("CordovaAppLoader has no options!");if(!t.fs)throw new Error('CordovaAppLoader has no "fs" option (cordova-promise-fs)');if(!t.serverRoot)throw new Error('CordovaAppLoader has no "serverRoot" option.');if(!window.pegasus||!window.Manifest)throw new Error("CordovaAppLoader bootstrap.js is missing.");s=t.fs.Promise,this.manifest=window.Manifest,this.newManifest=null,t.serverRoot=t.serverRoot||"",t.serverRoot&&"/"!==t.serverRoot[t.serverRoot.length-1]&&(t.serverRoot+="/"),this.newManifestUrl=t.serverRoot+(t.manifest||"manifest.json"),this.cache=new i(t),this._toBeDeleted=[],this._toBeDownloaded=[],this._updateReady=!1,this._checkTimeout=t.checkTimeout||1e4}var i=n(3),s=null;r.prototype.check=function(t){var e=this,n=this.manifest;return new s(function(r,i){function s(t){e.cache.ready.then(function(){if(!t.files)return void i('Downloaded Manifest has no "files" attribute.');var s=o(t.files),a=o(n.files);e._toBeDownloaded=Object.keys(s).filter(function(t){return!a[t]||a[t].version!==s[t].version||!e.cache.isCached(t)}),e.cache.list().then(function(n){e._toBeDeleted=n.map(function(t){return t.substr(e.cache._localRoot.length)}).filter(function(t){return!s[t]}).concat(e._toBeDownloaded),e._toBeDeleted.length>0||e._toBeDownloaded.length>0?(e.newManifest=t,e.newManifest.root=e.cache.toInternalURL("/")+(e.newManifest.root||""),r(!0)):r(!1)},i)},i)}"string"==typeof t&&(e.newManifestUrl=t,t=void 0),"object"==typeof t?s(t):(pegasus(e.newManifestUrl).then(s,i),setTimeout(function(){i(new Error("timeout"))},e._checkTimeout))})},r.prototype.canDownload=function(){return!!this.newManifest&&!this._updateReady},r.prototype.canUpdate=function(){return this._updateReady},r.prototype.download=function(t){var e=this;return e.canDownload()?(localStorage.removeItem("manifest"),this.manifest.files=Manifest.files={},e.cache.remove(e._toBeDeleted,!0).then(function(){return e.cache.add(e._toBeDownloaded),e.cache.download(t)}).then(function(){return e._toBeDeleted=[],e._toBeDownloaded=[],e._updateReady=!0,e.newManifest},function(t){return t&&t.length&&e.cache.remove(t),t})):s.resolve(null)},r.prototype.update=function(t){return this._updateReady?(localStorage.setItem("manifest",JSON.stringify(this.newManifest)),t!==!1&&location.reload(),!0):!1},r.prototype.clear=function(){return localStorage.removeItem("manifest"),this.cache.clear()},r.prototype.reset=function(){return this.clear().then(function(){location.reload()},function(){location.reload()})},t.exports=r},function(t,e,n){function o(t){if(this._fs=t.fs,!this._fs)throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.');i=this._fs.Promise,this._mirrorMode="hash"!==t.mode,this._retry=t.retry||[500,1500,8e3],this._cacheBuster=!!t.cacheBuster,this._localRoot=t.localRoot||"data","/"!==this._localRoot[this._localRoot.length-1]&&(this._localRoot+="/"),"/"!==this._localRoot[0]&&(this._localRoot="/"+this._localRoot),this._serverRoot=t.serverRoot||"",this._serverRoot&&"/"!==this._serverRoot[this._serverRoot.length-1]&&(this._serverRoot+="/"),"./"===this._serverRoot&&(this._serverRoot=""),this._downloading=[],this._added=[],this._cached={},this.ready=this.list()}var r=n(4),i=null,s="undefined"!=typeof cordova;s||(window.ProgressEvent=function(){}),o.prototype.list=function(){var t=this;return new i(function(e){t._fs.list(t._localRoot,"rfe").then(function(n){t._cached={},n=n.map(function(e){return t._cached[e.fullPath]={toInternalURL:s?e.toInternalURL():e.toURL(),toURL:e.toURL()},e.fullPath}),e(n)},function(){e([])})})},o.prototype.add=function(t){t||(t=[]),"string"==typeof t&&(t=[t]);var e=this;return t.forEach(function(t){t=e.toServerURL(t),-1===e._added.indexOf(t)&&e._added.push(t)}),e.isDirty()},o.prototype.remove=function(t,e){t||(t=[]);var n=[];"string"==typeof t&&(t=[t]);var o=this;return t.forEach(function(t){var e=o._added.indexOf(o.toServerURL(t));e>=0&&o._added.splice(e,1);var r=o.toPath(t);n.push(o._fs.remove(r)),delete o._cached[r]}),e?i.all(n):o.isDirty()},o.prototype.getDownloadQueue=function(){var t=this,e=t._added.filter(function(e){return!t.isCached(e)});return e},o.prototype.getAdded=function(){return this._added},o.prototype.isDirty=function(){return this.getDownloadQueue().length>0},o.prototype.download=function(t){var e=this._fs,n=this;return n.abort(),new i(function(o,r){e.ensure(n._localRoot).then(function(){return n.list()}).then(function(){if(!n.isDirty())return void o(n);var i=n.getDownloadQueue(),s=[],a=n._downloading.length,u=n._downloading.length,c=n._downloading.length+i.length;i.forEach(function(i){var f,l=n.toPath(i);"function"==typeof t&&(f=function(e){e.queueIndex=a,e.queueSize=c,e.url=i,e.path=l,e.percentage=a/c,e.loaded>0&&e.total>0&&a!==c&&(e.percentage+=e.loaded/e.total/c),s.indexOf(i)<0&&(s.push(i),a++),t(e)});var d=function(){u++,u===c&&(n._downloading=[],n.list().then(function(){f&&f(new ProgressEvent),n.isDirty()?r(n.getDownloadQueue()):o(n)},r))},h=i;n._cacheBuster&&(h+="?"+Date.now());var p=e.download(i,l,{retry:n._retry},f);p.then(d,d),n._downloading.push(p)})},r)})},o.prototype.abort=function(){this._downloading.forEach(function(t){t.abort()}),this._downloading=[]},o.prototype.isCached=function(t){return t=this.toPath(t),!!this._cached[t]},o.prototype.clear=function(){return this._cached={},this._fs.removeDir(this._localRoot)},o.prototype.toInternalURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:this.toServerURL(path)},o.prototype.get=o.prototype.toInternalURL,o.prototype.toDataURL=function(t){return this._fs.toDataURL(this.toPath(t))},o.prototype.toURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toURL:this.toServerURL(t)},o.prototype.toServerURL=function(t){return"/"===t[0]&&(t=t.substr(1)),t.indexOf("://")<0?this._serverRoot+t:t},o.prototype.toPath=function(t){return this._mirrorMode?(t=t||"",len=this._serverRoot.length,t.substr(0,len)!==this._serverRoot?("/"===t[0]&&(t=t.substr(1)),this._localRoot+t):this._localRoot+t.substr(len)):this._localRoot+r(t)+t.substr(t.lastIndexOf("."))},t.exports=o},function(t){function e(t,e){var n,o,r,i,s,a,u,c;for(n=3&t.length,o=t.length-n,r=e,s=3432918353,a=461845907,c=0;o>c;)u=255&t.charCodeAt(c)|(255&t.charCodeAt(++c))<<8|(255&t.charCodeAt(++c))<<16|(255&t.charCodeAt(++c))<<24,++c,u=(65535&u)*s+(((u>>>16)*s&65535)<<16)&4294967295,u=u<<15|u>>>17,u=(65535&u)*a+(((u>>>16)*a&65535)<<16)&4294967295,r^=u,r=r<<13|r>>>19,i=5*(65535&r)+((5*(r>>>16)&65535)<<16)&4294967295,r=(65535&i)+27492+(((i>>>16)+58964&65535)<<16);switch(u=0,n){case 3:u^=(255&t.charCodeAt(c+2))<<16;case 2:u^=(255&t.charCodeAt(c+1))<<8;case 1:u^=255&t.charCodeAt(c),u=(65535&u)*s+(((u>>>16)*s&65535)<<16)&4294967295,u=u<<15|u>>>17,u=(65535&u)*a+(((u>>>16)*a&65535)<<16)&4294967295,r^=u}return r^=t.length,r^=r>>>16,r=2246822507*(65535&r)+((2246822507*(r>>>16)&65535)<<16)&4294967295,r^=r>>>13,r=3266489909*(65535&r)+((3266489909*(r>>>16)&65535)<<16)&4294967295,r^=r>>>16,r>>>0}t.exports=e},function(t){function e(t,n,o,r){t.getDirectory(n[0],{create:!0},function(t){n.length>1?e(t,n.slice(1),o,r):o(t)},r)}function n(t){var e=t.split("/");return e.length>1?(e.splice(e.length-1,1),e.join("/")):""}function o(t){var e=t.split("/");return e[e.length-1]}var r=[],i=0;t.exports=function(t){function s(t){return a(n(t)).then(function(){return l(t,{create:!0})})}function a(t){return new E(function(n,o){return T.then(function(r){t?(t=t.split("/").filter(function(t){return t&&t.length>0&&"."!==t[0]}),e(r.root,t,n,o)):n(r.root)},o)})}function u(t){return new E(function(e,n){l(t).then(function(t){e(t)},function(t){1===t.code?e(!1):n(t)})})}function c(t){return l(t).then(function(t){return t.toURL()})}function f(t){return h(t,"readAsDataURL")}function l(t,e){return e=e||{},new E(function(n,o){return T.then(function(r){r.root.getFile(t,e,n,o)},o)})}function d(t,e){return e=e||{},new E(function(n,o){return T.then(function(r){t&&"/"!==t?r.root.getDirectory(t,e,n,o):n(r.root)},o)})}function h(t,e){return e=e||"readAsText",l(t).then(function(t){return new E(function(n,o){t.file(function(t){var o=new FileReader;o.onloadend=function(){n(this.result)},o[e](t)},o)})})}function p(t){return h(t).then(JSON.parse)}function v(t,e,o){return a(n(t)).then(function(){return l(t,{create:!0})}).then(function(t){return new E(function(n,r){t.createWriter(function(t){t.onwriteend=n,t.onerror=r,"string"==typeof e?e=new Blob([e],{type:o||"text/plain"}):e instanceof Blob!=!0&&(e=new Blob([JSON.stringify(e,null,4)],{type:o||"application/json"})),t.write(e)},r)})})}function w(t,e){return a(n(e)).then(function(n){return l(t).then(function(t){return new E(function(r,i){t.moveTo(n,o(e),r,i)})})})}function y(t,e){return a(n(e)).then(function(n){return l(t).then(function(t){return new E(function(r,i){t.copyTo(n,o(e),r,i)})})})}function m(t,e){var n=e?l:u;return new E(function(e,o){n(t).then(function(t){t!==!1?t.remove(e,o):e()},o)})}function g(t){return d(t).then(function(t){return new E(function(e,n){t.removeRecursively(e,n)})})}function _(t,e){e=e||"";var n=e.indexOf("r")>-1,o=e.indexOf("e")>-1,r=e.indexOf("f")>-1,i=e.indexOf("d")>-1;return r&&i&&(r=!1,i=!1),new E(function(e,s){return d(t).then(function(t){var a=t.createReader();a.readEntries(function(t){var a=[E.resolve(t)];n&&t.filter(function(t){return t.isDirectory}).forEach(function(t){a.push(_(t.fullPath,"re"))}),E.all(a).then(function(t){var n=[];n=n.concat.apply(n,t),r&&(n=n.filter(function(t){return t.isFile})),i&&(n=n.filter(function(t){return t.isDirectory})),o||(n=n.map(function(t){return t.fullPath})),e(n)},s)},s)},s)})}function R(){for(;r.length>0&&i<t.concurrency;){i++;var e=r.pop(),n=e.shift(),o=e.shift(),s=e.shift(),a=e.shift(),u=e.shift(),c=e.shift(),f=e.shift(),l=e.shift();n._aborted?i--:o?(n.download.call(n,s,a,u,c,f,l),n.onprogress&&n.onprogress(new ProgressEvent)):n.upload.call(n,a,s,u,c,l,f)}}function L(t){return i--,R(),t}function S(e,n,o,s,a){"function"==typeof s&&(a=s,s={}),n=encodeURI(n),x&&(o=A(o)),s=s||{},s.retry&&s.retry.length||(s.retry=t.retry),s.retry=s.retry.concat();var u=new FileTransfer;"function"==typeof a&&(u.onprogress=a);var c=new E(function(t,a){var c=function(i){if(0===s.retry.length)a(i);else{r.unshift([u,e,n,o,t,c,s.trustAllHosts||!1,s]);var f=s.retry.shift();f>0?setTimeout(L,f):L()}};s.retry.unshift(0),i++,c()});return c.then(L,L),c.progress=function(t){return u.onprogress=t,c},c.abort=function(){return u._aborted=!0,u.abort(),c},c}function b(t,e,n,o){return S(!0,t,e,n,o)}function D(t,e,n,o){return S(!1,e,t,n,o)}var E=t.Promise||window.Promise;if(!E)throw new Error("No Promise library given in options.Promise");this.options=t=t||{},t.persistent=void 0!==t.persistent?t.persistent:!0,t.storageSize=t.storageSize||20971520,t.concurrency=t.concurrency||3,t.retry=t.retry||[];var U,x="undefined"!=typeof cordova;x?U=new E(function(t,e){document.addEventListener("deviceready",t,!1),setTimeout(function(){e(new Error("deviceready has not fired after 5 seconds."))},5100)}):(U=E.resolve(),"undefined"!=typeof webkitRequestFileSystem?(window.requestFileSystem=webkitRequestFileSystem,window.FileTransfer=function(){},FileTransfer.prototype.download=function(t,e,n,o){var r=new XMLHttpRequest;return r.open("GET",t),r.onreadystatechange=function(){4==r.readyState&&(200===r.status?v(e,r.responseText).then(n,o):o(r.status))},r.send(),r}):window.requestFileSystem=function(t,e,n,o){o(new Error("requestFileSystem not supported!"))});var T=new E(function(e,n){U.then(function(){window.requestFileSystem(t.persistent?1:0,t.storageSize,e,n),setTimeout(function(){n(new Error("Could not retrieve FileSystem after 5 seconds."))},5100)},n)});T.then(function(t){window.__fs=t},function(t){console.error("Could not get Cordova FileSystem:",t)});var P,A;return x?(A=function(e){return"/"!==e[0]&&(e="/"+e),"cdvfile://localhost/"+(t.persistent?"persistent":"temporary")+e},P=function(t){return l(t).then(function(t){return t.toInternalURL()})}):(A=function(e){return"/"!==e[0]&&(e="/"+e),"filesystem:"+location.origin+(t.persistent?"/persistent":"/temporary")+e},P=function(t){return l(t).then(function(t){return t.toURL()})}),{fs:T,file:l,filename:o,dir:d,dirname:n,create:s,read:h,readJSON:p,write:v,move:w,copy:y,remove:m,removeDir:g,list:_,ensure:a,exists:u,download:b,upload:D,toURL:c,isCordova:x,toInternalURLSync:A,toInternalURL:P,toDataURL:f,deviceready:U,options:t,Promise:E}}},function(t){/**@license MIT-promiscuous-©Ruben Verborgh*/ | ||
!function(e,n){function o(t,e){return(typeof e)[0]==t}function r(t,s){return s=function a(u,c,f,l,d,h){function p(t){return function(e){d&&(d=0,a(o,t,e))}}if(l=a.q,u!=o)return r(function(t,e){l.push({p:this,r:t,j:e,1:u,0:c})});if(f&&o(e,f)|o(n,f))try{d=f.then}catch(v){c=0,f=v}if(o(e,d))try{d.call(f,p(1),c=p(0))}catch(v){c(v)}else for(s=function(n,s){return o(e,n=c?n:s)?r(function(t,e){i(this,t,e,f,n)}):t},h=0;h<l.length;)d=l[h++],o(e,u=d[c])?i(d.p,d.r,d.j,f,u):(c?d.r:d.j)(f)},s.q=[],t.call(t={then:function(t,e){return s(t,e)},"catch":function(t){return s(0,t)}},function(t){s(o,1,t)},function(t){s(o,0,t)}),t}function i(t,r,i,s,a){setImmediate(function(){try{s=a(s),a=s&&o(n,s)|o(e,s)&&s.then,o(e,a)?s==t?i(TypeError()):a.call(s,r,i):r(s)}catch(u){i(u)}})}function s(t){return r(function(e){e(t)})}t.exports=r,r.resolve=s,r.reject=function(t){return r(function(e,n){n(t)})},r.all=function(t){return r(function(e,n,o,r){r=[],o=t.length||e(r),t.map(function(t,i){s(t).then(function(t){r[i]=t,--o||e(r)},n)})})}}("f","o")}]); | ||
/******/ (function(modules) { // webpackBootstrap | ||
/******/ // The module cache | ||
/******/ var installedModules = {}; | ||
/******/ | ||
/******/ // The require function | ||
/******/ function __webpack_require__(moduleId) { | ||
/******/ | ||
/******/ // Check if module is in cache | ||
/******/ if(installedModules[moduleId]) | ||
/******/ return installedModules[moduleId].exports; | ||
/******/ | ||
/******/ // Create a new module (and put it into the cache) | ||
/******/ var module = installedModules[moduleId] = { | ||
/******/ exports: {}, | ||
/******/ id: moduleId, | ||
/******/ loaded: false | ||
/******/ }; | ||
/******/ | ||
/******/ // Execute the module function | ||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | ||
/******/ | ||
/******/ // Flag the module as loaded | ||
/******/ module.loaded = true; | ||
/******/ | ||
/******/ // Return the exports of the module | ||
/******/ return module.exports; | ||
/******/ } | ||
/******/ | ||
/******/ | ||
/******/ // expose the modules object (__webpack_modules__) | ||
/******/ __webpack_require__.m = modules; | ||
/******/ | ||
/******/ // expose the module cache | ||
/******/ __webpack_require__.c = installedModules; | ||
/******/ | ||
/******/ // __webpack_public_path__ | ||
/******/ __webpack_require__.p = ""; | ||
/******/ | ||
/******/ // Load entry module and return exports | ||
/******/ return __webpack_require__(0); | ||
/******/ }) | ||
/************************************************************************/ | ||
/******/ ([ | ||
/* 0 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
// Include the bootstrap script. | ||
__webpack_require__(1); | ||
var CordovaAppLoader = __webpack_require__(2); | ||
var CordovaPromiseFS = __webpack_require__(3); | ||
var Promise = __webpack_require__(4); | ||
window.setImmediate = window.setTimeout; | ||
// Helper variable: Chrome should use temporary storage. | ||
var isCordova = typeof cordova !== 'undefined', | ||
fs, | ||
loader, | ||
script, | ||
serverUrl; | ||
// Get SERVER_URL from script tag. | ||
script = document.querySelector('script[manifest]'); | ||
if(script){ | ||
serverUrl= script.getAttribute('server'); | ||
if(!serverUrl) { | ||
throw new Error('Add a "server" attribute to the autoupdate.js script!'); | ||
} | ||
} | ||
// Initialize filesystem and loader | ||
fs = new CordovaPromiseFS({ | ||
persistent: isCordova, | ||
Promise: Promise | ||
}); | ||
loader = new CordovaAppLoader({ | ||
fs: fs, | ||
localRoot: 'app', | ||
serverRoot: serverUrl, | ||
mode: 'mirror', | ||
cacheBuster: true | ||
}); | ||
// Check > Download > Update | ||
function check(){ | ||
loader.check() | ||
.then(function(){ | ||
return loader.download(); | ||
}) | ||
.then(function(){ | ||
return loader.update(); | ||
},function(err){ | ||
console.error('Auto-update error:',err); | ||
}); | ||
} | ||
// Couple events: | ||
// On launch | ||
check(); | ||
// Cordova: On resume | ||
fs.deviceready.then(function(){ | ||
document.addEventListener('resume',check); | ||
}); | ||
// Chrome: On page becomes visible again | ||
function handleVisibilityChange() { | ||
if (!document.webkitHidden) { | ||
check(); | ||
} | ||
} | ||
document.addEventListener("webkitvisibilitychange", handleVisibilityChange, false); | ||
/***/ }, | ||
/* 1 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
(function(){ | ||
// Retrieved and slightly modified from: https://github.com/typicode/pegasus | ||
// -------------------------------------------------------------------------- | ||
// | ||
// a url (naming it a, beacause it will be reused to store callbacks) | ||
// xhr placeholder to avoid using var, not to be used | ||
window.pegasus = function pegasus(a, xhr) { | ||
xhr = new XMLHttpRequest(); | ||
// Open url | ||
xhr.open('GET', a); | ||
// Reuse a to store callbacks | ||
a = []; | ||
// onSuccess handler | ||
// onError handler | ||
// cb placeholder to avoid using var, should not be used | ||
xhr.onreadystatechange = xhr.then = function(onSuccess, onError, cb) { | ||
// Test if onSuccess is a function or a load event | ||
if (onSuccess.call) a = [onSuccess, onError]; | ||
// Test if request is complete | ||
if (xhr.readyState == 4) { | ||
// index will be: | ||
// 0 if status is between 0 and 399 | ||
// 1 if status is over | ||
cb = a[0|xhr.status / 400]; | ||
// Safari doesn't support xhr.responseType = 'json' | ||
// so the response is parsed | ||
if (cb) cb(xhr.status === 200 || xhr.status === 0?JSON.parse(xhr.responseText):xhr); | ||
} | ||
}; | ||
// Send | ||
xhr.send(); | ||
// Return request | ||
return xhr; | ||
}; | ||
//------------------------------------------------------------------ | ||
// Step 2: After fetching manifest (localStorage or XHR), load it | ||
function loadManifest(manifest,fromLocalStorage,timeout){ | ||
if(!manifest.load) { | ||
console.error('Manifest has nothing to load (manifest.load is empty).',manifest); | ||
return; | ||
} | ||
var el, | ||
head = document.getElementsByTagName('head')[0], | ||
scripts = manifest.load.concat(); | ||
// Load Next Script | ||
function loadScript(){ | ||
var src = scripts.shift(); | ||
if(!src) return; | ||
// Ensure the 'src' has no '/' (it's in the root already) | ||
if(src[0] === '/') src = src.substr(1); | ||
src = manifest.root + src; | ||
// Load javascript | ||
if(src.substr(-3) === ".js"){ | ||
el= document.createElement('script'); | ||
el.type= 'text/javascript'; | ||
el.src= src; | ||
el.onload = loadScript; | ||
// Load CSS | ||
} else { | ||
el= document.createElement('link'); | ||
el.rel = "stylesheet"; | ||
el.href = src; | ||
el.type = "text/css"; | ||
setTimeout(loadScript,0); | ||
} | ||
head.appendChild(el); | ||
} | ||
//--------------------------------------------------- | ||
// Step 3: Ensure the 'root' end with a '/' | ||
manifest.root = manifest.root || './'; | ||
if(manifest.root.length > 0 && manifest.root[manifest.root.length-1] !== '/') | ||
manifest.root += '/'; | ||
// Step 4: Save manifest for next time | ||
if(!fromLocalStorage) | ||
localStorage.setItem('manifest',JSON.stringify(manifest)); | ||
// Step 5: Load Scripts | ||
// If we're loading Cordova files, make sure Cordova is ready first! | ||
if(manifest.root.substr(0,7) === 'cdvfile'){ | ||
document.addEventListener("deviceready", loadScript, false); | ||
} else { | ||
loadScript(); | ||
} | ||
// Save to global scope | ||
window.Manifest = manifest; | ||
// Safety timeout. If BOOTSTRAP_OK is not defined, | ||
// it will delete the 'localStorage' version and revert to factory settings. | ||
if(fromLocalStorage){ | ||
setTimeout(function(){ | ||
if(!window.BOOTSTRAP_OK){ | ||
localStorage.removeItem('manifest'); | ||
location.reload(); | ||
} | ||
},timeout); | ||
} | ||
} | ||
//--------------------------------------------------------------------- | ||
window.Manifest = {}; | ||
// Step 1: Load manifest from localStorage | ||
var manifest = JSON.parse(localStorage.getItem('manifest')); | ||
// grab manifest.json location from <script manifest="..."></script> | ||
var s = document.querySelector('script[manifest]'); | ||
// Not in localStorage? Fetch it! | ||
if(!manifest){ | ||
var url = (s? s.getAttribute('manifest'): null) || 'manifest.json'; | ||
// get manifest.json, then loadManifest. | ||
pegasus(url).then(loadManifest,function(xhr){ | ||
console.error('Could not download '+url+': '+xhr.status); | ||
}); | ||
// Manifest was in localStorage. Load it immediatly. | ||
} else { | ||
loadManifest(manifest,true,s.getAttribute('timeout') || 10000); | ||
} | ||
})(); | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var CordovaFileCache = __webpack_require__(5); | ||
var Promise = null; | ||
function createFilemap(files){ | ||
var result = {}; | ||
Object.keys(files).forEach(function(key){ | ||
result[files[key].filename] = files[key]; | ||
}); | ||
return result; | ||
} | ||
function AppLoader(options){ | ||
if(!options) throw new Error('CordovaAppLoader has no options!'); | ||
if(!options.fs) throw new Error('CordovaAppLoader has no "fs" option (cordova-promise-fs)'); | ||
if(!options.serverRoot) throw new Error('CordovaAppLoader has no "serverRoot" option.'); | ||
if(!window.pegasus || !window.Manifest) throw new Error('CordovaAppLoader bootstrap.js is missing.'); | ||
Promise = options.fs.Promise; | ||
// initialize variables | ||
this.manifest = window.Manifest; | ||
this.newManifest = null; | ||
// normalize serverRoot and set remote manifest url | ||
options.serverRoot = options.serverRoot || ''; | ||
if(!!options.serverRoot && options.serverRoot[options.serverRoot.length-1] !== '/') options.serverRoot += '/'; | ||
this.newManifestUrl = options.serverRoot + (options.manifest || 'manifest.json'); | ||
// initialize a file cache | ||
if(options.mode) options.mode = 'mirror'; | ||
this.cache = new CordovaFileCache(options); | ||
// private stuff | ||
this._toBeDeleted = []; | ||
this._toBeDownloaded = []; | ||
this._updateReady = false; | ||
this._checkTimeout = options.checkTimeout || 10000; | ||
} | ||
AppLoader.prototype.check = function(newManifest){ | ||
var self = this, manifest = this.manifest; | ||
return new Promise(function(resolve,reject){ | ||
if(typeof newManifest === "string") { | ||
self.newManifestUrl = newManifest; | ||
newManifest = undefined; | ||
} | ||
function checkManifest(newManifest){ | ||
// make sure cache is ready for the DIFF operations! | ||
self.cache.ready.then(function(){ | ||
if(!newManifest.files){ | ||
reject('Downloaded Manifest has no "files" attribute.'); | ||
return; | ||
} | ||
var newFiles = createFilemap(newManifest.files); | ||
var oldFiles = createFilemap(manifest.files); | ||
// Create the diff | ||
self._toBeDownloaded = Object.keys(newFiles) | ||
.filter(function(file){ | ||
return !oldFiles[file] | ||
|| oldFiles[file].version !== newFiles[file].version | ||
|| !self.cache.isCached(file); | ||
}); | ||
self.cache.list().then(function(files){ | ||
self._toBeDeleted = files | ||
.map(function(file){ | ||
return file.substr(self.cache._localRoot.length); | ||
}) | ||
.filter(function(file){ | ||
return !newFiles[file]; | ||
}) | ||
.concat(self._toBeDownloaded); | ||
if(self._toBeDeleted.length > 0 || self._toBeDownloaded.length > 0){ | ||
// Save the new Manifest | ||
self.newManifest = newManifest; | ||
self.newManifest.root = self.cache.toInternalURL('/') + (self.newManifest.root || ''); | ||
resolve(true); | ||
} else { | ||
resolve(false); | ||
} | ||
},reject); | ||
},reject); | ||
} | ||
if(typeof newManifest === "object") { | ||
checkManifest(newManifest); | ||
} else { | ||
pegasus(self.newManifestUrl).then(checkManifest,reject); | ||
setTimeout(function(){reject(new Error('timeout'));},self._checkTimeout); | ||
} | ||
}); | ||
}; | ||
AppLoader.prototype.canDownload = function(){ | ||
return !!this.newManifest && !this._updateReady; | ||
}; | ||
AppLoader.prototype.canUpdate = function(){ | ||
return this._updateReady; | ||
}; | ||
AppLoader.prototype.download = function(onprogress){ | ||
var self = this; | ||
if(!self.canDownload()) { | ||
return Promise.resolve(null); | ||
} | ||
// we will delete files, which will invalidate the current manifest... | ||
localStorage.removeItem('manifest'); | ||
this.manifest.files = Manifest.files = {}; | ||
return self.cache.remove(self._toBeDeleted,true) | ||
.then(function(){ | ||
self.cache.add(self._toBeDownloaded); | ||
return self.cache.download(onprogress); | ||
}).then(function(){ | ||
self._toBeDeleted = []; | ||
self._toBeDownloaded = []; | ||
self._updateReady = true; | ||
return self.newManifest; | ||
},function(files){ | ||
// on download error, remove files... | ||
if(!!files && files.length){ | ||
self.cache.remove(files); | ||
} | ||
return files; | ||
}); | ||
}; | ||
AppLoader.prototype.update = function(reload){ | ||
if(this._updateReady) { | ||
// update manifest | ||
localStorage.setItem('manifest',JSON.stringify(this.newManifest)); | ||
if(reload !== false) location.reload(); | ||
return true; | ||
} | ||
return false; | ||
}; | ||
AppLoader.prototype.clear = function(){ | ||
localStorage.removeItem('manifest'); | ||
return this.cache.clear(); | ||
}; | ||
AppLoader.prototype.reset = function(){ | ||
return this.clear().then(function(){ | ||
location.reload(); | ||
},function(){ | ||
location.reload(); | ||
}); | ||
}; | ||
module.exports = AppLoader; | ||
/***/ }, | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/** | ||
* Static Private functions | ||
*/ | ||
/* createDir, recursively */ | ||
function __createDir(rootDirEntry, folders, success,error) { | ||
rootDirEntry.getDirectory(folders[0], {create: true}, function(dirEntry) { | ||
// Recursively add the new subfolder (if we still have another to create). | ||
if (folders.length > 1) { | ||
__createDir(dirEntry, folders.slice(1),success,error); | ||
} else { | ||
success(dirEntry); | ||
} | ||
}, error); | ||
} | ||
function dirname(str) { | ||
var parts = str.split('/'); | ||
if(parts.length > 1) { | ||
parts.splice(parts.length-1,1); | ||
return parts.join('/'); | ||
} else { | ||
return ''; | ||
} | ||
} | ||
function filename(str) { | ||
var parts = str.split('/'); | ||
return parts[parts.length-1]; | ||
} | ||
var transferQueue = [], // queued fileTransfers | ||
inprogress = 0; // currently active filetransfers | ||
/** | ||
* Factory function: Create a single instance (based on single FileSystem) | ||
*/ | ||
module.exports = function(options){ | ||
/* Promise implementation */ | ||
var Promise = options.Promise || window.Promise; | ||
if(!Promise) { throw new Error("No Promise library given in options.Promise"); } | ||
/* default options */ | ||
this.options = options = options || {}; | ||
options.persistent = options.persistent !== undefined? options.persistent: true; | ||
options.storageSize = options.storageSize || 20*1024*1024; | ||
options.concurrency = options.concurrency || 3; | ||
options.retry = options.retry || []; | ||
/* Cordova deviceready promise */ | ||
var deviceready, isCordova = typeof cordova !== 'undefined'; | ||
if(isCordova){ | ||
deviceready = new Promise(function(resolve,reject){ | ||
document.addEventListener("deviceready", resolve, false); | ||
setTimeout(function(){ reject(new Error('deviceready has not fired after 5 seconds.')); },5100); | ||
}); | ||
} else { | ||
/* FileTransfer implementation for Chrome */ | ||
deviceready = Promise.resolve(); | ||
if(typeof webkitRequestFileSystem !== 'undefined'){ | ||
window.requestFileSystem = webkitRequestFileSystem; | ||
window.FileTransfer = function FileTransfer(){}; | ||
FileTransfer.prototype.download = function download(url,file,win,fail) { | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open('GET', url); | ||
xhr.onreadystatechange = function(onSuccess, onError, cb) { | ||
if (xhr.readyState == 4) { | ||
if(xhr.status === 200){ | ||
write(file,xhr.responseText).then(win,fail); | ||
} else { | ||
fail(xhr.status); | ||
} | ||
} | ||
}; | ||
xhr.send(); | ||
return xhr; | ||
}; | ||
} else { | ||
window.requestFileSystem = function(x,y,z,fail){ | ||
fail(new Error('requestFileSystem not supported!')); | ||
}; | ||
} | ||
} | ||
/* the filesystem! */ | ||
var fs = new Promise(function(resolve,reject){ | ||
deviceready.then(function(){ | ||
window.requestFileSystem(options.persistent? 1: 0, options.storageSize, resolve, reject); | ||
setTimeout(function(){ reject(new Error('Could not retrieve FileSystem after 5 seconds.')); },5100); | ||
},reject); | ||
}); | ||
/* debug */ | ||
fs.then(function(fs){ | ||
window.__fs = fs; | ||
},function(err){ | ||
console.error('Could not get Cordova FileSystem:',err); | ||
}); | ||
function create(path){ | ||
return ensure(dirname(path)).then(function(){ | ||
return file(path,{create:true}); | ||
}); | ||
} | ||
/* ensure directory exists */ | ||
function ensure(folders) { | ||
return new Promise(function(resolve,reject){ | ||
return fs.then(function(fs){ | ||
if(!folders) { | ||
resolve(fs.root); | ||
} else { | ||
folders = folders.split('/').filter(function(folder) { | ||
return folder && folder.length > 0 && folder[0] !== '.'; | ||
}); | ||
__createDir(fs.root,folders,resolve,reject); | ||
} | ||
},reject); | ||
}); | ||
} | ||
/* does file exist? If so, resolve with fileEntry, if not, resolve with false. */ | ||
function exists(path){ | ||
return new Promise(function(resolve,reject){ | ||
file(path).then( | ||
function(fileEntry){ | ||
resolve(fileEntry); | ||
}, | ||
function(err){ | ||
if(err.code === 1) { | ||
resolve(false); | ||
} else { | ||
reject(err); | ||
} | ||
} | ||
); | ||
}); | ||
} | ||
/* convert path to URL to be used in JS/CSS/HTML */ | ||
function toURL(path) { | ||
return file(path).then(function(fileEntry) { | ||
return fileEntry.toURL(); | ||
}); | ||
} | ||
/* convert path to URL to be used in JS/CSS/HTML */ | ||
var toInternalURL,toInternalURLSync; | ||
if(isCordova) { | ||
/* synchronous helper to get internal URL. */ | ||
toInternalURLSync = function(path){ | ||
if(path[0] !== '/') path = '/' + path; | ||
return 'cdvfile://localhost/'+(options.persistent? 'persistent':'temporary') + path; | ||
}; | ||
toInternalURL = function(path) { | ||
return file(path).then(function(fileEntry) { | ||
return fileEntry.toInternalURL(); | ||
}); | ||
}; | ||
} else { | ||
/* synchronous helper to get internal URL. */ | ||
toInternalURLSync = function(path){ | ||
if(path[0] !== '/') path = '/' + path; | ||
return 'filesystem:'+location.origin+(options.persistent? '/persistent':'/temporary') + path; | ||
}; | ||
toInternalURL = function(path) { | ||
return file(path).then(function(fileEntry) { | ||
return fileEntry.toURL(); | ||
}); | ||
}; | ||
} | ||
/* convert path to base64 date URI */ | ||
function toDataURL(path) { | ||
return read(path,'readAsDataURL'); | ||
} | ||
/* get file file */ | ||
function file(path,options){ | ||
options = options || {}; | ||
return new Promise(function(resolve,reject){ | ||
return fs.then(function(fs){ | ||
fs.root.getFile(path,options,resolve,reject); | ||
},reject); | ||
}); | ||
} | ||
/* get directory entry */ | ||
function dir(path,options){ | ||
options = options || {}; | ||
return new Promise(function(resolve,reject){ | ||
return fs.then(function(fs){ | ||
if(!path || path === '/') { | ||
resolve(fs.root); | ||
} else { | ||
fs.root.getDirectory(path,options,resolve,reject); | ||
} | ||
},reject); | ||
}); | ||
} | ||
/* return contents of a file */ | ||
function read(path,method) { | ||
method = method || 'readAsText'; | ||
return file(path).then(function(fileEntry) { | ||
return new Promise(function(resolve,reject){ | ||
fileEntry.file(function(file){ | ||
var reader = new FileReader(); | ||
reader.onloadend = function(){ | ||
resolve(this.result); | ||
}; | ||
reader[method](file); | ||
},reject); | ||
}); | ||
}); | ||
} | ||
function readJSON(path){ | ||
return read(path).then(JSON.parse); | ||
} | ||
/* write contents to a file */ | ||
function write(path,blob,mimeType) { | ||
return ensure(dirname(path)) | ||
.then(function() { return file(path,{create:true}); }) | ||
.then(function(fileEntry) { | ||
return new Promise(function(resolve,reject){ | ||
fileEntry.createWriter(function(writer){ | ||
writer.onwriteend = resolve; | ||
writer.onerror = reject; | ||
if(typeof blob === 'string') { | ||
blob = new Blob([blob],{type: mimeType || 'text/plain'}); | ||
} else if(blob instanceof Blob !== true){ | ||
blob = new Blob([JSON.stringify(blob,null,4)],{type: mimeType || 'application/json'}); | ||
} | ||
writer.write(blob); | ||
},reject); | ||
}); | ||
}); | ||
} | ||
/* move a file */ | ||
function move(src,dest) { | ||
return ensure(dirname(dest)) | ||
.then(function(dir) { | ||
return file(src).then(function(fileEntry){ | ||
return new Promise(function(resolve,reject){ | ||
fileEntry.moveTo(dir,filename(dest),resolve,reject); | ||
}); | ||
}); | ||
}); | ||
} | ||
/* copy a file */ | ||
function copy(src,dest) { | ||
return ensure(dirname(dest)) | ||
.then(function(dir) { | ||
return file(src).then(function(fileEntry){ | ||
return new Promise(function(resolve,reject){ | ||
fileEntry.copyTo(dir,filename(dest),resolve,reject); | ||
}); | ||
}); | ||
}); | ||
} | ||
/* delete a file */ | ||
function remove(path,mustExist) { | ||
var method = mustExist? file:exists; | ||
return new Promise(function(resolve,reject){ | ||
method(path).then(function(fileEntry){ | ||
if(fileEntry !== false) { | ||
fileEntry.remove(resolve,reject); | ||
} else { | ||
resolve(); | ||
} | ||
},reject); | ||
}); | ||
} | ||
/* delete a directory */ | ||
function removeDir(path) { | ||
return dir(path).then(function(dirEntry){ | ||
return new Promise(function(resolve,reject) { | ||
dirEntry.removeRecursively(resolve,reject); | ||
}); | ||
}); | ||
} | ||
/* list contents of a directory */ | ||
function list(path,mode) { | ||
mode = mode || ''; | ||
var recursive = mode.indexOf('r') > -1; | ||
var getAsEntries = mode.indexOf('e') > -1; | ||
var onlyFiles = mode.indexOf('f') > -1; | ||
var onlyDirs = mode.indexOf('d') > -1; | ||
if(onlyFiles && onlyDirs) { | ||
onlyFiles = false; | ||
onlyDirs = false; | ||
} | ||
return new Promise(function(resolve,reject){ | ||
return dir(path).then(function(dirEntry){ | ||
var dirReader = dirEntry.createReader(); | ||
dirReader.readEntries(function(entries) { | ||
var promises = [Promise.resolve(entries)]; | ||
if(recursive) { | ||
entries | ||
.filter(function(entry){return entry.isDirectory; }) | ||
.forEach(function(entry){ | ||
promises.push(list(entry.fullPath,'re')); | ||
}); | ||
} | ||
Promise.all(promises).then(function(values){ | ||
var entries = []; | ||
entries = entries.concat.apply(entries,values); | ||
if(onlyFiles) entries = entries.filter(function(entry) { return entry.isFile; }); | ||
if(onlyDirs) entries = entries.filter(function(entry) { return entry.isDirectory; }); | ||
if(!getAsEntries) entries = entries.map(function(entry) { return entry.fullPath; }); | ||
resolve(entries); | ||
},reject); | ||
}, reject); | ||
},reject); | ||
}); | ||
} | ||
// Whenever we want to start a transfer, we call popTransferQueue | ||
function popTransferQueue(){ | ||
// while we are not at max concurrency | ||
while(transferQueue.length > 0 && inprogress < options.concurrency){ | ||
// increment activity counter | ||
inprogress++; | ||
// fetch filetranfer, method-type (isDownload) and arguments | ||
var args = transferQueue.pop(); | ||
var ft = args.shift(); | ||
var isDownload = args.shift(); | ||
var serverUrl = args.shift(); | ||
var localPath = args.shift(); | ||
var win = args.shift(); | ||
var fail = args.shift(); | ||
var trustAllHosts = args.shift(); | ||
var transferOptions = args.shift(); | ||
if(ft._aborted) { | ||
inprogress--; | ||
} else if(isDownload){ | ||
ft.download.call(ft,serverUrl,localPath,win,fail,trustAllHosts,transferOptions); | ||
if(ft.onprogress) ft.onprogress(new ProgressEvent()); | ||
} else { | ||
ft.upload.call(ft,localPath,serverUrl,win,fail,transferOptions,trustAllHosts); | ||
} | ||
} | ||
// if we are at max concurrency, popTransferQueue() will be called whenever | ||
// the transfer is ready and there is space avaialable. | ||
} | ||
// Promise callback to check if there are any more queued transfers | ||
function nextTransfer(result){ | ||
inprogress--; // decrement counter to free up one space to start transfers again! | ||
popTransferQueue(); // check if there are any queued transfers | ||
return result; | ||
} | ||
function filetransfer(isDownload,serverUrl,localPath,transferOptions,onprogress){ | ||
if(typeof transferOptions === 'function') { | ||
onprogress = transferOptions; | ||
transferOptions = {}; | ||
} | ||
serverUrl = encodeURI(serverUrl); | ||
if(isCordova) localPath = toInternalURLSync(localPath); | ||
transferOptions = transferOptions || {}; | ||
if(!transferOptions.retry || !transferOptions.retry.length) { | ||
transferOptions.retry = options.retry; | ||
} | ||
transferOptions.retry = transferOptions.retry.concat(); | ||
var ft = new FileTransfer(); | ||
if(typeof onprogress === 'function') ft.onprogress = onprogress; | ||
var promise = new Promise(function(resolve,reject){ | ||
var attempt = function(err){ | ||
if(transferOptions.retry.length === 0) { | ||
reject(err); | ||
} else { | ||
transferQueue.unshift([ft,isDownload,serverUrl,localPath,resolve,attempt,transferOptions.trustAllHosts || false,transferOptions]); | ||
var timeout = transferOptions.retry.shift(); | ||
if(timeout > 0) { | ||
setTimeout(nextTransfer,timeout); | ||
} else { | ||
nextTransfer(); | ||
} | ||
} | ||
}; | ||
transferOptions.retry.unshift(0); | ||
inprogress++; | ||
attempt(); | ||
}); | ||
promise.then(nextTransfer,nextTransfer); | ||
promise.progress = function(onprogress){ | ||
ft.onprogress = onprogress; | ||
return promise; | ||
}; | ||
promise.abort = function(){ | ||
ft._aborted = true; | ||
ft.abort(); | ||
return promise; | ||
}; | ||
return promise; | ||
} | ||
function download(url,dest,options,onprogress){ | ||
return filetransfer(true,url,dest,options,onprogress); | ||
} | ||
function upload(source,dest,options,onprogress){ | ||
return filetransfer(false,dest,source,options,onprogress); | ||
} | ||
return { | ||
fs: fs, | ||
file: file, | ||
filename: filename, | ||
dir: dir, | ||
dirname: dirname, | ||
create:create, | ||
read: read, | ||
readJSON: readJSON, | ||
write: write, | ||
move: move, | ||
copy: copy, | ||
remove: remove, | ||
removeDir: removeDir, | ||
list: list, | ||
ensure: ensure, | ||
exists: exists, | ||
download: download, | ||
upload: upload, | ||
toURL:toURL, | ||
isCordova:isCordova, | ||
toInternalURLSync: toInternalURLSync, | ||
toInternalURL:toInternalURL, | ||
toDataURL:toDataURL, | ||
deviceready: deviceready, | ||
options: options, | ||
Promise: Promise | ||
}; | ||
}; | ||
/***/ }, | ||
/* 4 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/**@license MIT-promiscuous-©Ruben Verborgh*/ | ||
(function (func, obj) { | ||
// Type checking utility function | ||
function is(type, item) { return (typeof item)[0] == type; } | ||
// Creates a promise, calling callback(resolve, reject), ignoring other parameters. | ||
function Promise(callback, handler) { | ||
// The `handler` variable points to the function that will | ||
// 1) handle a .then(resolved, rejected) call | ||
// 2) handle a resolve or reject call (if the first argument === `is`) | ||
// Before 2), `handler` holds a queue of callbacks. | ||
// After 2), `handler` is a finalized .then handler. | ||
handler = function pendingHandler(resolved, rejected, value, queue, then, i) { | ||
queue = pendingHandler.q; | ||
// Case 1) handle a .then(resolved, rejected) call | ||
if (resolved != is) { | ||
return Promise(function (resolve, reject) { | ||
queue.push({ p: this, r: resolve, j: reject, 1: resolved, 0: rejected }); | ||
}); | ||
} | ||
// Case 2) handle a resolve or reject call | ||
// (`resolved` === `is` acts as a sentinel) | ||
// The actual function signature is | ||
// .re[ject|solve](<is>, success, value) | ||
// Check if the value is a promise and try to obtain its `then` method | ||
if (value && (is(func, value) | is(obj, value))) { | ||
try { then = value.then; } | ||
catch (reason) { rejected = 0; value = reason; } | ||
} | ||
// If the value is a promise, take over its state | ||
if (is(func, then)) { | ||
function valueHandler(resolved) { | ||
return function (value) { then && (then = 0, pendingHandler(is, resolved, value)); }; | ||
} | ||
try { then.call(value, valueHandler(1), rejected = valueHandler(0)); } | ||
catch (reason) { rejected(reason); } | ||
} | ||
// The value is not a promise; handle resolve/reject | ||
else { | ||
// Replace this handler with a finalized resolved/rejected handler | ||
handler = function (Resolved, Rejected) { | ||
// If the Resolved or Rejected parameter is not a function, | ||
// return the original promise (now stored in the `callback` variable) | ||
if (!is(func, (Resolved = rejected ? Resolved : Rejected))) | ||
return callback; | ||
// Otherwise, return a finalized promise, transforming the value with the function | ||
return Promise(function (resolve, reject) { finalize(this, resolve, reject, value, Resolved); }); | ||
}; | ||
// Resolve/reject pending callbacks | ||
i = 0; | ||
while (i < queue.length) { | ||
then = queue[i++]; | ||
// If no callback, just resolve/reject the promise | ||
if (!is(func, resolved = then[rejected])) | ||
(rejected ? then.r : then.j)(value); | ||
// Otherwise, resolve/reject the promise with the result of the callback | ||
else | ||
finalize(then.p, then.r, then.j, value, resolved); | ||
} | ||
} | ||
}; | ||
// The queue of pending callbacks; garbage-collected when handler is resolved/rejected | ||
handler.q = []; | ||
// Create and return the promise (reusing the callback variable) | ||
callback.call(callback = { then: function (resolved, rejected) { return handler(resolved, rejected); }, | ||
catch: function (rejected) { return handler(0, rejected); } }, | ||
function (value) { handler(is, 1, value); }, | ||
function (reason) { handler(is, 0, reason); }); | ||
return callback; | ||
} | ||
// Finalizes the promise by resolving/rejecting it with the transformed value | ||
function finalize(promise, resolve, reject, value, transform) { | ||
setImmediate(function () { | ||
try { | ||
// Transform the value through and check whether it's a promise | ||
value = transform(value); | ||
transform = value && (is(obj, value) | is(func, value)) && value.then; | ||
// Return the result if it's not a promise | ||
if (!is(func, transform)) | ||
resolve(value); | ||
// If it's a promise, make sure it's not circular | ||
else if (value == promise) | ||
reject(TypeError()); | ||
// Take over the promise's state | ||
else | ||
transform.call(value, resolve, reject); | ||
} | ||
catch (error) { reject(error); } | ||
}); | ||
} | ||
// Export the main module | ||
module.exports = Promise; | ||
// Creates a resolved promise | ||
Promise.resolve = ResolvedPromise; | ||
function ResolvedPromise(value) { return Promise(function (resolve) { resolve(value); }); } | ||
// Creates a rejected promise | ||
Promise.reject = function (reason) { return Promise(function (resolve, reject) { reject(reason); }); }; | ||
// Transforms an array of promises into a promise for an array | ||
Promise.all = function (promises) { | ||
return Promise(function (resolve, reject, count, values) { | ||
// Array of collected values | ||
values = []; | ||
// Resolve immediately if there are no promises | ||
count = promises.length || resolve(values); | ||
// Transform all elements (`map` is shorter than `forEach`) | ||
promises.map(function (promise, index) { | ||
ResolvedPromise(promise).then( | ||
// Store the value and resolve if it was the last | ||
function (value) { | ||
values[index] = value; | ||
--count || resolve(values); | ||
}, | ||
// Reject if one element fails | ||
reject); | ||
}); | ||
}); | ||
}; | ||
})('f', 'o'); | ||
/***/ }, | ||
/* 5 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var hash = __webpack_require__(6); | ||
var Promise = null; | ||
var isCordova = typeof cordova !== 'undefined'; | ||
if(!isCordova) { | ||
window.ProgressEvent = function ProgressEvent(){} | ||
} | ||
/* Cordova File Cache x */ | ||
function FileCache(options){ | ||
// cordova-promise-fs | ||
this._fs = options.fs; | ||
if(!this._fs) { | ||
throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.'); | ||
} | ||
// Use Promises from fs. | ||
Promise = this._fs.Promise; | ||
// 'mirror' mirrors files structure from "serverRoot" to "localRoot" | ||
// 'hash' creates a 1-deep filestructure, where the filenames are hashed server urls (with extension) | ||
this._mirrorMode = options.mode !== 'hash'; | ||
this._retry = options.retry || [500,1500,8000]; | ||
this._cacheBuster = !!options.cacheBuster; | ||
// normalize path | ||
this._localRoot = options.localRoot || 'data'; | ||
if(this._localRoot[this._localRoot.length -1] !== '/') this._localRoot += '/'; | ||
if(this._localRoot[0] !== '/') this._localRoot = '/' + this._localRoot; | ||
this._serverRoot = options.serverRoot || ''; | ||
if(!!this._serverRoot && this._serverRoot[this._serverRoot.length-1] !== '/') this._serverRoot += '/'; | ||
if(this._serverRoot === './') this._serverRoot = ''; | ||
// set internal variables | ||
this._downloading = []; // download promises | ||
this._added = []; // added files | ||
this._cached = {}; // cached files | ||
// list existing cache contents | ||
this.ready = this.list(); | ||
} | ||
/** | ||
* Helper to cache all 'internalURL' and 'URL' for quick synchronous access | ||
* to the cached files. | ||
*/ | ||
FileCache.prototype.list = function list(){ | ||
var self = this; | ||
return new Promise(function(resolve,reject){ | ||
self._fs.list(self._localRoot,'rfe').then(function(entries){ | ||
self._cached = {}; | ||
entries = entries.map(function(entry){ | ||
self._cached[entry.fullPath] = { | ||
toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(), | ||
toURL: entry.toURL(), | ||
}; | ||
return entry.fullPath; | ||
}); | ||
resolve(entries); | ||
},function(){ | ||
resolve([]); | ||
}); | ||
}); | ||
}; | ||
FileCache.prototype.add = function add(urls){ | ||
if(!urls) urls = []; | ||
if(typeof urls === 'string') urls = [urls]; | ||
var self = this; | ||
urls.forEach(function(url){ | ||
url = self.toServerURL(url); | ||
if(self._added.indexOf(url) === -1) { | ||
self._added.push(url); | ||
} | ||
}); | ||
return self.isDirty(); | ||
}; | ||
FileCache.prototype.remove = function remove(urls,returnPromises){ | ||
if(!urls) urls = []; | ||
var promises = []; | ||
if(typeof urls === 'string') urls = [urls]; | ||
var self = this; | ||
urls.forEach(function(url){ | ||
var index = self._added.indexOf(self.toServerURL(url)); | ||
if(index >= 0) self._added.splice(index,1); | ||
var path = self.toPath(url); | ||
promises.push(self._fs.remove(path)); | ||
delete self._cached[path]; | ||
}); | ||
return returnPromises? Promise.all(promises): self.isDirty(); | ||
}; | ||
FileCache.prototype.getDownloadQueue = function(){ | ||
var self = this; | ||
var queue = self._added.filter(function(url){ | ||
return !self.isCached(url); | ||
}); | ||
return queue; | ||
}; | ||
FileCache.prototype.getAdded = function() { | ||
return this._added; | ||
}; | ||
FileCache.prototype.isDirty = function isDirty(){ | ||
return this.getDownloadQueue().length > 0; | ||
}; | ||
FileCache.prototype.download = function download(onprogress){ | ||
var fs = this._fs; | ||
var self = this; | ||
self.abort(); | ||
return new Promise(function(resolve,reject){ | ||
// make sure cache directory exists and that | ||
// we have retrieved the latest cache contents | ||
// to avoid downloading files we already have! | ||
fs.ensure(self._localRoot).then(function(){ | ||
return self.list(); | ||
}).then(function(){ | ||
// no dowloads needed, resolve | ||
if(!self.isDirty()) { | ||
resolve(self); | ||
return; | ||
} | ||
// keep track of number of downloads! | ||
var queue = self.getDownloadQueue(); | ||
var started = []; | ||
var index = self._downloading.length; | ||
var done = self._downloading.length; | ||
var total = self._downloading.length + queue.length; | ||
// download every file in the queue (which is the diff from _added with _cached) | ||
queue.forEach(function(url){ | ||
var path = self.toPath(url); | ||
// augment progress event with index/total stats | ||
var onSingleDownloadProgress; | ||
if(typeof onprogress === 'function') { | ||
onSingleDownloadProgress = function(ev){ | ||
ev.queueIndex = index; | ||
ev.queueSize = total; | ||
ev.url = url; | ||
ev.path = path; | ||
ev.percentage = index / total; | ||
if(ev.loaded > 0 && ev.total > 0 && index !== total){ | ||
ev.percentage += (ev.loaded / ev.total) / total; | ||
} | ||
if(started.indexOf(url) < 0) { | ||
started.push(url); | ||
index++; | ||
} | ||
onprogress(ev); | ||
}; | ||
} | ||
// callback | ||
var onDone = function(){ | ||
done++; | ||
// when we're done | ||
if(done === total) { | ||
// reset downloads | ||
self._downloading = []; | ||
// check if we got everything | ||
self.list().then(function(){ | ||
// final progress event! | ||
if(onSingleDownloadProgress) onSingleDownloadProgress(new ProgressEvent()); | ||
// Yes, we're not dirty anymore! | ||
if(!self.isDirty()) { | ||
resolve(self); | ||
// Aye, some files got left behind! | ||
} else { | ||
reject(self.getDownloadQueue()); | ||
} | ||
},reject); | ||
} | ||
}; | ||
var downloadUrl = url; | ||
if(self._cacheBuster) downloadUrl += "?"+Date.now(); | ||
var download = fs.download(url,path,{retry:self._retry},onSingleDownloadProgress); | ||
download.then(onDone,onDone); | ||
self._downloading.push(download); | ||
}); | ||
},reject); | ||
}); | ||
}; | ||
FileCache.prototype.abort = function abort(){ | ||
this._downloading.forEach(function(download){ | ||
download.abort(); | ||
}); | ||
this._downloading = []; | ||
}; | ||
FileCache.prototype.isCached = function isCached(url){ | ||
url = this.toPath(url); | ||
return !!this._cached[url]; | ||
}; | ||
FileCache.prototype.clear = function clear(){ | ||
this._cached = {}; | ||
return this._fs.removeDir(this._localRoot); | ||
}; | ||
/** | ||
* Helpers to output to various formats | ||
*/ | ||
FileCache.prototype.toInternalURL = function toInternalURL(url){ | ||
path = this.toPath(url); | ||
if(this._cached[path]) return this._cached[path].toInternalURL; | ||
return this._fs.toInternalURLSync(path); | ||
}; | ||
FileCache.prototype.get = function get(url){ | ||
path = this.toPath(url); | ||
if(this._cached[path]) return this._cached[path].toInternalURL; | ||
return this.toServerURL(url); | ||
}; | ||
FileCache.prototype.toDataURL = function toDataURL(url){ | ||
return this._fs.toDataURL(this.toPath(url)); | ||
}; | ||
FileCache.prototype.toURL = function toURL(url){ | ||
path = this.toPath(url); | ||
return this._cached[path]? this._cached[path].toURL: url; | ||
}; | ||
FileCache.prototype.toServerURL = function toServerURL(path){ | ||
if(path[0] === '/') path = path.substr(1); | ||
return path.indexOf('://') < 0? this._serverRoot + path: path; | ||
}; | ||
/** | ||
* Helper to transform remote URL to a local path (for cordova-promise-fs) | ||
*/ | ||
FileCache.prototype.toPath = function toPath(url){ | ||
if(this._mirrorMode) { | ||
url = url || ''; | ||
len = this._serverRoot.length; | ||
if(url.substr(0,len) !== this._serverRoot) { | ||
if(url[0] === '/') url = url.substr(1); | ||
return this._localRoot + url; | ||
} else { | ||
return this._localRoot + url.substr(len); | ||
} | ||
} else { | ||
return this._localRoot + hash(url) + url.substr(url.lastIndexOf('.')); | ||
} | ||
}; | ||
module.exports = FileCache; | ||
/***/ }, | ||
/* 6 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/** | ||
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011) | ||
* | ||
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a> | ||
* @see http://github.com/garycourt/murmurhash-js | ||
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a> | ||
* @see http://sites.google.com/site/murmurhash/ | ||
* | ||
* @param {string} key ASCII only | ||
* @param {number} seed Positive integer only | ||
* @return {number} 32-bit positive integer hash | ||
*/ | ||
function murmurhash3_32_gc(key, seed) { | ||
var remainder, bytes, h1, h1b, c1, c1b, c2, c2b, k1, i; | ||
remainder = key.length & 3; // key.length % 4 | ||
bytes = key.length - remainder; | ||
h1 = seed; | ||
c1 = 0xcc9e2d51; | ||
c2 = 0x1b873593; | ||
i = 0; | ||
while (i < bytes) { | ||
k1 = | ||
((key.charCodeAt(i) & 0xff)) | | ||
((key.charCodeAt(++i) & 0xff) << 8) | | ||
((key.charCodeAt(++i) & 0xff) << 16) | | ||
((key.charCodeAt(++i) & 0xff) << 24); | ||
++i; | ||
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff; | ||
k1 = (k1 << 15) | (k1 >>> 17); | ||
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff; | ||
h1 ^= k1; | ||
h1 = (h1 << 13) | (h1 >>> 19); | ||
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff; | ||
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16)); | ||
} | ||
k1 = 0; | ||
switch (remainder) { | ||
case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16; | ||
case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8; | ||
case 1: k1 ^= (key.charCodeAt(i) & 0xff); | ||
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff; | ||
k1 = (k1 << 15) | (k1 >>> 17); | ||
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff; | ||
h1 ^= k1; | ||
} | ||
h1 ^= key.length; | ||
h1 ^= h1 >>> 16; | ||
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff; | ||
h1 ^= h1 >>> 13; | ||
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff; | ||
h1 ^= h1 >>> 16; | ||
return h1 >>> 0; | ||
} | ||
module.exports = murmurhash3_32_gc; | ||
/***/ } | ||
/******/ ]) |
@@ -1,1 +0,1 @@ | ||
(function(){window.pegasus=function t(e,o){o=new XMLHttpRequest;o.open("GET",e);e=[];o.onreadystatechange=o.then=function(t,n,s){if(t.call)e=[t,n];if(o.readyState==4){s=e[0|o.status/400];if(s)s(o.status===200||o.status===0?JSON.parse(o.responseText):o)}};o.send();return o};function t(t,e,o){if(!t.load){console.error("Manifest has nothing to load (manifest.load is empty).",t);return}var n,s=document.getElementsByTagName("head")[0],a=t.load.concat();function r(){var e=a.shift();if(!e)return;if(e[0]==="/")e=e.substr(1);e=t.root+e;if(e.substr(-3)===".js"){n=document.createElement("script");n.type="text/javascript";n.src=e;n.onload=r}else{n=document.createElement("link");n.rel="stylesheet";n.href=e;n.type="text/css";setTimeout(r,0)}s.appendChild(n)}t.root=t.root||"./";if(t.root.length>0&&t.root[t.root.length-1]!=="/")t.root+="/";if(!e)localStorage.setItem("manifest",JSON.stringify(t));if(t.root.substr(0,7)==="cdvfile"){document.addEventListener("deviceready",r,false)}else{r()}window.Manifest=t;if(e){setTimeout(function(){if(!window.BOOTSTRAP_OK){localStorage.removeItem("manifest");location.reload()}},o)}}var e=JSON.parse(localStorage.getItem("manifest"));var o=document.querySelector("script[manifest]");if(!e){var n=(o?o.getAttribute("manifest"):null)||"manifest.json";pegasus(n).then(t,function(t){console.error("Could not download "+n+": "+t.status)})}else{t(e,true,o.getAttribute("timeout")||1e4)}})(); | ||
(function(){window.pegasus=function t(e,o){o=new XMLHttpRequest;o.open("GET",e);e=[];o.onreadystatechange=o.then=function(t,n,s){if(t.call)e=[t,n];if(o.readyState==4){s=e[0|o.status/400];if(s)s(o.status===200||o.status===0?JSON.parse(o.responseText):o)}};o.send();return o};function t(t,e,o){if(!t.load){console.error("Manifest has nothing to load (manifest.load is empty).",t);return}var n,s=document.getElementsByTagName("head")[0],a=t.load.concat();function r(){var e=a.shift();if(!e)return;if(e[0]==="/")e=e.substr(1);e=t.root+e;if(e.substr(-3)===".js"){n=document.createElement("script");n.type="text/javascript";n.src=e;n.onload=r}else{n=document.createElement("link");n.rel="stylesheet";n.href=e;n.type="text/css";setTimeout(r,0)}s.appendChild(n)}t.root=t.root||"./";if(t.root.length>0&&t.root[t.root.length-1]!=="/")t.root+="/";if(!e)localStorage.setItem("manifest",JSON.stringify(t));if(t.root.substr(0,7)==="cdvfile"){document.addEventListener("deviceready",r,false)}else{r()}window.Manifest=t;if(e){setTimeout(function(){if(!window.BOOTSTRAP_OK){localStorage.removeItem("manifest");location.reload()}},o)}}window.Manifest={};var e=JSON.parse(localStorage.getItem("manifest"));var o=document.querySelector("script[manifest]");if(!e){var n=(o?o.getAttribute("manifest"):null)||"manifest.json";pegasus(n).then(t,function(t){console.error("Could not download "+n+": "+t.status)})}else{t(e,true,o.getAttribute("timeout")||1e4)}})(); |
@@ -76,2 +76,3 @@ var CordovaAppLoader = | ||
// initialize a file cache | ||
if(options.mode) options.mode = 'mirror'; | ||
this.cache = new CordovaFileCache(options); | ||
@@ -418,6 +419,10 @@ | ||
if(this._cached[path]) return this._cached[path].toInternalURL; | ||
return this.toServerURL(path); | ||
return this._fs.toInternalURLSync(path); | ||
}; | ||
FileCache.prototype.get = FileCache.prototype.toInternalURL; | ||
FileCache.prototype.get = function get(url){ | ||
path = this.toPath(url); | ||
if(this._cached[path]) return this._cached[path].toInternalURL; | ||
return this.toServerURL(url); | ||
}; | ||
@@ -430,3 +435,3 @@ FileCache.prototype.toDataURL = function toDataURL(url){ | ||
path = this.toPath(url); | ||
return this._cached[path]? this._cached[path].toURL: this.toServerURL(url); | ||
return this._cached[path]? this._cached[path].toURL: url; | ||
}; | ||
@@ -433,0 +438,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
498348
0
8013
386