angular-cached-resource
Version:
An AngularJS module to interact with RESTful resources, even when browser is offline
1 lines • 15.9 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){var LOCAL_STORAGE_PREFIX;LOCAL_STORAGE_PREFIX="cachedResource://";module.exports=window.localStorage!=null?{getItem:function(key,fallback){var item;item=localStorage.getItem(""+LOCAL_STORAGE_PREFIX+key);if(item!=null){return angular.fromJson(item)}else{return fallback}},setItem:function(key,value){localStorage.setItem(""+LOCAL_STORAGE_PREFIX+key,angular.toJson(value));return value}}:{getItem:function(key,fallback){return fallback},setItem:function(key,value){return value}}},{}],2:[function(require,module,exports){var CachedResourceManager,ResourceWriteQueue;ResourceWriteQueue=require("./resource_write_queue");CachedResourceManager=function(){function CachedResourceManager($timeout){this.$timeout=$timeout;this.queuesByKey={}}CachedResourceManager.prototype.add=function(CachedResource){return this.queuesByKey[CachedResource.$key]=new ResourceWriteQueue(CachedResource,this.$timeout)};CachedResourceManager.prototype.getQueue=function(CachedResource){return this.queuesByKey[CachedResource.$key]};CachedResourceManager.prototype.flushQueues=function(){var key,queue,_ref,_results;_ref=this.queuesByKey;_results=[];for(key in _ref){queue=_ref[key];_results.push(queue.flush())}return _results};return CachedResourceManager}();module.exports=CachedResourceManager},{"./resource_write_queue":6}],3:[function(require,module,exports){var CachedResourceManager,DEFAULT_ACTIONS,ResourceCacheArrayEntry,ResourceCacheEntry,app,resourceManagerListener,__slice=[].slice;DEFAULT_ACTIONS={get:{method:"GET"},query:{method:"GET",isArray:true},save:{method:"POST"},remove:{method:"DELETE"},"delete":{method:"DELETE"}};ResourceCacheEntry=require("./resource_cache_entry");ResourceCacheArrayEntry=require("./resource_cache_array_entry");CachedResourceManager=require("./cached_resource_manager");resourceManagerListener=null;app=angular.module("ngCachedResource",["ngResource"]);app.factory("$cachedResource",["$resource","$timeout","$q","$log",function($resource,$timeout,$q,$log){var modifyObjectInPlace,processReadArgs,readArrayCache,readCache,resourceManager,writeCache;resourceManager=new CachedResourceManager($timeout);if(resourceManagerListener){document.removeEventListener("online",resourceManagerListener)}resourceManagerListener=function(event){return resourceManager.flushQueues()};document.addEventListener("online",resourceManagerListener);processReadArgs=function(args){var deferred,error,params,success;args=Array.prototype.slice.call(args);params=angular.isObject(args[0])?args.shift():{};success=args[0],error=args[1];deferred=$q.defer();if(angular.isFunction(success)){deferred.promise.then(success)}if(angular.isFunction(error)){deferred.promise["catch"](error)}return{params:params,deferred:deferred}};modifyObjectInPlace=function(oldObject,newObject){var key,_i,_j,_len,_len1,_ref,_ref1,_results;_ref=Object.keys(oldObject);for(_i=0,_len=_ref.length;_i<_len;_i++){key=_ref[_i];if(key[0]!=="$"){if(newObject[key]==null){delete oldObject[key]}}}_ref1=Object.keys(newObject);_results=[];for(_j=0,_len1=_ref1.length;_j<_len1;_j++){key=_ref1[_j];if(key[0]!=="$"){if(angular.isObject(oldObject[key])&&angular.isObject(newObject[key])){_results.push(modifyObjectInPlace(oldObject[key],newObject[key]))}else if(!angular.equals(oldObject[key],newObject[key])){_results.push(oldObject[key]=newObject[key])}else{_results.push(void 0)}}}return _results};readArrayCache=function(name,CachedResource){return function(){var arrayInstance,cacheArrayEntry,cacheDeferred,cacheInstanceEntry,cacheInstanceParams,httpDeferred,params,resource,_i,_len,_ref,_ref1;_ref=processReadArgs(arguments),params=_ref.params,cacheDeferred=_ref.deferred;httpDeferred=$q.defer();arrayInstance=new Array;arrayInstance.$promise=cacheDeferred.promise;arrayInstance.$httpPromise=httpDeferred.promise;cacheArrayEntry=new ResourceCacheArrayEntry(CachedResource.$key,params);resource=CachedResource.$resource[name].apply(CachedResource.$resource,arguments);resource.$promise.then(function(){var cachedResourceInstances;cachedResourceInstances=resource.map(function(resourceInstance){return new CachedResource(resourceInstance)});arrayInstance.splice.apply(arrayInstance,[0,arrayInstance.length].concat(__slice.call(cachedResourceInstances)));if(!cacheArrayEntry.value){cacheDeferred.resolve(arrayInstance)}return httpDeferred.resolve(arrayInstance)});resource.$promise["catch"](function(error){if(!cacheArrayEntry.value){cacheDeferred.reject(error)}return httpDeferred.reject(error)});arrayInstance.$httpPromise.then(function(response){var cacheArrayReferences,cacheInstanceEntry,cacheInstanceParams,instance,_i,_len;cacheArrayReferences=[];for(_i=0,_len=response.length;_i<_len;_i++){instance=response[_i];cacheInstanceParams=instance.$params();if(Object.keys(cacheInstanceParams).length===0){$log.error("instance "+instance+" doesn't have any boundParams. Please, make sure you specified them in your resource's initialization, f.e. `{id: \"@id\"}`, or it won't be cached.")}else{cacheArrayReferences.push(cacheInstanceParams);cacheInstanceEntry=new ResourceCacheEntry(CachedResource.$key,cacheInstanceParams);cacheInstanceEntry.set(instance,false)}}return cacheArrayEntry.set(cacheArrayReferences)});if(cacheArrayEntry.value){_ref1=cacheArrayEntry.value;for(_i=0,_len=_ref1.length;_i<_len;_i++){cacheInstanceParams=_ref1[_i];cacheInstanceEntry=new ResourceCacheEntry(CachedResource.$key,cacheInstanceParams);arrayInstance.push(new CachedResource(cacheInstanceEntry.value))}cacheDeferred.resolve(arrayInstance)}return arrayInstance}};readCache=function(name,CachedResource){return function(){var cacheDeferred,cacheEntry,httpDeferred,instance,params,readHttp,_ref;_ref=processReadArgs(arguments),params=_ref.params,cacheDeferred=_ref.deferred;httpDeferred=$q.defer();instance=new CachedResource({$promise:cacheDeferred.promise,$httpPromise:httpDeferred.promise});cacheEntry=new ResourceCacheEntry(CachedResource.$key,params);readHttp=function(){var resource;resource=CachedResource.$resource[name].call(CachedResource.$resource,params);resource.$promise.then(function(response){angular.extend(instance,response);if(!cacheEntry.value){cacheDeferred.resolve(instance)}httpDeferred.resolve(instance);return cacheEntry.set(response,false)});return resource.$promise["catch"](function(error){if(!cacheEntry.value){cacheDeferred.reject(error)}return httpDeferred.reject(error)})};if(cacheEntry.dirty){resourceManager.getQueue(CachedResource).processResource(params,readHttp)}else{readHttp()}if(cacheEntry.value){angular.extend(instance,cacheEntry.value);cacheDeferred.resolve(instance)}return instance}};writeCache=function(action,CachedResource){return function(){var args,cacheEntry,deferred,error,instanceMethod,param,params,queue,queueDeferred,resource,success,value,_ref;instanceMethod=this instanceof CachedResource;args=Array.prototype.slice.call(arguments);params=!instanceMethod&&angular.isObject(args[1])?args.shift():instanceMethod&&angular.isObject(args[0])?args.shift():{};resource=instanceMethod?this:new CachedResource(args.shift());success=args[0],error=args[1];resource.$resolved=false;_ref=resource.$params();for(param in _ref){value=_ref[param];params[param]=value}deferred=$q.defer();resource.$promise=deferred.promise;if(angular.isFunction(success)){deferred.promise.then(success)}if(angular.isFunction(error)){deferred.promise["catch"](error)}cacheEntry=new ResourceCacheEntry(CachedResource.$key,params);if(!angular.equals(cacheEntry.data,resource)){cacheEntry.set(resource,true)}queueDeferred=$q.defer();queueDeferred.promise.then(function(httpResource){modifyObjectInPlace(resource,httpResource);resource.$resolved=true;return deferred.resolve(resource)});queueDeferred.promise["catch"](deferred.reject);queue=resourceManager.getQueue(CachedResource);queue.enqueue(params,action,queueDeferred);queue.flush();return resource}};return function(){var $key,CachedResource,Resource,actions,arg,args,boundParams,handler,isPermissibleBoundValue,name,param,paramDefault,paramDefaults,params,url,_ref;args=Array.prototype.slice.call(arguments);$key=args.shift();url=args.shift();while(args.length){arg=args.pop();if(angular.isObject(arg[Object.keys(arg)[0]])){actions=arg}else{paramDefaults=arg}}actions=angular.extend({},DEFAULT_ACTIONS,actions);if(paramDefaults==null){paramDefaults={}}boundParams={};for(param in paramDefaults){paramDefault=paramDefaults[param];if(paramDefault[0]==="@"){boundParams[paramDefault.substr(1)]=param}}Resource=$resource.call(null,url,paramDefaults,actions);isPermissibleBoundValue=function(value){return angular.isDate(value)||angular.isNumber(value)||angular.isString(value)};CachedResource=function(){CachedResource.prototype.$cache=true;function CachedResource(attrs){angular.extend(this,attrs)}CachedResource.prototype.$params=function(){var attribute,params;params={};for(attribute in boundParams){param=boundParams[attribute];if(isPermissibleBoundValue(this[attribute])){params[param]=this[attribute]}}return params};CachedResource.$resource=Resource;CachedResource.$key=$key;return CachedResource}();for(name in actions){params=actions[name];handler=params.method==="GET"&¶ms.isArray?readArrayCache(name,CachedResource):params.method==="GET"?readCache(name,CachedResource):(_ref=params.method)==="POST"||_ref==="PUT"||_ref==="DELETE"?writeCache(name,CachedResource):void 0;CachedResource[name]=handler;if(params.method!=="GET"){CachedResource.prototype["$"+name]=handler}}resourceManager.add(CachedResource);resourceManager.flushQueues();return CachedResource}}]);if(typeof module!=="undefined"&&module!==null){module.exports=app}},{"./cached_resource_manager":2,"./resource_cache_array_entry":4,"./resource_cache_entry":5}],4:[function(require,module,exports){var ResourceCacheArrayEntry,ResourceCacheEntry,__hasProp={}.hasOwnProperty,__extends=function(child,parent){for(var key in parent){if(__hasProp.call(parent,key))child[key]=parent[key]}function ctor(){this.constructor=child}ctor.prototype=parent.prototype;child.prototype=new ctor;child.__super__=parent.prototype;return child};ResourceCacheEntry=require("./resource_cache_entry");ResourceCacheArrayEntry=function(_super){__extends(ResourceCacheArrayEntry,_super);function ResourceCacheArrayEntry(){return ResourceCacheArrayEntry.__super__.constructor.apply(this,arguments)}ResourceCacheArrayEntry.prototype.defaultValue=[];ResourceCacheArrayEntry.prototype.setKey=function(key){return this.key=""+key+"/array"};return ResourceCacheArrayEntry}(ResourceCacheEntry);module.exports=ResourceCacheArrayEntry},{"./resource_cache_entry":5}],5:[function(require,module,exports){var Cache,ResourceCacheEntry;Cache=require("./cache");ResourceCacheEntry=function(){ResourceCacheEntry.prototype.defaultValue={};function ResourceCacheEntry(resourceKey,params){var param,paramKeys,_ref;this.setKey(resourceKey);paramKeys=angular.isObject(params)?Object.keys(params).sort():[];if(paramKeys.length){this.key+="?"+function(){var _i,_len,_results;_results=[];for(_i=0,_len=paramKeys.length;_i<_len;_i++){param=paramKeys[_i];_results.push(""+param+"="+params[param])}return _results}().join("&")}_ref=Cache.getItem(this.key,this.defaultValue),this.value=_ref.value,this.dirty=_ref.dirty}ResourceCacheEntry.prototype.setKey=function(key){this.key=key};ResourceCacheEntry.prototype.set=function(value,dirty){this.value=value;this.dirty=dirty;return this._update()};ResourceCacheEntry.prototype.setClean=function(){this.dirty=false;return this._update()};ResourceCacheEntry.prototype._update=function(){return Cache.setItem(this.key,{value:this.value,dirty:this.dirty})};return ResourceCacheEntry}();module.exports=ResourceCacheEntry},{"./cache":1}],6:[function(require,module,exports){var CACHE_RETRY_TIMEOUT,Cache,ResourceCacheEntry,ResourceWriteQueue;CACHE_RETRY_TIMEOUT=6e4;ResourceCacheEntry=require("./resource_cache_entry");Cache=require("./cache");ResourceWriteQueue=function(){function ResourceWriteQueue(CachedResource,$timeout){this.CachedResource=CachedResource;this.$timeout=$timeout;this.key=""+this.CachedResource.$key+"/write";this.queue=Cache.getItem(this.key,[])}ResourceWriteQueue.prototype.enqueue=function(params,action,deferred){var entry,_ref,_ref1;entry=this.findEntry({params:params,action:action});if(entry==null){this.queue.push({params:params,action:action,deferred:deferred});return this._update()}else{if((_ref=entry.deferred)!=null){_ref.promise.then(function(response){return deferred.resolve(response)})}return(_ref1=entry.deferred)!=null?_ref1.promise["catch"](function(error){return deferred.reject(error)}):void 0}};ResourceWriteQueue.prototype.findEntry=function(_arg){var action,entry,params,_i,_len,_ref;action=_arg.action,params=_arg.params;_ref=this.queue;for(_i=0,_len=_ref.length;_i<_len;_i++){entry=_ref[_i];if(action===entry.action&&angular.equals(params,entry.params)){return entry}}};ResourceWriteQueue.prototype.removeEntry=function(_arg){var action,entry,newQueue,params,_i,_len,_ref;action=_arg.action,params=_arg.params;newQueue=[];_ref=this.queue;for(_i=0,_len=_ref.length;_i<_len;_i++){entry=_ref[_i];if(!(action===entry.action&&angular.equals(params,entry.params))){newQueue.push(entry)}}this.queue=newQueue;if(this.queue.length===0&&this.timeoutPromise){this.$timeout.cancel(this.timeoutPromise);delete this.timeoutPromise}return this._update()};ResourceWriteQueue.prototype.flush=function(){var entry,_i,_len,_ref,_results;this._setFlushTimeout();_ref=this.queue;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){entry=_ref[_i];_results.push(this._processEntry(entry))}return _results};ResourceWriteQueue.prototype.processResource=function(params,done){var entry,notDone,_i,_len,_ref,_results;notDone=true;_ref=this._entriesForResource(params);_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){entry=_ref[_i];_results.push(this._processEntry(entry,function(_this){return function(){if(notDone&&_this._entriesForResource(params).length===0){notDone=false;return done()}}}(this)))}return _results};ResourceWriteQueue.prototype._entriesForResource=function(params){var entry,_i,_len,_ref,_results;_ref=this.queue;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){entry=_ref[_i];if(angular.equals(params,entry.params)){_results.push(entry)}}return _results};ResourceWriteQueue.prototype._processEntry=function(entry,done){var cacheEntry,onFailure,onSuccess;cacheEntry=new ResourceCacheEntry(this.CachedResource.$key,entry.params);onSuccess=function(_this){return function(value){var _ref;_this.removeEntry(entry);cacheEntry.setClean();if((_ref=entry.deferred)!=null){_ref.resolve(value)}if(angular.isFunction(done)){return done()}}}(this);onFailure=function(_this){return function(error){var _ref;return(_ref=entry.deferred)!=null?_ref.reject(error):void 0}}(this);return this.CachedResource.$resource[entry.action](entry.params,cacheEntry.value,onSuccess,onFailure)};ResourceWriteQueue.prototype._setFlushTimeout=function(){if(this.queue.length>0&&!this.timeoutPromise){this.timeoutPromise=this.$timeout(angular.bind(this,this.flush),CACHE_RETRY_TIMEOUT);return this.timeoutPromise.then(function(_this){return function(){delete _this.timeoutPromise;return _this._setFlushTimeout()}}(this))}};ResourceWriteQueue.prototype._update=function(){var savableQueue;savableQueue=this.queue.map(function(entry){return{params:entry.params,action:entry.action}});return Cache.setItem(this.key,savableQueue)};return ResourceWriteQueue}();module.exports=ResourceWriteQueue},{"./cache":1,"./resource_cache_entry":5}]},{},[3]);