diff --git a/ionic-cache-src.js b/ionic-cache-src.js
index 86c4538..554e711 100644
--- a/ionic-cache-src.js
+++ b/ionic-cache-src.js
@@ -1,4 +1,5 @@
(function() {
+
function extend(dst, src) {
for (var k in src)
dst[k] = dst[k] || src[k];
@@ -8,47 +9,50 @@
return typeof x == 'function' ? x : y;
};
-
- function getBase64Image(img) {
- var canvas = document.createElement("canvas");
- canvas.width = img.width;
- canvas.height = img.height;
- var ctx = canvas.getContext("2d");
- ctx.drawImage(img, 0, 0);
- return canvas.toDataURL();
- }
+ // For the Default Refresh Icon
+ //****************************************************************************************************//
+ var default_refreshIcon_style = {
+ refreshMedia: refreshMedia,
+ showRefreshIconInBrowser: true,
+ showRefreshIconInDevice: true,
+ refreshIconContainerStyle: 'text-align:center;position:relative;top:50%;transform: translateY(-50%);font-size:60px;color:#B3B4B6;'
+ };
// For the Default Progress Circle
//****************************************************************************************************//
var default_circle_style = {
- color: '#1D5ECE',
- bgcolor: '#eaeaea',
+ color: '#FFFFFF',
+ bgcolor: '#B3B4B6',
semi: false,
rounded: false,
clockwise: true,
- radius: '15',
- stroke: '5',
+ radius: '40',
+ stroke: '15',
+ responsive: true,
max: 100,
iterations: 50,
animation: 'easeOutCubic',
interval: 200,
showProgressCircleInBrowser: true,
showProgressCircleInDevice: true,
- circleContainerStyle: 'text-align:center'
+ circleContainerStyle: 'text-align:center;position:relative;top:50%;transform: translateY(-50%);'
};
-
- function getElement(element) {
- switch (element[0].nodeName) {
- case 'SOURCE':
- return element.parent();
-
- default:
- return element;
+ function getElement(element){
+ switch(element[0].nodeName){
+ case 'SOURCE':
+ return element.parent();
+
+ default:
+ return element;
}
}
-
+
function makeProgressCircle($scope, $compile) {
- return angular.element($compile('
')($scope));
+ return angular.element($compile('')($scope));
+ };
+
+ function makeRefreshIcon($scope, $compile, $rootScope) {
+ return angular.element($compile('
')($scope));
};
var uiOnProgress = function(scope, element, $compile, uiData) {
@@ -57,12 +61,34 @@
var uiOnStart = function(scope, element, $compile, uiData) {
// debugger;
var elm = getElement(element);
+
+ //if (uiData.loadFailed && scope.srcIs != 'background' && (element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO')) {
+ if (uiData.loadFailed && scope.srcIs != 'background' && (element[0].nodeName != 'AUDIO')) {
+ function rmRefreshIcon() {
+ elm.css('display', uiData.display);
+ uiData.refreshIcon.remove();
+ }
+ if (window.cordova) {
+ if (scope.showRefreshIconInDevice) {
+ rmRefreshIcon();
+ }
+ } else {
+ if (scope.showRefreshIconInBrowser) {
+ rmRefreshIcon();
+ }
+ }
+ }
+
+
+
if (scope.srcIs == 'background') {
- elm.css('background', scope.backgroundLoadingStyle);
- } else if (element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO') {
+ elm.css('background',scope.backgroundLoadingStyle);
+ }
+ //else if(element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO')
+ else if(element[0].nodeName != 'AUDIO')
+ {
extend(scope, default_circle_style);
- var progress_circle;
-
+ var progress_circle;
function addCircle() {
progress_circle = makeProgressCircle(scope, $compile);
uiData.display = elm.css('display');
@@ -83,9 +109,38 @@
}
};
var uiOnFinish = function(scope, element, $compile, uiData) {
- if (scope.srcIs != 'background' && (element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO')) {
+ //if (scope.srcIs != 'background' && (element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO')) {
+ if (scope.srcIs != 'background' && (element[0].nodeName != 'AUDIO')) {
+ var elm = getElement(element);
+ function rmCircle() {
+ elm.css('display', uiData.display);
+ uiData.progress_circle.remove();
+ }
+ if (window.cordova) {
+ if (scope.showProgressCircleInDevice) {
+ rmCircle();
+ }
+ } else {
+ if (scope.showProgressCircleInBrowser) {
+ rmCircle();
+ }
+ }
+ }
+ };
+ var onError = function(error, scope, element, $compile, uiData, $rootScope) {
+
+ //if (scope.srcIs != 'background' && (element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO')) {
+ if (scope.srcIs != 'background' && (element[0].nodeName != 'AUDIO')) {
+
+ console.log('error is');
+ console.log(error);
+ // display refresh icon here and set load error variable to true
+
+ // set download error true
+ downloadError = true;
+
+ // remove spinner
var elm = getElement(element);
-
function rmCircle() {
elm.css('display', uiData.display);
uiData.progress_circle.remove();
@@ -100,6 +155,35 @@
}
}
}
+
+ // add refresh icon
+ if (scope.srcIs == 'background') {
+ elm.css('background',scope.backgroundRefreshStyle);
+ }
+ //else if(element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO')
+ else if(element[0].nodeName != 'AUDIO')
+ {
+ extend(scope, default_refreshIcon_style);
+ var refreshIcon;
+ function addRefreshIcon() {
+ refreshIcon = makeRefreshIcon(scope, $compile, $rootScope);
+ uiData.display = elm.css('display');
+ elm.css('display', 'none');
+ elm.after(refreshIcon);
+ };
+
+ if (window.cordova) {
+ if (scope.showRefreshIconInDevice) {
+ addRefreshIcon();
+ }
+ } else {
+ if (scope.showRefreshIconInBrowser) {
+ addRefreshIcon();
+ }
+ }
+ uiData.refreshIcon = refreshIcon;
+ }
+
};
//****************************************************************************************************//
function id() {
@@ -119,13 +203,12 @@
}
return false;
};
-
function needDownload(path) {
if (startsWith(path, [
- 'http://',
- 'https://',
- 'ftp://'
- ])) {
+ 'http://',
+ 'https://',
+ 'ftp://'
+ ])) {
return true;
} else {
return false;
@@ -137,13 +220,14 @@
interval: 200,
backgroundStyle: '',
backgroundLoadingStyle: "url('lib/ionic-cache-src/img/loader.gif') no-repeat center",
+ backgroundRefreshStyle: "url('lib/ionic-cache-src/img/refresh.gif') no-repeat center", ///////////// need to add this in
uiOnStart: uiOnStart,
uiOnFinish: uiOnFinish,
uiOnProgress: uiOnProgress,
- encodeUri: true,
+ onError: onError,
expire: Infinity
};
- var getCacheDir = function() {};
+ var getCacheDir = function(){};
angular
.module('ionic-cache-src', [
'ionic',
@@ -162,6 +246,7 @@
}
return this;
};
+
this.$get = function() {
return this.config;
};
@@ -170,29 +255,29 @@
var c = {};
c._cache = $localStorage.cache_src || {};
c.get = function(url) {
- if (needDownload(url)) {
+ if(needDownload(url)){
var cache_url = c._cache[url] && (getCacheDir() + c._cache[url]);
- return cache_url || url;
+ return cache_url || url;
}
- return undefined;
+ return undefined;
};
c.set = function(url, localUrl) {
c._cache[url] = localUrl;
return c;
};
- c.reset = function(url) {
- if (url != undefined)
+ c.reset = function(url){
+ if(url != undefined)
delete $localStorage.cache_src[url];
else
- $localStorage.cache_src = {};
+ $localStorage.cache_src = {};
};
-
+
return c;
})
- .directive('cacheSrc', function($ionicPlatform, $window, $interval, $timeout, $compile, $cacheSrc, $cordovaFileTransfer, $localStorage,$cordovaFile) {
+ .directive('cacheSrc', function($ionicPlatform, $window, $interval, $timeout, $compile, $cacheSrc, $cordovaFileTransfer, $localStorage, $rootScope, $cordovaGoogleAnalytics) {
return {
restrict: 'A',
- priority: 99,
+ priority: 99,
scope: {
'onProgress': '=?',
'onFinish': '=?',
@@ -205,22 +290,18 @@
},
link: function(scope, element, attrs) {
-
+ var uiData = {}
+
// debugger;
- extend(scope, $cacheSrc);
+ extend(scope, $cacheSrc);
for (var k in attrs) {
if (!angular.isFunction(scope[k])) {
scope[k] = attrs[k];
}
}
-
+
scope.expire = parseInt(scope.expire) || $cacheSrc.expire;
- if (scope.encodeUri === "false")
- scope.encodeUri = false;
- else
- scope.encodeUri = $cacheSrc.encodeUri;
- // console.log("encodeURI:"+scope.encodeUri);
scope.onProgress = ensureFunction(scope.onProgress, angular.noop);
scope.onFinish = ensureFunction(scope.onFinish, angular.noop);
scope.onError = ensureFunction(scope.onError, angular.noop);
@@ -228,162 +309,235 @@
scope.uiOnProgress = ensureFunction(scope.uiOnProgress, angular.noop); //use default ones
scope.uiOnFinish = ensureFunction(scope.uiOnFinish, angular.noop);
scope.uiOnStart = ensureFunction(scope.uiOnStart, angular.noop);
-
-
-
+ var loadFailed = false;
+
+ scope.refreshClicked = function () {
+
+ $rootScope.$broadcast("refreshFailedMediaLoads");
+
+ };
+
+
function addSrcWithoutFinish(result) {
if (scope.srcIs == 'background') {
- getElement(element).css('background', "url('" + result + "') " + scope.backgroundStyle);
- } else {
- getElement(element).attr(scope.srcIs || 'src', result);
+ getElement(element).css('background',"url('" + result + "') " + scope.backgroundStyle);
+ } else {
+ getElement(element).attr(scope.srcIs || 'src',result);
}
}
-
function addSrc(result) {
- addSrcWithoutFinish(result);
- scope.onFinish(result);
+
+ // need to do a tag check here to determine if cache is video or image
+
+ if(element[0].nodeName != 'VIDEO' && element[0].nodeName != 'AUDIO') {
+
+
+ var img = new Image();
+ img.onload = function () {
+
+ addSrcWithoutFinish(result);
+ scope.onFinish(result);
+
+
+ }
+ img.onerror = function () {
+
+ loadFailed = true;
+
+ var err = 'Device stored image load error, setting element to refresh';
+
+ if ($localStorage.cache_src[result] != undefined) {
+
+ // remove cache entry as device url result no good then show refresh
+
+ delete $localStorage.cache_src[result];
+
+ scope.onError(err, scope, element, $compile, uiData, $rootScope);
+
+ }
+ else {
+
+ // no cache entry for this, re-attempt download
+
+ scope.onError(err, scope, element, $compile, uiData, $rootScope);
+
+
+ }
+
+
+ }
+ img.src = result;
+
+ }
+ else {
+
+ addSrcWithoutFinish(result);
+ scope.onFinish(result);
+
+ }
};
if ($window.cordova) {
- var getCacheDir = function() {
- if (window.device)
- if (window.cordova.file) {
- switch (device.platform) {
- case 'iOS':
- return $window.cordova.file.documentsDirectory;
- case 'Android':
- return $window.cordova.file.dataDirectory;
- case 'windows':
- return $window.cordova.file.dataDirectory;
- }
- } else
- throw new Error("window.cordova.file is not defined! Maybe you should install cordova-plugin-file first!");
- else
- throw new Error("window.device is not defined! Maybe you should install cordova-plugin-device first!");
+ var getCacheDir = function () {
+ switch (device.platform) {
+ case 'iOS':
+ return $window.cordova.file.documentsDirectory;
+ case 'Android':
+ return $window.cordova.file.dataDirectory;
+ case 'windows':
+ return $window.cordova.file.dataDirectory;
+ }
return '';
- };
-
+ };
var cache = $localStorage.cache_src = $localStorage.cache_src || {};
var create_time = $localStorage.cache_src_create_time = $localStorage.cache_src_create_time || {};
- function scopeOnError(fileName,uiData) {
- return function(E) {
- if (E.code == 3) {
- console.log('Error Occurs: ' + E.exception + "\nCode:" + E.code + "\nSrc:" + E.source);
- if(uiData)
- scope.uiOnFinish(scope, element, $compile, uiData);
- addSrcWithoutFinish(E.source);
- // var b64 = getBase64Image(getElement(element));
- // if(b64.length > 200)
- // $cordovaFile
- // .writeFile(getCacheDir(), fileName, b64, true)
- // .then(function(){
- // cache[attrs.cacheSrc] = fileName;
- // if (scope.expire !== Infinity) {
- // create_time[attrs.cacheSrc] = Date.now();
- // }
- // addSrc(getCacheDir() + fileName);
- // },scope.onError);
- } else {
- scope.onError(E);
- }
- };
- }
-
- function fetchRemoteWithoutLoading() {
- var ext = '.' + attrs.cacheSrc.split('.').pop();
+ function fetchRemoteWithoutLoading(){
+ uiData.loadFailed = loadFailed;
+
+ // example code mod to allow for split+pop on complex URLs
+
+// if(attrs.cacheSrc.includes('fbcdn') || attrs.cacheSrc.includes('facebook')) {
+// var ext = '.' + attrs.cacheSrc.split('ext=').pop();
+// var encoded = false;
+// }
+// else {
+// var ext = '.' + attrs.cacheSrc.split('.').pop();
+// var encoded = true;
+// }
+
+ // simple split+pop
+
+ var ext = '.' + attrs.cacheSrc.split('.').pop();
+ var encoded = true;
+
var fileName = id() + ext;
+
$cordovaFileTransfer
- .download(attrs.cacheSrc, getCacheDir() + fileName, {
- encodeURI: scope.encodeUri,
- chunkedMode: false,
- headers: {
- Connection: "close"
- }
- }, true)
+ .download(attrs.cacheSrc, getCacheDir() + fileName, {encodeURI: encoded}, true)
.then(function() {
+
+ loadFailed = false;
cache[attrs.cacheSrc] = fileName;
if (scope.expire !== Infinity) {
create_time[attrs.cacheSrc] = Date.now();
}
addSrc(getCacheDir() + fileName);
- }, scopeOnError(fileName), angular.noop);
- }
-
+ }, function(err) {
+ // Error
+ loadFailed = true;
+ scope.onError(err, scope, element, $compile, uiData, $rootScope);
+
+ }, angular.noop);
+ }
function fetchRemote() {
- var uiData = {};
+ uiData.loadFailed = loadFailed;
scope.onStart(attrs.cacheSrc);
- // var elem = getElement(element);
scope.uiOnStart(scope, element, $compile, uiData);
+
+ // example code mod to allow for split+pop on complex URLs
+
+// if (attrs.cacheSrc.includes('fbcdn') || attrs.cacheSrc.includes('facebook')) {
+// var ext = '.' + attrs.cacheSrc.split('ext=').pop();
+// var encoded = false;
+// }
+// else {
+// var ext = '.' + attrs.cacheSrc.split('.').pop();
+// var encoded = true;
+// }
+
+ // simple split+pop
+
+ var ext = '.' + attrs.cacheSrc.split('.').pop();
+ var encoded = true;
-
- var ext = '.' + attrs.cacheSrc.split('.').pop();
var fileName = id() + ext;
+
$cordovaFileTransfer
- .download(attrs.cacheSrc, getCacheDir() + fileName, {
- encodeURI: scope.encodeUri,
- chunkedMode: false,
- headers: {
-
- Connection: "close"
- }
- }, true)
+ .download(attrs.cacheSrc, getCacheDir() + fileName, {encodeURI: encoded}, true)
.then(function() {
+
+ loadFailed = false;
cache[attrs.cacheSrc] = fileName;
if (scope.expire !== Infinity) {
create_time[attrs.cacheSrc] = Date.now();
}
scope.uiOnFinish(scope, element, $compile, uiData);
addSrc(getCacheDir() + fileName);
- }, scopeOnError(fileName,uiData), function(progress) {
+ }, function(err) {
+ // Error
+ loadFailed = true;
+ scope.onError(err, scope, element, $compile, uiData, $rootScope)
+
+ }, function(progress) {
uiData.progress = (progress.loaded / progress.total) * 100;
scope.uiOnProgress(scope, element, $compile, uiData);
scope.onProgress(uiData.progress);
});
}
-
function fetchCache() {
addSrc(getCacheDir() + cache[attrs.cacheSrc]);
}
-
-
$ionicPlatform
.ready(function() {
- attrs.$observe('cacheSrc',
- function() {
- // debugger;
- if (attrs.cacheSrc) {
- if (needDownload(attrs.cacheSrc)) {
- if (cache[attrs.cacheSrc]) {
- var now = Date.now();
- var create = create_time[attrs.cacheSrc] || Infinity;
- if (now - create < scope.expire * 1000) {
- fetchCache();
- } else {
- // alert('Cache expired');
- addSrcWithoutFinish(getCacheDir() + cache[attrs.cacheSrc]);
- fetchRemoteWithoutLoading();
- }
- } else {
- fetchRemote();
- }
- } else {
- addSrc(attrs.cacheSrc);
- }
- }
- });
+ attrs.$observe('cacheSrc',
+ function() {
+ // debugger;
+ if (attrs.cacheSrc) {
+ if (needDownload(attrs.cacheSrc)) {
+ if (cache[attrs.cacheSrc]) {
+ var now = Date.now();
+ var create = create_time[attrs.cacheSrc] || Infinity;
+ if (now - create < scope.expire * 1000) {
+ fetchCache();
+ } else {
+ addSrcWithoutFinish(getCacheDir() + cache[attrs.cacheSrc]);
+ fetchRemoteWithoutLoading();
+ }
+ } else {
+ fetchRemote();
+ }
+ } else {
+ addSrc(attrs.cacheSrc);
+ }
+ }
+ });
+
+ $rootScope.$on("refreshFailedMediaLoads", function(){
+ if(loadFailed) {
+ // debugger;
+ if (attrs.cacheSrc) {
+ if (needDownload(attrs.cacheSrc)) {
+ if (cache[attrs.cacheSrc]) {
+ var now = Date.now();
+ var create = create_time[attrs.cacheSrc] || Infinity;
+ if (now - create < scope.expire * 1000) {
+ fetchCache();
+ } else {
+ addSrcWithoutFinish(getCacheDir() + cache[attrs.cacheSrc]);
+ fetchRemoteWithoutLoading();
+ }
+ } else {
+ fetchRemote();
+ }
+ } else {
+ addSrc(attrs.cacheSrc);
+ }
+ }
+ }
+ });
});
} else {
- // in browser
- attrs.$observe('cacheSrc', function() {
+ // in browser
+ attrs.$observe('cacheSrc',
+ function() {
if (attrs.cacheSrc) {
if (needDownload(attrs.cacheSrc)) {
- // var elem = getElement(element);
var uiData = {};
scope.onStart(attrs.cacheSrc);
scope.uiOnStart(scope, element, $compile, uiData);
- element[0].onerror = scope.onError;
+
uiData.progress = scope.progress || 0;
// debugger;
var promise = $interval(function() {
@@ -402,6 +556,36 @@
}
}
});
+ $rootScope.$on("refreshFailedMediaLoads", function(){
+ if(loadFailed) {
+
+ if (attrs.cacheSrc) {
+ if (needDownload(attrs.cacheSrc)) {
+ var uiData = {};
+ scope.onStart(attrs.cacheSrc);
+ scope.uiOnStart(scope, element, $compile, uiData);
+
+ uiData.progress = scope.progress || 0;
+ // debugger;
+ var promise = $interval(function() {
+ uiData.progress += 10;
+ scope.uiOnProgress(scope, element, $compile, uiData);
+ scope.onProgress(uiData.progress);
+
+ if (uiData.progress == 100) {
+ $interval.cancel(promise);
+ scope.uiOnFinish(scope, element, $compile, uiData);
+ addSrc(attrs.cacheSrc);
+ }
+ }, scope.interval);
+ } else {
+ addSrc(attrs.cacheSrc);
+ }
+ }
+
+
+ }
+ });
}