UNPKG

angular-cached-resource

Version:

An AngularJS module to interact with RESTful resources, even when browser is offline

617 lines (553 loc) 21.1 kB
(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){ // Generated by CoffeeScript 1.7.1 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){ // Generated by CoffeeScript 1.7.1 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){ // Generated by CoffeeScript 1.7.1 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' && params.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){ // Generated by CoffeeScript 1.7.1 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){ // Generated by CoffeeScript 1.7.1 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){ // Generated by CoffeeScript 1.7.1 var CACHE_RETRY_TIMEOUT, Cache, ResourceCacheEntry, ResourceWriteQueue; CACHE_RETRY_TIMEOUT = 60000; 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])