Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

cordova-app-loader

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

cordova-app-loader - npm Package Compare versions

Comparing version 0.7.0 to 0.8.0

2

bower.json
{
"name": "cordova-app-loadaer",
"main": "dist/CordovaAppLoader.js",
"version": "0.7.0",
"version": "0.8.0",
"homepage": "https://github.com/markmarijnissen/cordova-file-cache",

@@ -6,0 +6,0 @@ "authors": [

@@ -60,12 +60,2 @@ /******/ (function(modules) { // webpackBootstrap

function createFilemap(files){
var result = {};
Object.keys(files).forEach(function(key){
var filename = files[key].filename;
if(filename[0] === '/') filename = filename[0].substr(1);
result[filename] = files[key];
});
return result;
}
function AppLoader(options){

@@ -77,6 +67,7 @@ if(!options) throw new Error('CordovaAppLoader has no options!');

Promise = options.fs.Promise;
// initialize variables
// initialize variables
this.manifest = window.Manifest;
this.newManifest = null;
this._lastUpdateManifest = localStorage.getItem('last_update_manifest');

@@ -87,3 +78,3 @@ // normalize serverRoot and set remote manifest url

this.newManifestUrl = options.serverRoot + (options.manifest || 'manifest.json');
// initialize a file cache

@@ -100,2 +91,12 @@ if(options.mode) options.mode = 'mirror';

AppLoader.prototype._createFilemap = function(files){
var result = {};
var normalize = this.cache._fs.normalize;
Object.keys(files).forEach(function(key){
files[key].filename = normalize(files[key].filename);
result[files[key].filename] = files[key];
});
return result;
};
AppLoader.prototype.check = function(newManifest){

@@ -111,2 +112,7 @@ var self = this, manifest = this.manifest;

function checkManifest(newManifest){
if(JSON.stringify(newManifest) === self._lastUpdateManifest) {
resolve(false);
return;
}
// make sure cache is ready for the DIFF operations!

@@ -119,4 +125,4 @@ self.cache.ready.then(function(list){

var newFiles = createFilemap(newManifest.files);
var oldFiles = createFilemap(manifest.files);
var newFiles = self._createFilemap(newManifest.files);
var oldFiles = self._createFilemap(manifest.files);

@@ -134,4 +140,3 @@ // Create the diff

.map(function(file){
if(file[0] === '/') file = file.substr(1);
return file.substr(self.cache._localRoot.length);
return file.substr(self.cache.localRoot.length);
})

@@ -146,3 +151,3 @@ .filter(function(file){

self.newManifest = newManifest;
self.newManifest.root = self.cache.toInternalURL('/') + (self.newManifest.root || '');
self.newManifest.root = self.cache.localInternalURL;
resolve(true);

@@ -201,3 +206,5 @@ } else {

// update manifest
localStorage.setItem('manifest',JSON.stringify(this.newManifest));
json = JSON.stringify(this.newManifest);
localStorage.setItem('manifest',json);
localStorage.setItem('last_update_manifest',json);
if(reload !== false) location.reload();

@@ -232,7 +239,2 @@ return true;

function removeFirstSlash(path){
if(path[0] === '/') path = path.substr(1);
return path;
}
/* Cordova File Cache x */

@@ -256,9 +258,5 @@ function FileCache(options){

// normalize path
this._localRoot = removeFirstSlash(options.localRoot || 'data');
if(this._localRoot[this._localRoot.length -1] !== '/') this._localRoot += '/';
this.localRoot = this._fs.normalize(options.localRoot || 'data');
this.serverRoot = this._fs.normalize(options.serverRoot || '');
this._serverRoot = options.serverRoot || '';
if(!!this._serverRoot && this._serverRoot[this._serverRoot.length-1] !== '/') this._serverRoot += '/';
if(this._serverRoot === './') this._serverRoot = '';
// set internal variables

@@ -270,3 +268,6 @@ this._downloading = []; // download promises

// list existing cache contents
this.ready = this._fs.ensure(this._localRoot).then(function(){
this.ready = this._fs.ensure(this.localRoot)
.then(function(entry){
self.localInternalURL = isCordova? entry.toInternalURL(): entry.toURL();
self.localUrl = entry.toURL();
return self.list();

@@ -283,6 +284,6 @@ });

return new Promise(function(resolve,reject){
self._fs.list(self._localRoot,'rfe').then(function(entries){
self._fs.list(self.localRoot,'rfe').then(function(entries){
self._cached = {};
entries = entries.map(function(entry){
var fullPath = removeFirstSlash(entry.fullPath);
var fullPath = self._fs.normalize(entry.fullPath);
self._cached[fullPath] = {

@@ -292,3 +293,3 @@ toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(),

};
return entry.fullPath;
return fullPath;
});

@@ -355,3 +356,3 @@ resolve(entries);

// to avoid downloading files we already have!
fs.ensure(self._localRoot).then(function(){
fs.ensure(self.localRoot).then(function(){
return self.list();

@@ -441,4 +442,4 @@ }).then(function(){

this._cached = {};
return this._fs.removeDir(this._localRoot).then(function(){
return self._fs.ensure(self._localRoot);
return this._fs.removeDir(this.localRoot).then(function(){
return self._fs.ensure(self.localRoot);
});

@@ -472,4 +473,4 @@ };

FileCache.prototype.toServerURL = function toServerURL(path){
if(path[0] === '/') path = path.substr(1);
return path.indexOf('://') < 0? this._serverRoot + path: path;
path = this._fs.normalize(path);
return path.indexOf('://') < 0? this.serverRoot + path: path;
};

@@ -482,12 +483,11 @@

if(this._mirrorMode) {
url = url || '';
len = this._serverRoot.length;
if(url.substr(0,len) !== this._serverRoot) {
url = removeFirstSlash(url);
return this._localRoot + url;
url = url = this._fs.normalize(url || '');
len = this.serverRoot.length;
if(url.substr(0,len) !== this.serverRoot) {
return this.localRoot + url;
} else {
return this._localRoot + url.substr(len);
return this.localRoot + url.substr(len);
}
} else {
return this._localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
return this.localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
}

@@ -519,16 +519,18 @@ };

function dirname(str) {
var parts = str.split('/');
if(parts.length > 1) {
parts.splice(parts.length-1,1);
return parts.join('/');
} else {
return '';
}
str = str.substr(0,str.lastIndexOf('/')+1);
if(str[0] === '/') str = str.substr(1);
return str;
}
function filename(str) {
var parts = str.split('/');
return parts[parts.length-1];
return str.substr(str.lastIndexOf('/')+1);
}
function normalize(str){
if(str[0] === '/') str = str.substr(1);
if(!!str && str.indexOf('.') < 0 && str[str.length-1] !== '/') str += '/';
if(str === './') str = '';
return str;
}
var transferQueue = [], // queued fileTransfers

@@ -544,3 +546,3 @@ inprogress = 0; // currently active filetransfers

if(!Promise) { throw new Error("No Promise library given in options.Promise"); }
/* default options */

@@ -611,8 +613,2 @@ this.options = options = options || {};

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* ensure directory exists */

@@ -634,2 +630,65 @@ function ensure(folders) {

/* get file file */
function file(path,options){
path = normalize(path);
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){
path = normalize(path);
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);
});
}
/* 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 = [ResolvedPromise(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);
});
}
/* does file exist? If so, resolve with fileEntry, if not, resolve with false. */

@@ -653,2 +712,8 @@ function exists(path){

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* convert path to URL to be used in JS/CSS/HTML */

@@ -666,4 +731,4 @@ function toURL(path) {

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'cdvfile://localhost/'+(options.persistent? 'persistent':'temporary') + path;
path = normalize(path);
return 'cdvfile://localhost/'+(options.persistent? 'persistent/':'temporary/') + path;
};

@@ -679,4 +744,4 @@

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'filesystem:'+location.origin+(options.persistent? '/persistent':'/temporary') + path;
path = normalize(path);
return 'filesystem:'+location.origin+(options.persistent? '/persistent/':'/temporary/') + path;
};

@@ -689,33 +754,4 @@

};
}
/* 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 */

@@ -737,2 +773,8 @@ function read(path,method) {

/* convert path to base64 date URI */
function toDataURL(path) {
return read(path,'readAsDataURL');
}
function readJSON(path){

@@ -811,39 +853,2 @@ return read(path).then(JSON.parse);

/* 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 = [ResolvedPromise(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

@@ -855,3 +860,3 @@ function popTransferQueue(){

inprogress++;
// fetch filetranfer, method-type (isDownload) and arguments

@@ -867,3 +872,3 @@ var args = transferQueue.pop();

var transferOptions = args.shift();
if(ft._aborted) {

@@ -882,3 +887,3 @@ inprogress--;

// Promise callback to check if there are any more queued transfers
// Promise callback to check if there are any more queued transfers
function nextTransfer(result){

@@ -894,3 +899,3 @@ inprogress--; // decrement counter to free up one space to start transfers again!

transferOptions = {};
}
}
serverUrl = encodeURI(serverUrl);

@@ -904,3 +909,3 @@ if(isCordova) localPath = toInternalURLSync(localPath);

transferOptions.retry = transferOptions.retry.concat();
var ft = new FileTransfer();

@@ -950,2 +955,3 @@ onprogress = onprogress || transferOptions.onprogress;

fs: fs,
normalize: normalize,
file: file,

@@ -952,0 +958,0 @@ filename: filename,

@@ -1,1 +0,1 @@

!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){window.CordovaAppLoader=n(1),window.CordovaFileCache=n(2),window.CordovaPromiseFS=n(3),window.Promise=n(4),window.setImmediate=window.setTimeout},function(t,e,n){function o(t){var e={};return Object.keys(t).forEach(function(n){var o=t[n].filename;"/"===o[0]&&(o=o[0].substr(1)),e[o]=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.");u=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"),t.mode&&(t.mode="mirror"),this.cache=new i(t),this._toBeDeleted=[],this._toBeDownloaded=[],this._updateReady=!1,this._checkTimeout=t.checkTimeout||1e4}var i=n(2),u=null;r.prototype.check=function(t){var e=this,n=this.manifest;return new u(function(r,i){function u(t){e.cache.ready.then(function(){if(!t.files)return void i('Downloaded Manifest has no "files" attribute.');var u=o(t.files),s=o(n.files);e._toBeDownloaded=Object.keys(u).filter(function(t){return!s[t]||s[t].version!==u[t].version||!e.cache.isCached(t)}),e.cache.list().then(function(n){e._toBeDeleted=n.map(function(t){return"/"===t[0]&&(t=t.substr(1)),t.substr(e.cache._localRoot.length)}).filter(function(t){return!u[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?u(t):(pegasus(e.newManifestUrl).then(u,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})):u.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){return"/"===t[0]&&(t=t.substr(1)),t}function r(t){var e=this;if(this._fs=t.fs,!this._fs)throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.');u=this._fs.Promise,this._mirrorMode="hash"!==t.mode,this._retry=t.retry||[500,1500,8e3],this._cacheBuster=!!t.cacheBuster,this._localRoot=o(t.localRoot||"data"),"/"!==this._localRoot[this._localRoot.length-1]&&(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._fs.ensure(this._localRoot).then(function(){return e.list()})}var i=n(5),u=null,s="undefined"!=typeof cordova;r.prototype.list=function(){var t=this;return new u(function(e){t._fs.list(t._localRoot,"rfe").then(function(n){t._cached={},n=n.map(function(e){var n=o(e.fullPath);return t._cached[n]={toInternalURL:s?e.toInternalURL():e.toURL(),toURL:e.toURL()},e.fullPath}),e(n)},function(){e([])})})},r.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()},r.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?u.all(n):o.isDirty()},r.prototype.getDownloadQueue=function(){var t=this,e=t._added.filter(function(e){return!t.isCached(e)});return e},r.prototype.getAdded=function(){return this._added},r.prototype.isDirty=function(){return this.getDownloadQueue().length>0},r.prototype.download=function(t){var e=this._fs,n=this;return n.abort(),new u(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(),u=[],s=n._downloading.length,c=n._downloading.length,a=n._downloading.length+i.length;i.forEach(function(i){var f,h=n.toPath(i);"function"==typeof t&&(f=function(e){e.queueIndex=s,e.queueSize=a,e.url=i,e.path=h,e.percentage=s/a,e.loaded>0&&e.total>0&&s!==a&&(e.percentage+=e.loaded/e.total/a),u.indexOf(i)<0&&(u.push(i),s++),t(e)});var d=function(){c++,c===a&&(n._downloading=[],n.list().then(function(){f&&f(new ProgressEvent),n.isDirty()?r(n.getDownloadQueue()):o(n)},r))},l=i;n._cacheBuster&&(l+="?"+Date.now());var p=e.download(i,h,{retry:n._retry},f);p.then(d,d),n._downloading.push(p)})},r)})},r.prototype.abort=function(){this._downloading.forEach(function(t){t.abort()}),this._downloading=[]},r.prototype.isCached=function(t){return t=this.toPath(t),!!this._cached[t]},r.prototype.clear=function(){var t=this;return this._cached={},this._fs.removeDir(this._localRoot).then(function(){return t._fs.ensure(t._localRoot)})},r.prototype.toInternalURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:t},r.prototype.get=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:this.toServerURL(t)},r.prototype.toDataURL=function(t){return this._fs.toDataURL(this.toPath(t))},r.prototype.toURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toURL:t},r.prototype.toServerURL=function(t){return"/"===t[0]&&(t=t.substr(1)),t.indexOf("://")<0?this._serverRoot+t:t},r.prototype.toPath=function(t){return this._mirrorMode?(t=t||"",len=this._serverRoot.length,t.substr(0,len)!==this._serverRoot?(t=o(t),this._localRoot+t):this._localRoot+t.substr(len)):this._localRoot+i(t)+t.substr(t.lastIndexOf("."))},t.exports=r},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 u(t){return new x(function(e){return e(t)})}function s(t){return c(n(t)).then(function(){return d(t,{create:!0})})}function c(t){return new x(function(n,o){return C.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 a(t){return new x(function(e,n){d(t).then(function(t){e(t)},function(t){1===t.code?e(!1):n(t)})})}function f(t){return d(t).then(function(t){return t.toURL()})}function h(t){return p(t,"readAsDataURL")}function d(t,e){return e=e||{},new x(function(n,o){return C.then(function(r){r.root.getFile(t,e,n,o)},o)})}function l(t,e){return e=e||{},new x(function(n,o){return C.then(function(r){t&&"/"!==t?r.root.getDirectory(t,e,n,o):n(r.root)},o)})}function p(t,e){return e=e||"readAsText",d(t).then(function(t){return new x(function(n,o){t.file(function(t){var o=new FileReader;o.onloadend=function(){n(this.result)},o[e](t)},o)})})}function w(t){return p(t).then(JSON.parse)}function v(t,e,o){return c(n(t)).then(function(){return d(t,{create:!0})}).then(function(t){return new x(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 y(t,e){return c(n(e)).then(function(n){return d(t).then(function(t){return new x(function(r,i){t.moveTo(n,o(e),r,i)})})})}function _(t,e){return c(n(e)).then(function(n){return d(t).then(function(t){return new x(function(r,i){t.copyTo(n,o(e),r,i)})})})}function m(t,e){var n=e?d:a;return new x(function(e,o){n(t).then(function(t){t!==!1?t.remove(e,o):e(1)},o)}).then(function(t){return 1===t?!1:!0})}function g(t){return l(t).then(function(t){return new x(function(e,n){t.removeRecursively(e,n)})})}function R(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 x(function(e,s){return l(t).then(function(t){var c=t.createReader();c.readEntries(function(t){var c=[u(t)];n&&t.filter(function(t){return t.isDirectory}).forEach(function(t){c.push(R(t.fullPath,"re"))}),x.all(c).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 D(){for(;r.length>0&&i<t.concurrency;){i++;var e=r.pop(),n=e.shift(),o=e.shift(),u=e.shift(),s=e.shift(),c=e.shift(),a=e.shift(),f=e.shift(),h=e.shift();n._aborted?i--:o?(n.download.call(n,u,s,c,a,f,h),n.onprogress&&n.onprogress(new ProgressEvent)):n.upload.call(n,s,u,c,a,h,f)}}function L(t){return i--,D(),t}function U(e,n,o,u,s){"function"==typeof u&&(s=u,u={}),n=encodeURI(n),E&&(o=A(o)),u=u||{},u.retry&&u.retry.length||(u.retry=t.retry),u.retry=u.retry.concat();var c=new FileTransfer;s=s||u.onprogress,"function"==typeof s&&(c.onprogress=s);var a=new x(function(t,s){var a=function(i){if(0===u.retry.length)s(i);else{r.unshift([c,e,n,o,t,a,u.trustAllHosts||!1,u]);var f=u.retry.shift();f>0?setTimeout(L,f):L()}};u.retry.unshift(0),i++,a()});return a.then(L,L),a.progress=function(t){return c.onprogress=t,a},a.abort=function(){return c._aborted=!0,c.abort(),a},a}function b(t,e,n,o){return U(!0,t,e,n,o)}function S(t,e,n,o){return U(!1,e,t,n,o)}var x=t.Promise||window.Promise;if(!x)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 P,E="undefined"!=typeof cordova;E?P=new x(function(t,e){document.addEventListener("deviceready",t,!1),setTimeout(function(){e(new Error("deviceready has not fired after 5 seconds."))},5100)}):(P=u(!0),"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.ProgressEvent=function(){}):window.requestFileSystem=function(t,e,n,o){o(new Error("requestFileSystem not supported!"))});var C=new x(function(e,n){P.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)});C.then(function(t){window.__fs=t},function(t){console.error("Could not get Cordova FileSystem:",t)});var M,A;return E?(A=function(e){return"/"!==e[0]&&(e="/"+e),"cdvfile://localhost/"+(t.persistent?"persistent":"temporary")+e},M=function(t){return d(t).then(function(t){return t.toInternalURL()})}):(A=function(e){return"/"!==e[0]&&(e="/"+e),"filesystem:"+location.origin+(t.persistent?"/persistent":"/temporary")+e},M=function(t){return d(t).then(function(t){return t.toURL()})}),{fs:C,file:d,filename:o,dir:l,dirname:n,create:s,read:p,readJSON:w,write:v,move:y,copy:_,remove:m,removeDir:g,list:R,ensure:c,exists:a,download:b,upload:S,toURL:f,isCordova:E,toInternalURLSync:A,toInternalURL:M,toDataURL:h,deviceready:P,options:t,Promise:x}}},function(t){!function(e,n){function o(t,e){return(typeof e)[0]==t}function r(t,u){return u=function s(c,a,f,h,d,l){function p(t){return function(e){d&&(d=0,s(o,t,e))}}if(h=s.q,c!=o)return r(function(t,e){h.push({p:this,r:t,j:e,1:c,0:a})});if(f&&o(e,f)|o(n,f))try{d=f.then}catch(t){a=0,f=t}if(o(e,d))try{d.call(f,p(1),a=p(0))}catch(t){a(t)}else for(u=function(n,u){return o(e,n=a?n:u)?r(function(t,e){i(this,t,e,f,n)}):t},l=0;l<h.length;)d=h[l++],o(e,c=d[a])?i(d.p,d.r,d.j,f,c):(a?d.r:d.j)(f)},u.q=[],t.call(t={then:function(t,e){return u(t,e)},catch:function(t){return u(0,t)}},function(t){u(o,1,t)},function(t){u(o,0,t)}),t}function i(t,r,i,u,s){setImmediate(function(){try{u=s(u),s=u&&o(n,u)|o(e,u)&&u.then,o(e,s)?u==t?i(TypeError()):s.call(u,r,i):r(u)}catch(t){i(t)}})}function u(t){return r(function(e){e(t)})}t.exports=r,r.resolve=u,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){u(t).then(function(t){r[i]=t,--o||e(r)},n)})})}}("f","o")},function(t){function e(t,e){var n,o,r,i,u,s,c,a;for(n=3&t.length,o=t.length-n,r=e,u=3432918353,s=461845907,a=0;o>a;)c=255&t.charCodeAt(a)|(255&t.charCodeAt(++a))<<8|(255&t.charCodeAt(++a))<<16|(255&t.charCodeAt(++a))<<24,++a,c=(65535&c)*u+(((c>>>16)*u&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*s+(((c>>>16)*s&65535)<<16)&4294967295,r^=c,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(c=0,n){case 3:c^=(255&t.charCodeAt(a+2))<<16;case 2:c^=(255&t.charCodeAt(a+1))<<8;case 1:c^=255&t.charCodeAt(a),c=(65535&c)*u+(((c>>>16)*u&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*s+(((c>>>16)*s&65535)<<16)&4294967295,r^=c}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(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){window.CordovaAppLoader=n(1),window.CordovaFileCache=n(2),window.CordovaPromiseFS=n(3),window.Promise=n(4),window.setImmediate=window.setTimeout},function(t,e,n){function o(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.");i=t.fs.Promise,this.manifest=window.Manifest,this.newManifest=null,this._lastUpdateManifest=localStorage.getItem("last_update_manifest"),t.serverRoot=t.serverRoot||"",t.serverRoot&&"/"!==t.serverRoot[t.serverRoot.length-1]&&(t.serverRoot+="/"),this.newManifestUrl=t.serverRoot+(t.manifest||"manifest.json"),t.mode&&(t.mode="mirror"),this.cache=new r(t),this._toBeDeleted=[],this._toBeDownloaded=[],this._updateReady=!1,this._checkTimeout=t.checkTimeout||1e4}var r=n(2),i=null;o.prototype._createFilemap=function(t){var e={},n=this.cache._fs.normalize;return Object.keys(t).forEach(function(o){t[o].filename=n(t[o].filename),e[t[o].filename]=t[o]}),e},o.prototype.check=function(t){var e=this,n=this.manifest;return new i(function(o,r){function i(t){return JSON.stringify(t)===e._lastUpdateManifest?void o(!1):void e.cache.ready.then(function(){if(!t.files)return void r('Downloaded Manifest has no "files" attribute.');var i=e._createFilemap(t.files),a=e._createFilemap(n.files);e._toBeDownloaded=Object.keys(i).filter(function(t){return!a[t]||a[t].version!==i[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!i[t]}).concat(e._toBeDownloaded),e._toBeDeleted.length>0||e._toBeDownloaded.length>0?(e.newManifest=t,e.newManifest.root=e.cache.localInternalURL,o(!0)):o(!1)},r)},r)}"string"==typeof t&&(e.newManifestUrl=t,t=void 0),"object"==typeof t?i(t):(pegasus(e.newManifestUrl).then(i,r),setTimeout(function(){r(new Error("timeout"))},e._checkTimeout))})},o.prototype.canDownload=function(){return!!this.newManifest&&!this._updateReady},o.prototype.canUpdate=function(){return this._updateReady},o.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})):i.resolve(null)},o.prototype.update=function(t){return this._updateReady?(json=JSON.stringify(this.newManifest),localStorage.setItem("manifest",json),localStorage.setItem("last_update_manifest",json),t!==!1&&location.reload(),!0):!1},o.prototype.clear=function(){return localStorage.removeItem("manifest"),this.cache.clear()},o.prototype.reset=function(){return this.clear().then(function(){location.reload()},function(){location.reload()})},t.exports=o},function(t,e,n){function o(t){var e=this;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=this._fs.normalize(t.localRoot||"data"),this.serverRoot=this._fs.normalize(t.serverRoot||""),this._downloading=[],this._added=[],this._cached={},this.ready=this._fs.ensure(this.localRoot).then(function(t){return e.localInternalURL=a?t.toInternalURL():t.toURL(),e.localUrl=t.toURL(),e.list()})}var r=n(5),i=null,a="undefined"!=typeof cordova;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){var n=t._fs.normalize(e.fullPath);return t._cached[n]={toInternalURL:a?e.toInternalURL():e.toURL(),toURL:e.toURL()},n}),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(),a=[],s=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=s,e.queueSize=c,e.url=i,e.path=l,e.percentage=s/c,e.loaded>0&&e.total>0&&s!==c&&(e.percentage+=e.loaded/e.total/c),a.indexOf(i)<0&&(a.push(i),s++),t(e)});var h=function(){u++,u===c&&(n._downloading=[],n.list().then(function(){f&&f(new ProgressEvent),n.isDirty()?r(n.getDownloadQueue()):o(n)},r))},d=i;n._cacheBuster&&(d+="?"+Date.now());var p=e.download(i,l,{retry:n._retry},f);p.then(h,h),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(){var t=this;return this._cached={},this._fs.removeDir(this.localRoot).then(function(){return t._fs.ensure(t.localRoot)})},o.prototype.toInternalURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:t},o.prototype.get=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:this.toServerURL(t)},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:t},o.prototype.toServerURL=function(t){return t=this._fs.normalize(t),t.indexOf("://")<0?this.serverRoot+t:t},o.prototype.toPath=function(t){return this._mirrorMode?(t=t=this._fs.normalize(t||""),len=this.serverRoot.length,t.substr(0,len)!==this.serverRoot?this.localRoot+t:this.localRoot+t.substr(len)):this.localRoot+r(t)+t.substr(t.lastIndexOf("."))},t.exports=o},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){return t=t.substr(0,t.lastIndexOf("/")+1),"/"===t[0]&&(t=t.substr(1)),t}function o(t){return t.substr(t.lastIndexOf("/")+1)}function r(t){return"/"===t[0]&&(t=t.substr(1)),t&&t.indexOf(".")<0&&"/"!==t[t.length-1]&&(t+="/"),"./"===t&&(t=""),t}var i=[],a=0;t.exports=function(t){function s(t){return new E(function(e){return e(t)})}function u(t){return new E(function(n,o){return C.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 c(t,e){return t=r(t),e=e||{},new E(function(n,o){return C.then(function(r){r.root.getFile(t,e,n,o)},o)})}function f(t,e){return t=r(t),e=e||{},new E(function(n,o){return C.then(function(r){t&&"/"!==t?r.root.getDirectory(t,e,n,o):n(r.root)},o)})}function l(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,a){return f(t).then(function(t){var u=t.createReader();u.readEntries(function(t){var u=[s(t)];n&&t.filter(function(t){return t.isDirectory}).forEach(function(t){u.push(l(t.fullPath,"re"))}),E.all(u).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)},a)},a)},a)})}function h(t){return new E(function(e,n){c(t).then(function(t){e(t)},function(t){1===t.code?e(!1):n(t)})})}function d(t){return u(n(t)).then(function(){return c(t,{create:!0})})}function p(t){return c(t).then(function(t){return t.toURL()})}function w(t,e){return e=e||"readAsText",c(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 v(t){return w(t,"readAsDataURL")}function y(t){return w(t).then(JSON.parse)}function m(t,e,o){return u(n(t)).then(function(){return c(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 _(t,e){return u(n(e)).then(function(n){return c(t).then(function(t){return new E(function(r,i){t.moveTo(n,o(e),r,i)})})})}function g(t,e){return u(n(e)).then(function(n){return c(t).then(function(t){return new E(function(r,i){t.copyTo(n,o(e),r,i)})})})}function R(t,e){var n=e?c:h;return new E(function(e,o){n(t).then(function(t){t!==!1?t.remove(e,o):e(1)},o)}).then(function(t){return 1===t?!1:!0})}function U(t){return f(t).then(function(t){return new E(function(e,n){t.removeRecursively(e,n)})})}function L(){for(;i.length>0&&a<t.concurrency;){a++;var e=i.pop(),n=e.shift(),o=e.shift(),r=e.shift(),s=e.shift(),u=e.shift(),c=e.shift(),f=e.shift(),l=e.shift();n._aborted?a--:o?(n.download.call(n,r,s,u,c,f,l),n.onprogress&&n.onprogress(new ProgressEvent)):n.upload.call(n,s,r,u,c,l,f)}}function D(t){return a--,L(),t}function S(e,n,o,r,s){"function"==typeof r&&(s=r,r={}),n=encodeURI(n),P&&(o=M(o)),r=r||{},r.retry&&r.retry.length||(r.retry=t.retry),r.retry=r.retry.concat();var u=new FileTransfer;s=s||r.onprogress,"function"==typeof s&&(u.onprogress=s);var c=new E(function(t,s){var c=function(a){if(0===r.retry.length)s(a);else{i.unshift([u,e,n,o,t,c,r.trustAllHosts||!1,r]);var f=r.retry.shift();f>0?setTimeout(D,f):D()}};r.retry.unshift(0),a++,c()});return c.then(D,D),c.progress=function(t){return u.onprogress=t,c},c.abort=function(){return u._aborted=!0,u.abort(),c},c}function x(t,e,n,o){return S(!0,t,e,n,o)}function b(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 I,P="undefined"!=typeof cordova;P?I=new E(function(t,e){document.addEventListener("deviceready",t,!1),setTimeout(function(){e(new Error("deviceready has not fired after 5 seconds."))},5100)}):(I=s(!0),"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?m(e,r.responseText).then(n,o):o(r.status))},r.send(),r},window.ProgressEvent=function(){}):window.requestFileSystem=function(t,e,n,o){o(new Error("requestFileSystem not supported!"))});var C=new E(function(e,n){I.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)});C.then(function(t){window.__fs=t},function(t){console.error("Could not get Cordova FileSystem:",t)});var F,M;return P?(M=function(e){return e=r(e),"cdvfile://localhost/"+(t.persistent?"persistent/":"temporary/")+e},F=function(t){return c(t).then(function(t){return t.toInternalURL()})}):(M=function(e){return e=r(e),"filesystem:"+location.origin+(t.persistent?"/persistent/":"/temporary/")+e},F=function(t){return c(t).then(function(t){return t.toURL()})}),{fs:C,normalize:r,file:c,filename:o,dir:f,dirname:n,create:d,read:w,readJSON:y,write:m,move:_,copy:g,remove:R,removeDir:U,list:l,ensure:u,exists:h,download:x,upload:b,toURL:p,isCordova:P,toInternalURLSync:M,toInternalURL:F,toDataURL:v,deviceready:I,options:t,Promise:E}}},function(t){!function(e,n){function o(t,e){return(typeof e)[0]==t}function r(t,a){return a=function s(u,c,f,l,h,d){function p(t){return function(e){h&&(h=0,s(o,t,e))}}if(l=s.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{h=f.then}catch(t){c=0,f=t}if(o(e,h))try{h.call(f,p(1),c=p(0))}catch(t){c(t)}else for(a=function(n,a){return o(e,n=c?n:a)?r(function(t,e){i(this,t,e,f,n)}):t},d=0;d<l.length;)h=l[d++],o(e,u=h[c])?i(h.p,h.r,h.j,f,u):(c?h.r:h.j)(f)},a.q=[],t.call(t={then:function(t,e){return a(t,e)},catch:function(t){return a(0,t)}},function(t){a(o,1,t)},function(t){a(o,0,t)}),t}function i(t,r,i,a,s){setImmediate(function(){try{a=s(a),s=a&&o(n,a)|o(e,a)&&a.then,o(e,s)?a==t?i(TypeError()):s.call(a,r,i):r(a)}catch(t){i(t)}})}function a(t){return r(function(e){e(t)})}t.exports=r,r.resolve=a,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){a(t).then(function(t){r[i]=t,--o||e(r)},n)})})}}("f","o")},function(t){function e(t,e){var n,o,r,i,a,s,u,c;for(n=3&t.length,o=t.length-n,r=e,a=3432918353,s=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)*a+(((u>>>16)*a&65535)<<16)&4294967295,u=u<<15|u>>>17,u=(65535&u)*s+(((u>>>16)*s&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)*a+(((u>>>16)*a&65535)<<16)&4294967295,u=u<<15|u>>>17,u=(65535&u)*s+(((u>>>16)*s&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}]);

@@ -51,12 +51,2 @@ var CordovaAppLoader =

function createFilemap(files){
var result = {};
Object.keys(files).forEach(function(key){
var filename = files[key].filename;
if(filename[0] === '/') filename = filename[0].substr(1);
result[filename] = files[key];
});
return result;
}
function AppLoader(options){

@@ -68,6 +58,7 @@ if(!options) throw new Error('CordovaAppLoader has no options!');

Promise = options.fs.Promise;
// initialize variables
// initialize variables
this.manifest = window.Manifest;
this.newManifest = null;
this._lastUpdateManifest = localStorage.getItem('last_update_manifest');

@@ -78,3 +69,3 @@ // normalize serverRoot and set remote manifest url

this.newManifestUrl = options.serverRoot + (options.manifest || 'manifest.json');
// initialize a file cache

@@ -91,2 +82,12 @@ if(options.mode) options.mode = 'mirror';

AppLoader.prototype._createFilemap = function(files){
var result = {};
var normalize = this.cache._fs.normalize;
Object.keys(files).forEach(function(key){
files[key].filename = normalize(files[key].filename);
result[files[key].filename] = files[key];
});
return result;
};
AppLoader.prototype.check = function(newManifest){

@@ -102,2 +103,7 @@ var self = this, manifest = this.manifest;

function checkManifest(newManifest){
if(JSON.stringify(newManifest) === self._lastUpdateManifest) {
resolve(false);
return;
}
// make sure cache is ready for the DIFF operations!

@@ -110,4 +116,4 @@ self.cache.ready.then(function(list){

var newFiles = createFilemap(newManifest.files);
var oldFiles = createFilemap(manifest.files);
var newFiles = self._createFilemap(newManifest.files);
var oldFiles = self._createFilemap(manifest.files);

@@ -125,4 +131,3 @@ // Create the diff

.map(function(file){
if(file[0] === '/') file = file.substr(1);
return file.substr(self.cache._localRoot.length);
return file.substr(self.cache.localRoot.length);
})

@@ -137,3 +142,3 @@ .filter(function(file){

self.newManifest = newManifest;
self.newManifest.root = self.cache.toInternalURL('/') + (self.newManifest.root || '');
self.newManifest.root = self.cache.localInternalURL;
resolve(true);

@@ -192,3 +197,5 @@ } else {

// update manifest
localStorage.setItem('manifest',JSON.stringify(this.newManifest));
json = JSON.stringify(this.newManifest);
localStorage.setItem('manifest',json);
localStorage.setItem('last_update_manifest',json);
if(reload !== false) location.reload();

@@ -223,7 +230,2 @@ return true;

function removeFirstSlash(path){
if(path[0] === '/') path = path.substr(1);
return path;
}
/* Cordova File Cache x */

@@ -247,9 +249,5 @@ function FileCache(options){

// normalize path
this._localRoot = removeFirstSlash(options.localRoot || 'data');
if(this._localRoot[this._localRoot.length -1] !== '/') this._localRoot += '/';
this.localRoot = this._fs.normalize(options.localRoot || 'data');
this.serverRoot = this._fs.normalize(options.serverRoot || '');
this._serverRoot = options.serverRoot || '';
if(!!this._serverRoot && this._serverRoot[this._serverRoot.length-1] !== '/') this._serverRoot += '/';
if(this._serverRoot === './') this._serverRoot = '';
// set internal variables

@@ -261,3 +259,6 @@ this._downloading = []; // download promises

// list existing cache contents
this.ready = this._fs.ensure(this._localRoot).then(function(){
this.ready = this._fs.ensure(this.localRoot)
.then(function(entry){
self.localInternalURL = isCordova? entry.toInternalURL(): entry.toURL();
self.localUrl = entry.toURL();
return self.list();

@@ -274,6 +275,6 @@ });

return new Promise(function(resolve,reject){
self._fs.list(self._localRoot,'rfe').then(function(entries){
self._fs.list(self.localRoot,'rfe').then(function(entries){
self._cached = {};
entries = entries.map(function(entry){
var fullPath = removeFirstSlash(entry.fullPath);
var fullPath = self._fs.normalize(entry.fullPath);
self._cached[fullPath] = {

@@ -283,3 +284,3 @@ toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(),

};
return entry.fullPath;
return fullPath;
});

@@ -346,3 +347,3 @@ resolve(entries);

// to avoid downloading files we already have!
fs.ensure(self._localRoot).then(function(){
fs.ensure(self.localRoot).then(function(){
return self.list();

@@ -432,4 +433,4 @@ }).then(function(){

this._cached = {};
return this._fs.removeDir(this._localRoot).then(function(){
return self._fs.ensure(self._localRoot);
return this._fs.removeDir(this.localRoot).then(function(){
return self._fs.ensure(self.localRoot);
});

@@ -463,4 +464,4 @@ };

FileCache.prototype.toServerURL = function toServerURL(path){
if(path[0] === '/') path = path.substr(1);
return path.indexOf('://') < 0? this._serverRoot + path: path;
path = this._fs.normalize(path);
return path.indexOf('://') < 0? this.serverRoot + path: path;
};

@@ -473,12 +474,11 @@

if(this._mirrorMode) {
url = url || '';
len = this._serverRoot.length;
if(url.substr(0,len) !== this._serverRoot) {
url = removeFirstSlash(url);
return this._localRoot + url;
url = url = this._fs.normalize(url || '');
len = this.serverRoot.length;
if(url.substr(0,len) !== this.serverRoot) {
return this.localRoot + url;
} else {
return this._localRoot + url.substr(len);
return this.localRoot + url.substr(len);
}
} else {
return this._localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
return this.localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
}

@@ -485,0 +485,0 @@ };

@@ -1,1 +0,1 @@

var CordovaAppLoader=function(t){function e(n){if(o[n])return o[n].exports;var r=o[n]={exports:{},id:n,loaded:!1};return t[n].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var o={};return e.m=t,e.c=o,e.p="",e(0)}([function(t,e,o){function n(t){var e={};return Object.keys(t).forEach(function(o){var n=t[o].filename;"/"===n[0]&&(n=n[0].substr(1)),e[n]=t[o]}),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.");a=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"),t.mode&&(t.mode="mirror"),this.cache=new i(t),this._toBeDeleted=[],this._toBeDownloaded=[],this._updateReady=!1,this._checkTimeout=t.checkTimeout||1e4}var i=o(1),a=null;r.prototype.check=function(t){var e=this,o=this.manifest;return new a(function(r,i){function a(t){e.cache.ready.then(function(){if(!t.files)return void i('Downloaded Manifest has no "files" attribute.');var a=n(t.files),s=n(o.files);e._toBeDownloaded=Object.keys(a).filter(function(t){return!s[t]||s[t].version!==a[t].version||!e.cache.isCached(t)}),e.cache.list().then(function(o){e._toBeDeleted=o.map(function(t){return"/"===t[0]&&(t=t.substr(1)),t.substr(e.cache._localRoot.length)}).filter(function(t){return!a[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?a(t):(pegasus(e.newManifestUrl).then(a,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})):a.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,o){function n(t){return"/"===t[0]&&(t=t.substr(1)),t}function r(t){var e=this;if(this._fs=t.fs,!this._fs)throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.');a=this._fs.Promise,this._mirrorMode="hash"!==t.mode,this._retry=t.retry||[500,1500,8e3],this._cacheBuster=!!t.cacheBuster,this._localRoot=n(t.localRoot||"data"),"/"!==this._localRoot[this._localRoot.length-1]&&(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._fs.ensure(this._localRoot).then(function(){return e.list()})}var i=o(2),a=null,s="undefined"!=typeof cordova;r.prototype.list=function(){var t=this;return new a(function(e){t._fs.list(t._localRoot,"rfe").then(function(o){t._cached={},o=o.map(function(e){var o=n(e.fullPath);return t._cached[o]={toInternalURL:s?e.toInternalURL():e.toURL(),toURL:e.toURL()},e.fullPath}),e(o)},function(){e([])})})},r.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()},r.prototype.remove=function(t,e){t||(t=[]);var o=[];"string"==typeof t&&(t=[t]);var n=this;return t.forEach(function(t){var e=n._added.indexOf(n.toServerURL(t));e>=0&&n._added.splice(e,1);var r=n.toPath(t);o.push(n._fs.remove(r)),delete n._cached[r]}),e?a.all(o):n.isDirty()},r.prototype.getDownloadQueue=function(){var t=this,e=t._added.filter(function(e){return!t.isCached(e)});return e},r.prototype.getAdded=function(){return this._added},r.prototype.isDirty=function(){return this.getDownloadQueue().length>0},r.prototype.download=function(t){var e=this._fs,o=this;return o.abort(),new a(function(n,r){e.ensure(o._localRoot).then(function(){return o.list()}).then(function(){if(!o.isDirty())return void n(o);var i=o.getDownloadQueue(),a=[],s=o._downloading.length,h=o._downloading.length,c=o._downloading.length+i.length;i.forEach(function(i){var d,u=o.toPath(i);"function"==typeof t&&(d=function(e){e.queueIndex=s,e.queueSize=c,e.url=i,e.path=u,e.percentage=s/c,e.loaded>0&&e.total>0&&s!==c&&(e.percentage+=e.loaded/e.total/c),a.indexOf(i)<0&&(a.push(i),s++),t(e)});var f=function(){h++,h===c&&(o._downloading=[],o.list().then(function(){d&&d(new ProgressEvent),o.isDirty()?r(o.getDownloadQueue()):n(o)},r))},l=i;o._cacheBuster&&(l+="?"+Date.now());var p=e.download(i,u,{retry:o._retry},d);p.then(f,f),o._downloading.push(p)})},r)})},r.prototype.abort=function(){this._downloading.forEach(function(t){t.abort()}),this._downloading=[]},r.prototype.isCached=function(t){return t=this.toPath(t),!!this._cached[t]},r.prototype.clear=function(){var t=this;return this._cached={},this._fs.removeDir(this._localRoot).then(function(){return t._fs.ensure(t._localRoot)})},r.prototype.toInternalURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:t},r.prototype.get=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:this.toServerURL(t)},r.prototype.toDataURL=function(t){return this._fs.toDataURL(this.toPath(t))},r.prototype.toURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toURL:t},r.prototype.toServerURL=function(t){return"/"===t[0]&&(t=t.substr(1)),t.indexOf("://")<0?this._serverRoot+t:t},r.prototype.toPath=function(t){return this._mirrorMode?(t=t||"",len=this._serverRoot.length,t.substr(0,len)!==this._serverRoot?(t=n(t),this._localRoot+t):this._localRoot+t.substr(len)):this._localRoot+i(t)+t.substr(t.lastIndexOf("."))},t.exports=r},function(t){function e(t,e){var o,n,r,i,a,s,h,c;for(o=3&t.length,n=t.length-o,r=e,a=3432918353,s=461845907,c=0;n>c;)h=255&t.charCodeAt(c)|(255&t.charCodeAt(++c))<<8|(255&t.charCodeAt(++c))<<16|(255&t.charCodeAt(++c))<<24,++c,h=(65535&h)*a+(((h>>>16)*a&65535)<<16)&4294967295,h=h<<15|h>>>17,h=(65535&h)*s+(((h>>>16)*s&65535)<<16)&4294967295,r^=h,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(h=0,o){case 3:h^=(255&t.charCodeAt(c+2))<<16;case 2:h^=(255&t.charCodeAt(c+1))<<8;case 1:h^=255&t.charCodeAt(c),h=(65535&h)*a+(((h>>>16)*a&65535)<<16)&4294967295,h=h<<15|h>>>17,h=(65535&h)*s+(((h>>>16)*s&65535)<<16)&4294967295,r^=h}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}]);
var CordovaAppLoader=function(t){function e(n){if(o[n])return o[n].exports;var r=o[n]={exports:{},id:n,loaded:!1};return t[n].call(r.exports,r,r.exports,e),r.loaded=!0,r.exports}var o={};return e.m=t,e.c=o,e.p="",e(0)}([function(t,e,o){function n(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.");a=t.fs.Promise,this.manifest=window.Manifest,this.newManifest=null,this._lastUpdateManifest=localStorage.getItem("last_update_manifest"),t.serverRoot=t.serverRoot||"",t.serverRoot&&"/"!==t.serverRoot[t.serverRoot.length-1]&&(t.serverRoot+="/"),this.newManifestUrl=t.serverRoot+(t.manifest||"manifest.json"),t.mode&&(t.mode="mirror"),this.cache=new r(t),this._toBeDeleted=[],this._toBeDownloaded=[],this._updateReady=!1,this._checkTimeout=t.checkTimeout||1e4}var r=o(1),a=null;n.prototype._createFilemap=function(t){var e={},o=this.cache._fs.normalize;return Object.keys(t).forEach(function(n){t[n].filename=o(t[n].filename),e[t[n].filename]=t[n]}),e},n.prototype.check=function(t){var e=this,o=this.manifest;return new a(function(n,r){function a(t){return JSON.stringify(t)===e._lastUpdateManifest?void n(!1):void e.cache.ready.then(function(){if(!t.files)return void r('Downloaded Manifest has no "files" attribute.');var a=e._createFilemap(t.files),i=e._createFilemap(o.files);e._toBeDownloaded=Object.keys(a).filter(function(t){return!i[t]||i[t].version!==a[t].version||!e.cache.isCached(t)}),e.cache.list().then(function(o){e._toBeDeleted=o.map(function(t){return t.substr(e.cache.localRoot.length)}).filter(function(t){return!a[t]}).concat(e._toBeDownloaded),e._toBeDeleted.length>0||e._toBeDownloaded.length>0?(e.newManifest=t,e.newManifest.root=e.cache.localInternalURL,n(!0)):n(!1)},r)},r)}"string"==typeof t&&(e.newManifestUrl=t,t=void 0),"object"==typeof t?a(t):(pegasus(e.newManifestUrl).then(a,r),setTimeout(function(){r(new Error("timeout"))},e._checkTimeout))})},n.prototype.canDownload=function(){return!!this.newManifest&&!this._updateReady},n.prototype.canUpdate=function(){return this._updateReady},n.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})):a.resolve(null)},n.prototype.update=function(t){return this._updateReady?(json=JSON.stringify(this.newManifest),localStorage.setItem("manifest",json),localStorage.setItem("last_update_manifest",json),t!==!1&&location.reload(),!0):!1},n.prototype.clear=function(){return localStorage.removeItem("manifest"),this.cache.clear()},n.prototype.reset=function(){return this.clear().then(function(){location.reload()},function(){location.reload()})},t.exports=n},function(t,e,o){function n(t){var e=this;if(this._fs=t.fs,!this._fs)throw new Error('Missing required option "fs". Add an instance of cordova-promise-fs.');a=this._fs.Promise,this._mirrorMode="hash"!==t.mode,this._retry=t.retry||[500,1500,8e3],this._cacheBuster=!!t.cacheBuster,this.localRoot=this._fs.normalize(t.localRoot||"data"),this.serverRoot=this._fs.normalize(t.serverRoot||""),this._downloading=[],this._added=[],this._cached={},this.ready=this._fs.ensure(this.localRoot).then(function(t){return e.localInternalURL=i?t.toInternalURL():t.toURL(),e.localUrl=t.toURL(),e.list()})}var r=o(2),a=null,i="undefined"!=typeof cordova;n.prototype.list=function(){var t=this;return new a(function(e){t._fs.list(t.localRoot,"rfe").then(function(o){t._cached={},o=o.map(function(e){var o=t._fs.normalize(e.fullPath);return t._cached[o]={toInternalURL:i?e.toInternalURL():e.toURL(),toURL:e.toURL()},o}),e(o)},function(){e([])})})},n.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()},n.prototype.remove=function(t,e){t||(t=[]);var o=[];"string"==typeof t&&(t=[t]);var n=this;return t.forEach(function(t){var e=n._added.indexOf(n.toServerURL(t));e>=0&&n._added.splice(e,1);var r=n.toPath(t);o.push(n._fs.remove(r)),delete n._cached[r]}),e?a.all(o):n.isDirty()},n.prototype.getDownloadQueue=function(){var t=this,e=t._added.filter(function(e){return!t.isCached(e)});return e},n.prototype.getAdded=function(){return this._added},n.prototype.isDirty=function(){return this.getDownloadQueue().length>0},n.prototype.download=function(t){var e=this._fs,o=this;return o.abort(),new a(function(n,r){e.ensure(o.localRoot).then(function(){return o.list()}).then(function(){if(!o.isDirty())return void n(o);var a=o.getDownloadQueue(),i=[],s=o._downloading.length,c=o._downloading.length,h=o._downloading.length+a.length;a.forEach(function(a){var d,l=o.toPath(a);"function"==typeof t&&(d=function(e){e.queueIndex=s,e.queueSize=h,e.url=a,e.path=l,e.percentage=s/h,e.loaded>0&&e.total>0&&s!==h&&(e.percentage+=e.loaded/e.total/h),i.indexOf(a)<0&&(i.push(a),s++),t(e)});var f=function(){c++,c===h&&(o._downloading=[],o.list().then(function(){d&&d(new ProgressEvent),o.isDirty()?r(o.getDownloadQueue()):n(o)},r))},u=a;o._cacheBuster&&(u+="?"+Date.now());var p=e.download(a,l,{retry:o._retry},d);p.then(f,f),o._downloading.push(p)})},r)})},n.prototype.abort=function(){this._downloading.forEach(function(t){t.abort()}),this._downloading=[]},n.prototype.isCached=function(t){return t=this.toPath(t),!!this._cached[t]},n.prototype.clear=function(){var t=this;return this._cached={},this._fs.removeDir(this.localRoot).then(function(){return t._fs.ensure(t.localRoot)})},n.prototype.toInternalURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:t},n.prototype.get=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toInternalURL:this.toServerURL(t)},n.prototype.toDataURL=function(t){return this._fs.toDataURL(this.toPath(t))},n.prototype.toURL=function(t){return path=this.toPath(t),this._cached[path]?this._cached[path].toURL:t},n.prototype.toServerURL=function(t){return t=this._fs.normalize(t),t.indexOf("://")<0?this.serverRoot+t:t},n.prototype.toPath=function(t){return this._mirrorMode?(t=t=this._fs.normalize(t||""),len=this.serverRoot.length,t.substr(0,len)!==this.serverRoot?this.localRoot+t:this.localRoot+t.substr(len)):this.localRoot+r(t)+t.substr(t.lastIndexOf("."))},t.exports=n},function(t){function e(t,e){var o,n,r,a,i,s,c,h;for(o=3&t.length,n=t.length-o,r=e,i=3432918353,s=461845907,h=0;n>h;)c=255&t.charCodeAt(h)|(255&t.charCodeAt(++h))<<8|(255&t.charCodeAt(++h))<<16|(255&t.charCodeAt(++h))<<24,++h,c=(65535&c)*i+(((c>>>16)*i&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*s+(((c>>>16)*s&65535)<<16)&4294967295,r^=c,r=r<<13|r>>>19,a=5*(65535&r)+((5*(r>>>16)&65535)<<16)&4294967295,r=(65535&a)+27492+(((a>>>16)+58964&65535)<<16);switch(c=0,o){case 3:c^=(255&t.charCodeAt(h+2))<<16;case 2:c^=(255&t.charCodeAt(h+1))<<8;case 1:c^=255&t.charCodeAt(h),c=(65535&c)*i+(((c>>>16)*i&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*s+(((c>>>16)*s&65535)<<16)&4294967295,r^=c}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}]);

@@ -65,16 +65,18 @@ var CordovaPromiseFS =

function dirname(str) {
var parts = str.split('/');
if(parts.length > 1) {
parts.splice(parts.length-1,1);
return parts.join('/');
} else {
return '';
}
str = str.substr(0,str.lastIndexOf('/')+1);
if(str[0] === '/') str = str.substr(1);
return str;
}
function filename(str) {
var parts = str.split('/');
return parts[parts.length-1];
return str.substr(str.lastIndexOf('/')+1);
}
function normalize(str){
if(str[0] === '/') str = str.substr(1);
if(!!str && str.indexOf('.') < 0 && str[str.length-1] !== '/') str += '/';
if(str === './') str = '';
return str;
}
var transferQueue = [], // queued fileTransfers

@@ -90,3 +92,3 @@ inprogress = 0; // currently active filetransfers

if(!Promise) { throw new Error("No Promise library given in options.Promise"); }
/* default options */

@@ -157,8 +159,2 @@ this.options = options = options || {};

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* ensure directory exists */

@@ -180,2 +176,65 @@ function ensure(folders) {

/* get file file */
function file(path,options){
path = normalize(path);
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){
path = normalize(path);
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);
});
}
/* 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 = [ResolvedPromise(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);
});
}
/* does file exist? If so, resolve with fileEntry, if not, resolve with false. */

@@ -199,2 +258,8 @@ function exists(path){

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* convert path to URL to be used in JS/CSS/HTML */

@@ -212,4 +277,4 @@ function toURL(path) {

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'cdvfile://localhost/'+(options.persistent? 'persistent':'temporary') + path;
path = normalize(path);
return 'cdvfile://localhost/'+(options.persistent? 'persistent/':'temporary/') + path;
};

@@ -225,4 +290,4 @@

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'filesystem:'+location.origin+(options.persistent? '/persistent':'/temporary') + path;
path = normalize(path);
return 'filesystem:'+location.origin+(options.persistent? '/persistent/':'/temporary/') + path;
};

@@ -235,33 +300,4 @@

};
}
/* 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 */

@@ -283,2 +319,8 @@ function read(path,method) {

/* convert path to base64 date URI */
function toDataURL(path) {
return read(path,'readAsDataURL');
}
function readJSON(path){

@@ -357,39 +399,2 @@ return read(path).then(JSON.parse);

/* 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 = [ResolvedPromise(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

@@ -401,3 +406,3 @@ function popTransferQueue(){

inprogress++;
// fetch filetranfer, method-type (isDownload) and arguments

@@ -413,3 +418,3 @@ var args = transferQueue.pop();

var transferOptions = args.shift();
if(ft._aborted) {

@@ -428,3 +433,3 @@ inprogress--;

// Promise callback to check if there are any more queued transfers
// Promise callback to check if there are any more queued transfers
function nextTransfer(result){

@@ -440,3 +445,3 @@ inprogress--; // decrement counter to free up one space to start transfers again!

transferOptions = {};
}
}
serverUrl = encodeURI(serverUrl);

@@ -450,3 +455,3 @@ if(isCordova) localPath = toInternalURLSync(localPath);

transferOptions.retry = transferOptions.retry.concat();
var ft = new FileTransfer();

@@ -496,2 +501,3 @@ onprogress = onprogress || transferOptions.onprogress;

fs: fs,
normalize: normalize,
file: file,

@@ -498,0 +504,0 @@ filename: filename,

@@ -1,1 +0,1 @@

var CordovaPromiseFS=function(n){function t(r){if(e[r])return e[r].exports;var o=e[r]={exports:{},id:r,loaded:!1};return n[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var e={};return t.m=n,t.c=e,t.p="",t(0)}([function(n){function t(n,e,r,o){n.getDirectory(e[0],{create:!0},function(n){e.length>1?t(n,e.slice(1),r,o):r(n)},o)}function e(n){var t=n.split("/");return t.length>1?(t.splice(t.length-1,1),t.join("/")):""}function r(n){var t=n.split("/");return t[t.length-1]}var o=[],i=0;n.exports=function(n){function u(n){return new L(function(t){return t(n)})}function c(n){return f(e(n)).then(function(){return d(n,{create:!0})})}function f(n){return new L(function(e,r){return q.then(function(o){n?(n=n.split("/").filter(function(n){return n&&n.length>0&&"."!==n[0]}),t(o.root,n,e,r)):e(o.root)},r)})}function s(n){return new L(function(t,e){d(n).then(function(n){t(n)},function(n){1===n.code?t(!1):e(n)})})}function a(n){return d(n).then(function(n){return n.toURL()})}function l(n){return h(n,"readAsDataURL")}function d(n,t){return t=t||{},new L(function(e,r){return q.then(function(o){o.root.getFile(n,t,e,r)},r)})}function p(n,t){return t=t||{},new L(function(e,r){return q.then(function(o){n&&"/"!==n?o.root.getDirectory(n,t,e,r):e(o.root)},r)})}function h(n,t){return t=t||"readAsText",d(n).then(function(n){return new L(function(e,r){n.file(function(n){var r=new FileReader;r.onloadend=function(){e(this.result)},r[t](n)},r)})})}function y(n){return h(n).then(JSON.parse)}function w(n,t,r){return f(e(n)).then(function(){return d(n,{create:!0})}).then(function(n){return new L(function(e,o){n.createWriter(function(n){n.onwriteend=e,n.onerror=o,"string"==typeof t?t=new Blob([t],{type:r||"text/plain"}):t instanceof Blob!=!0&&(t=new Blob([JSON.stringify(t,null,4)],{type:r||"application/json"})),n.write(t)},o)})})}function v(n,t){return f(e(t)).then(function(e){return d(n).then(function(n){return new L(function(o,i){n.moveTo(e,r(t),o,i)})})})}function m(n,t){return f(e(t)).then(function(e){return d(n).then(function(n){return new L(function(o,i){n.copyTo(e,r(t),o,i)})})})}function g(n,t){var e=t?d:s;return new L(function(t,r){e(n).then(function(n){n!==!1?n.remove(t,r):t(1)},r)}).then(function(n){return 1===n?!1:!0})}function S(n){return p(n).then(function(n){return new L(function(t,e){n.removeRecursively(t,e)})})}function F(n,t){t=t||"";var e=t.indexOf("r")>-1,r=t.indexOf("e")>-1,o=t.indexOf("f")>-1,i=t.indexOf("d")>-1;return o&&i&&(o=!1,i=!1),new L(function(t,c){return p(n).then(function(n){var f=n.createReader();f.readEntries(function(n){var f=[u(n)];e&&n.filter(function(n){return n.isDirectory}).forEach(function(n){f.push(F(n.fullPath,"re"))}),L.all(f).then(function(n){var e=[];e=e.concat.apply(e,n),o&&(e=e.filter(function(n){return n.isFile})),i&&(e=e.filter(function(n){return n.isDirectory})),r||(e=e.map(function(n){return n.fullPath})),t(e)},c)},c)},c)})}function R(){for(;o.length>0&&i<n.concurrency;){i++;var t=o.pop(),e=t.shift(),r=t.shift(),u=t.shift(),c=t.shift(),f=t.shift(),s=t.shift(),a=t.shift(),l=t.shift();e._aborted?i--:r?(e.download.call(e,u,c,f,s,a,l),e.onprogress&&e.onprogress(new ProgressEvent)):e.upload.call(e,c,u,f,s,l,a)}}function x(n){return i--,R(),n}function T(t,e,r,u,c){"function"==typeof u&&(c=u,u={}),e=encodeURI(e),U&&(r=O(r)),u=u||{},u.retry&&u.retry.length||(u.retry=n.retry),u.retry=u.retry.concat();var f=new FileTransfer;c=c||u.onprogress,"function"==typeof c&&(f.onprogress=c);var s=new L(function(n,c){var s=function(i){if(0===u.retry.length)c(i);else{o.unshift([f,t,e,r,n,s,u.trustAllHosts||!1,u]);var a=u.retry.shift();a>0?setTimeout(x,a):x()}};u.retry.unshift(0),i++,s()});return s.then(x,x),s.progress=function(n){return f.onprogress=n,s},s.abort=function(){return f._aborted=!0,f.abort(),s},s}function b(n,t,e,r){return T(!0,n,t,e,r)}function E(n,t,e,r){return T(!1,t,n,e,r)}var L=n.Promise||window.Promise;if(!L)throw new Error("No Promise library given in options.Promise");this.options=n=n||{},n.persistent=void 0!==n.persistent?n.persistent:!0,n.storageSize=n.storageSize||20971520,n.concurrency=n.concurrency||3,n.retry=n.retry||[];var P,U="undefined"!=typeof cordova;U?P=new L(function(n,t){document.addEventListener("deviceready",n,!1),setTimeout(function(){t(new Error("deviceready has not fired after 5 seconds."))},5100)}):(P=u(!0),"undefined"!=typeof webkitRequestFileSystem?(window.requestFileSystem=webkitRequestFileSystem,window.FileTransfer=function(){},FileTransfer.prototype.download=function(n,t,e,r){var o=new XMLHttpRequest;return o.open("GET",n),o.onreadystatechange=function(){4==o.readyState&&(200===o.status?w(t,o.responseText).then(e,r):r(o.status))},o.send(),o},window.ProgressEvent=function(){}):window.requestFileSystem=function(n,t,e,r){r(new Error("requestFileSystem not supported!"))});var q=new L(function(t,e){P.then(function(){window.requestFileSystem(n.persistent?1:0,n.storageSize,t,e),setTimeout(function(){e(new Error("Could not retrieve FileSystem after 5 seconds."))},5100)},e)});q.then(function(n){window.__fs=n},function(n){console.error("Could not get Cordova FileSystem:",n)});var D,O;return U?(O=function(t){return"/"!==t[0]&&(t="/"+t),"cdvfile://localhost/"+(n.persistent?"persistent":"temporary")+t},D=function(n){return d(n).then(function(n){return n.toInternalURL()})}):(O=function(t){return"/"!==t[0]&&(t="/"+t),"filesystem:"+location.origin+(n.persistent?"/persistent":"/temporary")+t},D=function(n){return d(n).then(function(n){return n.toURL()})}),{fs:q,file:d,filename:r,dir:p,dirname:e,create:c,read:h,readJSON:y,write:w,move:v,copy:m,remove:g,removeDir:S,list:F,ensure:f,exists:s,download:b,upload:E,toURL:a,isCordova:U,toInternalURLSync:O,toInternalURL:D,toDataURL:l,deviceready:P,options:n,Promise:L}}}]);
var CordovaPromiseFS=function(n){function t(r){if(e[r])return e[r].exports;var o=e[r]={exports:{},id:r,loaded:!1};return n[r].call(o.exports,o,o.exports,t),o.loaded=!0,o.exports}var e={};return t.m=n,t.c=e,t.p="",t(0)}([function(n){function t(n,e,r,o){n.getDirectory(e[0],{create:!0},function(n){e.length>1?t(n,e.slice(1),r,o):r(n)},o)}function e(n){return n=n.substr(0,n.lastIndexOf("/")+1),"/"===n[0]&&(n=n.substr(1)),n}function r(n){return n.substr(n.lastIndexOf("/")+1)}function o(n){return"/"===n[0]&&(n=n.substr(1)),n&&n.indexOf(".")<0&&"/"!==n[n.length-1]&&(n+="/"),"./"===n&&(n=""),n}var i=[],u=0;n.exports=function(n){function f(n){return new O(function(t){return t(n)})}function c(n){return new O(function(e,r){return q.then(function(o){n?(n=n.split("/").filter(function(n){return n&&n.length>0&&"."!==n[0]}),t(o.root,n,e,r)):e(o.root)},r)})}function s(n,t){return n=o(n),t=t||{},new O(function(e,r){return q.then(function(o){o.root.getFile(n,t,e,r)},r)})}function a(n,t){return n=o(n),t=t||{},new O(function(e,r){return q.then(function(o){n&&"/"!==n?o.root.getDirectory(n,t,e,r):e(o.root)},r)})}function l(n,t){t=t||"";var e=t.indexOf("r")>-1,r=t.indexOf("e")>-1,o=t.indexOf("f")>-1,i=t.indexOf("d")>-1;return o&&i&&(o=!1,i=!1),new O(function(t,u){return a(n).then(function(n){var c=n.createReader();c.readEntries(function(n){var c=[f(n)];e&&n.filter(function(n){return n.isDirectory}).forEach(function(n){c.push(l(n.fullPath,"re"))}),O.all(c).then(function(n){var e=[];e=e.concat.apply(e,n),o&&(e=e.filter(function(n){return n.isFile})),i&&(e=e.filter(function(n){return n.isDirectory})),r||(e=e.map(function(n){return n.fullPath})),t(e)},u)},u)},u)})}function d(n){return new O(function(t,e){s(n).then(function(n){t(n)},function(n){1===n.code?t(!1):e(n)})})}function p(n){return c(e(n)).then(function(){return s(n,{create:!0})})}function h(n){return s(n).then(function(n){return n.toURL()})}function y(n,t){return t=t||"readAsText",s(n).then(function(n){return new O(function(e,r){n.file(function(n){var r=new FileReader;r.onloadend=function(){e(this.result)},r[t](n)},r)})})}function w(n){return y(n,"readAsDataURL")}function v(n){return y(n).then(JSON.parse)}function m(n,t,r){return c(e(n)).then(function(){return s(n,{create:!0})}).then(function(n){return new O(function(e,o){n.createWriter(function(n){n.onwriteend=e,n.onerror=o,"string"==typeof t?t=new Blob([t],{type:r||"text/plain"}):t instanceof Blob!=!0&&(t=new Blob([JSON.stringify(t,null,4)],{type:r||"application/json"})),n.write(t)},o)})})}function g(n,t){return c(e(t)).then(function(e){return s(n).then(function(n){return new O(function(o,i){n.moveTo(e,r(t),o,i)})})})}function x(n,t){return c(e(t)).then(function(e){return s(n).then(function(n){return new O(function(o,i){n.copyTo(e,r(t),o,i)})})})}function S(n,t){var e=t?s:d;return new O(function(t,r){e(n).then(function(n){n!==!1?n.remove(t,r):t(1)},r)}).then(function(n){return 1===n?!1:!0})}function F(n){return a(n).then(function(n){return new O(function(t,e){n.removeRecursively(t,e)})})}function R(){for(;i.length>0&&u<n.concurrency;){u++;var t=i.pop(),e=t.shift(),r=t.shift(),o=t.shift(),f=t.shift(),c=t.shift(),s=t.shift(),a=t.shift(),l=t.shift();e._aborted?u--:r?(e.download.call(e,o,f,c,s,a,l),e.onprogress&&e.onprogress(new ProgressEvent)):e.upload.call(e,f,o,c,s,l,a)}}function b(n){return u--,R(),n}function T(t,e,r,o,f){"function"==typeof o&&(f=o,o={}),e=encodeURI(e),U&&(r=I(r)),o=o||{},o.retry&&o.retry.length||(o.retry=n.retry),o.retry=o.retry.concat();var c=new FileTransfer;f=f||o.onprogress,"function"==typeof f&&(c.onprogress=f);var s=new O(function(n,f){var s=function(u){if(0===o.retry.length)f(u);else{i.unshift([c,t,e,r,n,s,o.trustAllHosts||!1,o]);var a=o.retry.shift();a>0?setTimeout(b,a):b()}};o.retry.unshift(0),u++,s()});return s.then(b,b),s.progress=function(n){return c.onprogress=n,s},s.abort=function(){return c._aborted=!0,c.abort(),s},s}function E(n,t,e,r){return T(!0,n,t,e,r)}function L(n,t,e,r){return T(!1,t,n,e,r)}var O=n.Promise||window.Promise;if(!O)throw new Error("No Promise library given in options.Promise");this.options=n=n||{},n.persistent=void 0!==n.persistent?n.persistent:!0,n.storageSize=n.storageSize||20971520,n.concurrency=n.concurrency||3,n.retry=n.retry||[];var P,U="undefined"!=typeof cordova;U?P=new O(function(n,t){document.addEventListener("deviceready",n,!1),setTimeout(function(){t(new Error("deviceready has not fired after 5 seconds."))},5100)}):(P=f(!0),"undefined"!=typeof webkitRequestFileSystem?(window.requestFileSystem=webkitRequestFileSystem,window.FileTransfer=function(){},FileTransfer.prototype.download=function(n,t,e,r){var o=new XMLHttpRequest;return o.open("GET",n),o.onreadystatechange=function(){4==o.readyState&&(200===o.status?m(t,o.responseText).then(e,r):r(o.status))},o.send(),o},window.ProgressEvent=function(){}):window.requestFileSystem=function(n,t,e,r){r(new Error("requestFileSystem not supported!"))});var q=new O(function(t,e){P.then(function(){window.requestFileSystem(n.persistent?1:0,n.storageSize,t,e),setTimeout(function(){e(new Error("Could not retrieve FileSystem after 5 seconds."))},5100)},e)});q.then(function(n){window.__fs=n},function(n){console.error("Could not get Cordova FileSystem:",n)});var D,I;return U?(I=function(t){return t=o(t),"cdvfile://localhost/"+(n.persistent?"persistent/":"temporary/")+t},D=function(n){return s(n).then(function(n){return n.toInternalURL()})}):(I=function(t){return t=o(t),"filesystem:"+location.origin+(n.persistent?"/persistent/":"/temporary/")+t},D=function(n){return s(n).then(function(n){return n.toURL()})}),{fs:q,normalize:o,file:s,filename:r,dir:a,dirname:e,create:p,read:y,readJSON:v,write:m,move:g,copy:x,remove:S,removeDir:F,list:l,ensure:c,exists:d,download:E,upload:L,toURL:h,isCordova:U,toInternalURLSync:I,toInternalURL:D,toDataURL:w,deviceready:P,options:n,Promise:O}}}]);
var CordovaFileCache = require('cordova-file-cache');
var Promise = null;
function createFilemap(files){
var result = {};
Object.keys(files).forEach(function(key){
var filename = files[key].filename;
if(filename[0] === '/') filename = filename[0].substr(1);
result[filename] = files[key];
});
return result;
}
function AppLoader(options){

@@ -20,6 +10,7 @@ if(!options) throw new Error('CordovaAppLoader has no options!');

Promise = options.fs.Promise;
// initialize variables
// initialize variables
this.manifest = window.Manifest;
this.newManifest = null;
this._lastUpdateManifest = localStorage.getItem('last_update_manifest');

@@ -30,3 +21,3 @@ // normalize serverRoot and set remote manifest url

this.newManifestUrl = options.serverRoot + (options.manifest || 'manifest.json');
// initialize a file cache

@@ -43,2 +34,12 @@ if(options.mode) options.mode = 'mirror';

AppLoader.prototype._createFilemap = function(files){
var result = {};
var normalize = this.cache._fs.normalize;
Object.keys(files).forEach(function(key){
files[key].filename = normalize(files[key].filename);
result[files[key].filename] = files[key];
});
return result;
};
AppLoader.prototype.check = function(newManifest){

@@ -54,2 +55,7 @@ var self = this, manifest = this.manifest;

function checkManifest(newManifest){
if(JSON.stringify(newManifest) === self._lastUpdateManifest) {
resolve(false);
return;
}
// make sure cache is ready for the DIFF operations!

@@ -62,4 +68,4 @@ self.cache.ready.then(function(list){

var newFiles = createFilemap(newManifest.files);
var oldFiles = createFilemap(manifest.files);
var newFiles = self._createFilemap(newManifest.files);
var oldFiles = self._createFilemap(manifest.files);

@@ -77,4 +83,3 @@ // Create the diff

.map(function(file){
if(file[0] === '/') file = file.substr(1);
return file.substr(self.cache._localRoot.length);
return file.substr(self.cache.localRoot.length);
})

@@ -89,3 +94,3 @@ .filter(function(file){

self.newManifest = newManifest;
self.newManifest.root = self.cache.toInternalURL('/') + (self.newManifest.root || '');
self.newManifest.root = self.cache.localInternalURL;
resolve(true);

@@ -144,3 +149,5 @@ } else {

// update manifest
localStorage.setItem('manifest',JSON.stringify(this.newManifest));
json = JSON.stringify(this.newManifest);
localStorage.setItem('manifest',json);
localStorage.setItem('last_update_manifest',json);
if(reload !== false) location.reload();

@@ -147,0 +154,0 @@ return true;

{
"name": "cordova-app-loader",
"version": "0.7.0",
"version": "0.8.0",
"description": "Cordova App Loader - remote update your cordova app",

@@ -28,4 +28,4 @@ "main": "index.js",

"dependencies": {
"cordova-file-cache": "^0.7.1"
"cordova-file-cache": "^0.8.0"
}
}

@@ -262,6 +262,26 @@ cordova-app-loader

## Design Decisions
## Testing
I want CordovaAppLoader to be fast, responsive, flexible, reliable and safe. In order to do this, I've made the following decisions:
With the demo app, you can test:
* Check, with a new manfiest (resolve true)
* Check, with no new manifest (resolve false)
* Check, with no internet (reject timeout)
* Download (resolve with manifest)
* Download with no internet / while interrupting internet (resolve if withing retry attempts, reject with error otherwise)
* Download without checking (null)
* Update (true if update possible, false otherwise)
* Reset to factory
* Slow Download (progress bar)
* Broken Link (reject download with broken link)
* Broken App (resets back to factory)
There are also [unit tests](http://data.madebymark.nl/cordova-app-loader/test/) (*Chrome only!*).
It includes unit tests for [CordovaPromiseFS](https://github.com/markmarijnissen/cordova-promise-fs) and [CordovaFileCache](https://github.com/markmarijnissen/cordova-file-cache).
## Why Cordova App Loader is Awesomene.
I want CordovaAppLoader to be fast, responsive, flexible, reliable and safe. In order to do this, I've thought about everything that could destroy the app loader and fixed it.
### Loading JS/CSS dynamically using bootstrap.js

@@ -326,2 +346,29 @@

### Avoid never-ending update loop
If for some reason the downloaded files cannot be found in the cache on the next `check()`, CordovaAppLoader will indicate `true`, meaning there are still files to be downloaded.
This is correct and intended behavior, as we expect all files to be in the cache when `check()` returns false.
However, depending on how/when you call `check()`, this could result in a never-ending loop in which the app attempts to download files, but for some reason, the never end up in the cache.
To avoid this pitfall, the following safeguard is implemented:
* Whenever you call `update()`, the manifest is written to localStorage twice:
* `manifest`
* `update_attempt_manifest`
* When calling `check()`, it compares the new manifest with `update_attempt_manifest`. If they are the same, it means you've attempted this before, so `check()` will return false.
### Normalize path everywhere
All filenames and paths are normalized.
* This avoids problems on android (when a path starts with a `/`, Android throws a NullPointerExpception)
* The Manifest.json writer does not have to worry which path convention to use.
* This avoids errors when comparing cache with old manifest with new manifest.
See [CordovaPromiseFS](https://github.com/markmarijnissen/cordova-promise-fs) for more details.
### More to be considered?

@@ -334,3 +381,2 @@

* Create a demo for **autoupdate.js**
* Document and double-check all the urls and paths. (Especially: Do `serverUrl` and `Manifest.root` work together as expected?)
* TODO: safety - if reverting to factory, don't attempt again to use an invalid manifest!

@@ -341,6 +387,13 @@

### 0.7.0 (28/11/2014)
### 0.8.0 (28/11/2014)
* Normalized all paths.
* Updated dependencies.
* Added Safe-guard for never-ending update loop.
### 0.7.0 (27/11/2014)
* Fixed a nasty path issue (remove prepending / when getting files to delete to match convention of file-cache - otherwise check will always return true!)
* Added initial tests.
* Added initial [QUnit tests](http://data.madebymark.nl/cordova-app-loader/test/).
* Updated dependencies.

@@ -347,0 +400,0 @@

var fs = new CordovaPromiseFS({ persistent: typeof cordova !== 'undefined' });
var SERVER = 'http://data.madebymark.nl/cordova-app-loader/';
//var SERVER = 'http://localhost:8080/';
//var SERVER = 'http://data.madebymark.nl/cordova-app-loader/';
var SERVER = 'http://localhost:8181/';

@@ -6,0 +6,0 @@ var loader = window.loader = new CordovaAppLoader({

@@ -60,12 +60,2 @@ /******/ (function(modules) { // webpackBootstrap

function createFilemap(files){
var result = {};
Object.keys(files).forEach(function(key){
var filename = files[key].filename;
if(filename[0] === '/') filename = filename[0].substr(1);
result[filename] = files[key];
});
return result;
}
function AppLoader(options){

@@ -77,6 +67,7 @@ if(!options) throw new Error('CordovaAppLoader has no options!');

Promise = options.fs.Promise;
// initialize variables
// initialize variables
this.manifest = window.Manifest;
this.newManifest = null;
this._lastUpdateManifest = localStorage.getItem('last_update_manifest');

@@ -87,3 +78,3 @@ // normalize serverRoot and set remote manifest url

this.newManifestUrl = options.serverRoot + (options.manifest || 'manifest.json');
// initialize a file cache

@@ -100,2 +91,12 @@ if(options.mode) options.mode = 'mirror';

AppLoader.prototype._createFilemap = function(files){
var result = {};
var normalize = this.cache._fs.normalize;
Object.keys(files).forEach(function(key){
files[key].filename = normalize(files[key].filename);
result[files[key].filename] = files[key];
});
return result;
};
AppLoader.prototype.check = function(newManifest){

@@ -111,2 +112,7 @@ var self = this, manifest = this.manifest;

function checkManifest(newManifest){
if(JSON.stringify(newManifest) === self._lastUpdateManifest) {
resolve(false);
return;
}
// make sure cache is ready for the DIFF operations!

@@ -119,4 +125,4 @@ self.cache.ready.then(function(list){

var newFiles = createFilemap(newManifest.files);
var oldFiles = createFilemap(manifest.files);
var newFiles = self._createFilemap(newManifest.files);
var oldFiles = self._createFilemap(manifest.files);

@@ -134,4 +140,3 @@ // Create the diff

.map(function(file){
if(file[0] === '/') file = file.substr(1);
return file.substr(self.cache._localRoot.length);
return file.substr(self.cache.localRoot.length);
})

@@ -146,3 +151,3 @@ .filter(function(file){

self.newManifest = newManifest;
self.newManifest.root = self.cache.toInternalURL('/') + (self.newManifest.root || '');
self.newManifest.root = self.cache.localInternalURL;
resolve(true);

@@ -201,3 +206,5 @@ } else {

// update manifest
localStorage.setItem('manifest',JSON.stringify(this.newManifest));
json = JSON.stringify(this.newManifest);
localStorage.setItem('manifest',json);
localStorage.setItem('last_update_manifest',json);
if(reload !== false) location.reload();

@@ -232,7 +239,2 @@ return true;

function removeFirstSlash(path){
if(path[0] === '/') path = path.substr(1);
return path;
}
/* Cordova File Cache x */

@@ -256,9 +258,5 @@ function FileCache(options){

// normalize path
this._localRoot = removeFirstSlash(options.localRoot || 'data');
if(this._localRoot[this._localRoot.length -1] !== '/') this._localRoot += '/';
this.localRoot = this._fs.normalize(options.localRoot || 'data');
this.serverRoot = this._fs.normalize(options.serverRoot || '');
this._serverRoot = options.serverRoot || '';
if(!!this._serverRoot && this._serverRoot[this._serverRoot.length-1] !== '/') this._serverRoot += '/';
if(this._serverRoot === './') this._serverRoot = '';
// set internal variables

@@ -270,3 +268,6 @@ this._downloading = []; // download promises

// list existing cache contents
this.ready = this._fs.ensure(this._localRoot).then(function(){
this.ready = this._fs.ensure(this.localRoot)
.then(function(entry){
self.localInternalURL = isCordova? entry.toInternalURL(): entry.toURL();
self.localUrl = entry.toURL();
return self.list();

@@ -283,6 +284,6 @@ });

return new Promise(function(resolve,reject){
self._fs.list(self._localRoot,'rfe').then(function(entries){
self._fs.list(self.localRoot,'rfe').then(function(entries){
self._cached = {};
entries = entries.map(function(entry){
var fullPath = removeFirstSlash(entry.fullPath);
var fullPath = self._fs.normalize(entry.fullPath);
self._cached[fullPath] = {

@@ -292,3 +293,3 @@ toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(),

};
return entry.fullPath;
return fullPath;
});

@@ -355,3 +356,3 @@ resolve(entries);

// to avoid downloading files we already have!
fs.ensure(self._localRoot).then(function(){
fs.ensure(self.localRoot).then(function(){
return self.list();

@@ -441,4 +442,4 @@ }).then(function(){

this._cached = {};
return this._fs.removeDir(this._localRoot).then(function(){
return self._fs.ensure(self._localRoot);
return this._fs.removeDir(this.localRoot).then(function(){
return self._fs.ensure(self.localRoot);
});

@@ -472,4 +473,4 @@ };

FileCache.prototype.toServerURL = function toServerURL(path){
if(path[0] === '/') path = path.substr(1);
return path.indexOf('://') < 0? this._serverRoot + path: path;
path = this._fs.normalize(path);
return path.indexOf('://') < 0? this.serverRoot + path: path;
};

@@ -482,12 +483,11 @@

if(this._mirrorMode) {
url = url || '';
len = this._serverRoot.length;
if(url.substr(0,len) !== this._serverRoot) {
url = removeFirstSlash(url);
return this._localRoot + url;
url = url = this._fs.normalize(url || '');
len = this.serverRoot.length;
if(url.substr(0,len) !== this.serverRoot) {
return this.localRoot + url;
} else {
return this._localRoot + url.substr(len);
return this.localRoot + url.substr(len);
}
} else {
return this._localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
return this.localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
}

@@ -519,16 +519,18 @@ };

function dirname(str) {
var parts = str.split('/');
if(parts.length > 1) {
parts.splice(parts.length-1,1);
return parts.join('/');
} else {
return '';
}
str = str.substr(0,str.lastIndexOf('/')+1);
if(str[0] === '/') str = str.substr(1);
return str;
}
function filename(str) {
var parts = str.split('/');
return parts[parts.length-1];
return str.substr(str.lastIndexOf('/')+1);
}
function normalize(str){
if(str[0] === '/') str = str.substr(1);
if(!!str && str.indexOf('.') < 0 && str[str.length-1] !== '/') str += '/';
if(str === './') str = '';
return str;
}
var transferQueue = [], // queued fileTransfers

@@ -544,3 +546,3 @@ inprogress = 0; // currently active filetransfers

if(!Promise) { throw new Error("No Promise library given in options.Promise"); }
/* default options */

@@ -611,8 +613,2 @@ this.options = options = options || {};

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* ensure directory exists */

@@ -634,2 +630,65 @@ function ensure(folders) {

/* get file file */
function file(path,options){
path = normalize(path);
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){
path = normalize(path);
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);
});
}
/* 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 = [ResolvedPromise(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);
});
}
/* does file exist? If so, resolve with fileEntry, if not, resolve with false. */

@@ -653,2 +712,8 @@ function exists(path){

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* convert path to URL to be used in JS/CSS/HTML */

@@ -666,4 +731,4 @@ function toURL(path) {

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'cdvfile://localhost/'+(options.persistent? 'persistent':'temporary') + path;
path = normalize(path);
return 'cdvfile://localhost/'+(options.persistent? 'persistent/':'temporary/') + path;
};

@@ -679,4 +744,4 @@

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'filesystem:'+location.origin+(options.persistent? '/persistent':'/temporary') + path;
path = normalize(path);
return 'filesystem:'+location.origin+(options.persistent? '/persistent/':'/temporary/') + path;
};

@@ -689,33 +754,4 @@

};
}
/* 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 */

@@ -737,2 +773,8 @@ function read(path,method) {

/* convert path to base64 date URI */
function toDataURL(path) {
return read(path,'readAsDataURL');
}
function readJSON(path){

@@ -811,39 +853,2 @@ return read(path).then(JSON.parse);

/* 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 = [ResolvedPromise(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

@@ -855,3 +860,3 @@ function popTransferQueue(){

inprogress++;
// fetch filetranfer, method-type (isDownload) and arguments

@@ -867,3 +872,3 @@ var args = transferQueue.pop();

var transferOptions = args.shift();
if(ft._aborted) {

@@ -882,3 +887,3 @@ inprogress--;

// Promise callback to check if there are any more queued transfers
// Promise callback to check if there are any more queued transfers
function nextTransfer(result){

@@ -894,3 +899,3 @@ inprogress--; // decrement counter to free up one space to start transfers again!

transferOptions = {};
}
}
serverUrl = encodeURI(serverUrl);

@@ -904,3 +909,3 @@ if(isCordova) localPath = toInternalURLSync(localPath);

transferOptions.retry = transferOptions.retry.concat();
var ft = new FileTransfer();

@@ -950,2 +955,3 @@ onprogress = onprogress || transferOptions.onprogress;

fs: fs,
normalize: normalize,
file: file,

@@ -952,0 +958,0 @@ filename: filename,

@@ -51,12 +51,2 @@ var CordovaAppLoader =

function createFilemap(files){
var result = {};
Object.keys(files).forEach(function(key){
var filename = files[key].filename;
if(filename[0] === '/') filename = filename[0].substr(1);
result[filename] = files[key];
});
return result;
}
function AppLoader(options){

@@ -68,6 +58,7 @@ if(!options) throw new Error('CordovaAppLoader has no options!');

Promise = options.fs.Promise;
// initialize variables
// initialize variables
this.manifest = window.Manifest;
this.newManifest = null;
this._lastUpdateManifest = localStorage.getItem('last_update_manifest');

@@ -78,3 +69,3 @@ // normalize serverRoot and set remote manifest url

this.newManifestUrl = options.serverRoot + (options.manifest || 'manifest.json');
// initialize a file cache

@@ -91,2 +82,12 @@ if(options.mode) options.mode = 'mirror';

AppLoader.prototype._createFilemap = function(files){
var result = {};
var normalize = this.cache._fs.normalize;
Object.keys(files).forEach(function(key){
files[key].filename = normalize(files[key].filename);
result[files[key].filename] = files[key];
});
return result;
};
AppLoader.prototype.check = function(newManifest){

@@ -102,2 +103,7 @@ var self = this, manifest = this.manifest;

function checkManifest(newManifest){
if(JSON.stringify(newManifest) === self._lastUpdateManifest) {
resolve(false);
return;
}
// make sure cache is ready for the DIFF operations!

@@ -110,4 +116,4 @@ self.cache.ready.then(function(list){

var newFiles = createFilemap(newManifest.files);
var oldFiles = createFilemap(manifest.files);
var newFiles = self._createFilemap(newManifest.files);
var oldFiles = self._createFilemap(manifest.files);

@@ -125,4 +131,3 @@ // Create the diff

.map(function(file){
if(file[0] === '/') file = file.substr(1);
return file.substr(self.cache._localRoot.length);
return file.substr(self.cache.localRoot.length);
})

@@ -137,3 +142,3 @@ .filter(function(file){

self.newManifest = newManifest;
self.newManifest.root = self.cache.toInternalURL('/') + (self.newManifest.root || '');
self.newManifest.root = self.cache.localInternalURL;
resolve(true);

@@ -192,3 +197,5 @@ } else {

// update manifest
localStorage.setItem('manifest',JSON.stringify(this.newManifest));
json = JSON.stringify(this.newManifest);
localStorage.setItem('manifest',json);
localStorage.setItem('last_update_manifest',json);
if(reload !== false) location.reload();

@@ -223,7 +230,2 @@ return true;

function removeFirstSlash(path){
if(path[0] === '/') path = path.substr(1);
return path;
}
/* Cordova File Cache x */

@@ -247,9 +249,5 @@ function FileCache(options){

// normalize path
this._localRoot = removeFirstSlash(options.localRoot || 'data');
if(this._localRoot[this._localRoot.length -1] !== '/') this._localRoot += '/';
this.localRoot = this._fs.normalize(options.localRoot || 'data');
this.serverRoot = this._fs.normalize(options.serverRoot || '');
this._serverRoot = options.serverRoot || '';
if(!!this._serverRoot && this._serverRoot[this._serverRoot.length-1] !== '/') this._serverRoot += '/';
if(this._serverRoot === './') this._serverRoot = '';
// set internal variables

@@ -261,3 +259,6 @@ this._downloading = []; // download promises

// list existing cache contents
this.ready = this._fs.ensure(this._localRoot).then(function(){
this.ready = this._fs.ensure(this.localRoot)
.then(function(entry){
self.localInternalURL = isCordova? entry.toInternalURL(): entry.toURL();
self.localUrl = entry.toURL();
return self.list();

@@ -274,6 +275,6 @@ });

return new Promise(function(resolve,reject){
self._fs.list(self._localRoot,'rfe').then(function(entries){
self._fs.list(self.localRoot,'rfe').then(function(entries){
self._cached = {};
entries = entries.map(function(entry){
var fullPath = removeFirstSlash(entry.fullPath);
var fullPath = self._fs.normalize(entry.fullPath);
self._cached[fullPath] = {

@@ -283,3 +284,3 @@ toInternalURL: isCordova? entry.toInternalURL(): entry.toURL(),

};
return entry.fullPath;
return fullPath;
});

@@ -346,3 +347,3 @@ resolve(entries);

// to avoid downloading files we already have!
fs.ensure(self._localRoot).then(function(){
fs.ensure(self.localRoot).then(function(){
return self.list();

@@ -432,4 +433,4 @@ }).then(function(){

this._cached = {};
return this._fs.removeDir(this._localRoot).then(function(){
return self._fs.ensure(self._localRoot);
return this._fs.removeDir(this.localRoot).then(function(){
return self._fs.ensure(self.localRoot);
});

@@ -463,4 +464,4 @@ };

FileCache.prototype.toServerURL = function toServerURL(path){
if(path[0] === '/') path = path.substr(1);
return path.indexOf('://') < 0? this._serverRoot + path: path;
path = this._fs.normalize(path);
return path.indexOf('://') < 0? this.serverRoot + path: path;
};

@@ -473,12 +474,11 @@

if(this._mirrorMode) {
url = url || '';
len = this._serverRoot.length;
if(url.substr(0,len) !== this._serverRoot) {
url = removeFirstSlash(url);
return this._localRoot + url;
url = url = this._fs.normalize(url || '');
len = this.serverRoot.length;
if(url.substr(0,len) !== this.serverRoot) {
return this.localRoot + url;
} else {
return this._localRoot + url.substr(len);
return this.localRoot + url.substr(len);
}
} else {
return this._localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
return this.localRoot + hash(url) + url.substr(url.lastIndexOf('.'));
}

@@ -485,0 +485,0 @@ };

@@ -65,16 +65,18 @@ var CordovaPromiseFS =

function dirname(str) {
var parts = str.split('/');
if(parts.length > 1) {
parts.splice(parts.length-1,1);
return parts.join('/');
} else {
return '';
}
str = str.substr(0,str.lastIndexOf('/')+1);
if(str[0] === '/') str = str.substr(1);
return str;
}
function filename(str) {
var parts = str.split('/');
return parts[parts.length-1];
return str.substr(str.lastIndexOf('/')+1);
}
function normalize(str){
if(str[0] === '/') str = str.substr(1);
if(!!str && str.indexOf('.') < 0 && str[str.length-1] !== '/') str += '/';
if(str === './') str = '';
return str;
}
var transferQueue = [], // queued fileTransfers

@@ -90,3 +92,3 @@ inprogress = 0; // currently active filetransfers

if(!Promise) { throw new Error("No Promise library given in options.Promise"); }
/* default options */

@@ -157,8 +159,2 @@ this.options = options = options || {};

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* ensure directory exists */

@@ -180,2 +176,65 @@ function ensure(folders) {

/* get file file */
function file(path,options){
path = normalize(path);
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){
path = normalize(path);
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);
});
}
/* 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 = [ResolvedPromise(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);
});
}
/* does file exist? If so, resolve with fileEntry, if not, resolve with false. */

@@ -199,2 +258,8 @@ function exists(path){

function create(path){
return ensure(dirname(path)).then(function(){
return file(path,{create:true});
});
}
/* convert path to URL to be used in JS/CSS/HTML */

@@ -212,4 +277,4 @@ function toURL(path) {

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'cdvfile://localhost/'+(options.persistent? 'persistent':'temporary') + path;
path = normalize(path);
return 'cdvfile://localhost/'+(options.persistent? 'persistent/':'temporary/') + path;
};

@@ -225,4 +290,4 @@

toInternalURLSync = function(path){
if(path[0] !== '/') path = '/' + path;
return 'filesystem:'+location.origin+(options.persistent? '/persistent':'/temporary') + path;
path = normalize(path);
return 'filesystem:'+location.origin+(options.persistent? '/persistent/':'/temporary/') + path;
};

@@ -235,33 +300,4 @@

};
}
/* 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 */

@@ -283,2 +319,8 @@ function read(path,method) {

/* convert path to base64 date URI */
function toDataURL(path) {
return read(path,'readAsDataURL');
}
function readJSON(path){

@@ -357,39 +399,2 @@ return read(path).then(JSON.parse);

/* 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 = [ResolvedPromise(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

@@ -401,3 +406,3 @@ function popTransferQueue(){

inprogress++;
// fetch filetranfer, method-type (isDownload) and arguments

@@ -413,3 +418,3 @@ var args = transferQueue.pop();

var transferOptions = args.shift();
if(ft._aborted) {

@@ -428,3 +433,3 @@ inprogress--;

// Promise callback to check if there are any more queued transfers
// Promise callback to check if there are any more queued transfers
function nextTransfer(result){

@@ -440,3 +445,3 @@ inprogress--; // decrement counter to free up one space to start transfers again!

transferOptions = {};
}
}
serverUrl = encodeURI(serverUrl);

@@ -450,3 +455,3 @@ if(isCordova) localPath = toInternalURLSync(localPath);

transferOptions.retry = transferOptions.retry.concat();
var ft = new FileTransfer();

@@ -496,2 +501,3 @@ onprogress = onprogress || transferOptions.onprogress;

fs: fs,
normalize: normalize,
file: file,

@@ -498,0 +504,0 @@ filename: filename,

@@ -28,11 +28,11 @@ (function(){

QUnit.test('normalize "localRoot" to "cache-test/"',function(assert){
assert.equal(cache._localRoot,'cache-test/');
assert.equal(cache.localRoot,'cache-test/');
});
QUnit.test('normalize "serverRoot" to end with "/"',function(assert){
assert.equal(cache._serverRoot[cache._serverRoot.length-1],'/');
assert.equal(cache.serverRoot[cache.serverRoot.length-1],'/');
});
QUnit.asyncTest('"localRoot" exists',function(assert){
fs.list(cache._localRoot,'rfd').then(ok(assert,truthy),err(assert));
fs.list(cache.localRoot,'rfd').then(ok(assert,truthy),err(assert));
});

@@ -50,3 +50,3 @@

.then(function(){
return fs.list(cache._localRoot,'rfd');
return fs.list(cache.localRoot,'rfd');
})

@@ -133,3 +133,3 @@ .then(function(list){

assert.equal(cache.isCached(file3),true,'file3 is cached');
return fs.list(cache._localRoot,'rf');
return fs.list(cache.localRoot,'rf');
})

@@ -234,3 +234,3 @@ .then(function(list){

.then(function(){
return fs.list(cache._localRoot,'rfd');
return fs.list(cache.localRoot,'rfd');
})

@@ -237,0 +237,0 @@ .then(function(list){

@@ -25,6 +25,20 @@ (function(){

QUnit.test('fs.dirname("bla/bla/test.txt") = "bla/bla"',function(assert){
assert.equal(fs.dirname('bla/bla/test.txt'),'bla/bla');
QUnit.test('fs.dirname("bla/bla/test.txt") = "bla/bla/"',function(assert){
assert.equal(fs.dirname('bla/bla/test.txt'),'bla/bla/');
});
QUnit.test('fs.normalize: "/dir/dir/file.txt => dir/file.txt; /dir => dir/; ./ => ""',function(assert){
assert.equal(fs.normalize('./'), '', '"./" => ""');
assert.equal(fs.normalize('test'), 'test/', 'test => test/');
assert.equal(fs.normalize('test/'), 'test/', 'test/ => test/');
assert.equal(fs.normalize('/test'), 'test/', '/test => test/');
assert.equal(fs.normalize('/test/'), 'test/', '/test/ => test/');
assert.equal(fs.normalize('/dir.txt/'),'dir.txt/', '/dir.txt/ => dir.txt/ (directory with a dot)');
assert.equal(fs.normalize('file.txt'), 'file.txt', 'file.txt => file.txt');
assert.equal(fs.normalize('/file.txt'), 'file.txt', '/file.txt => file.txt');
assert.equal(fs.normalize('/dir/sub/sub/text.txt'), 'dir/sub/sub/text.txt', 'subdirectories with file');
assert.equal(fs.normalize('/dir/sub/sub/sub'), 'dir/sub/sub/sub/', 'subdirectories');
});
/*************************************/

@@ -286,3 +300,6 @@ // CREATE

fs.download('http://data.madebymark.nl/cordova-promise-fs/does-not-exist.txt','download-error.txt',{retry:[0,0]})
.then(err(assert),ok(assert,false));
.then(err(assert),function(){
assert.ok(true);
QUnit.start();
});
});

@@ -289,0 +306,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc