Initial commit
This commit is contained in:
217
lib/assets/javascripts/dashboard/data/backbone/sync-options.js
Normal file
217
lib/assets/javascripts/dashboard/data/backbone/sync-options.js
Normal file
@@ -0,0 +1,217 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
|
||||
(function () {
|
||||
// helper functions needed from backbone (they are not exported)
|
||||
var getValue = function (object, prop, method) {
|
||||
if (!(object && object[prop])) return null;
|
||||
return _.isFunction(object[prop]) ? object[prop](method) : object[prop];
|
||||
};
|
||||
|
||||
// Throw an error when a URL is needed, and none is supplied.
|
||||
var urlError = function () {
|
||||
throw new Error('A "url" property or function must be specified');
|
||||
};
|
||||
|
||||
// backbone.sync replacement to control url prefix
|
||||
Backbone.originalSync = Backbone.sync;
|
||||
Backbone.sync = function (method, model, options) {
|
||||
var url = options.url || getValue(model, 'url', method) || urlError();
|
||||
// prefix if http is not present
|
||||
var absoluteUrl = url.indexOf('http') === 0 || url.indexOf('//') === 0;
|
||||
if (!absoluteUrl) {
|
||||
// We need to fix this
|
||||
// this comes from cdb.config.prefixUrl
|
||||
options.url = (model._configModel || model._config || model).get('base_url') + url;
|
||||
} else {
|
||||
options.url = url;
|
||||
}
|
||||
if (method !== 'read') {
|
||||
// remove everything related
|
||||
if (model.surrogateKeys) {
|
||||
Backbone.cachedSync.invalidateSurrogateKeys(getValue(model, 'surrogateKeys'));
|
||||
}
|
||||
}
|
||||
return Backbone.originalSync(method, model, options);
|
||||
};
|
||||
|
||||
Backbone.currentSync = Backbone.sync;
|
||||
Backbone.withCORS = function (method, model, options) {
|
||||
if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (!options.crossDomain) {
|
||||
options.crossDomain = true;
|
||||
}
|
||||
|
||||
if (!options.xhrFields) {
|
||||
options.xhrFields = { withCredentials: true };
|
||||
}
|
||||
|
||||
return Backbone.currentSync(method, model, options);
|
||||
};
|
||||
|
||||
// this method returns a cached version of backbone sync
|
||||
// take a look at https://github.com/teambox/backbone.memoized_sync/blob/master/backbone.memoized_sync.js
|
||||
// this is the same concept but implemented as a wrapper for ``Backbone.sync``
|
||||
// usage:
|
||||
// initialize: function () {
|
||||
// this.sync = Backbone.cachedSync(this.user_name);
|
||||
// }
|
||||
Backbone.cachedSync = function (namespace, sync) {
|
||||
if (!namespace) {
|
||||
throw new Error('cachedSync needs a namespace as argument');
|
||||
}
|
||||
|
||||
var surrogateKey = namespace;
|
||||
var session = window.user_data && window.user_data.username;
|
||||
// no user session, no cache
|
||||
// there should be a session to have cache so we avoid
|
||||
// cache collision for someone with more than one account
|
||||
if (session) {
|
||||
namespace += '-' + session;
|
||||
} else {
|
||||
return Backbone.sync;
|
||||
}
|
||||
|
||||
var namespaceKey = 'cdb-cache/' + namespace;
|
||||
|
||||
// saves all the localstore references to the namespace
|
||||
// inside localstore. It allows to remove all the references
|
||||
// at a time
|
||||
var index = {
|
||||
// return a list of references for the namespace
|
||||
_keys: function () {
|
||||
return JSON.parse(localStorage.getItem(namespaceKey) || '{}');
|
||||
},
|
||||
|
||||
// add a new reference for the namespace
|
||||
add: function (key) {
|
||||
var keys = this._keys();
|
||||
keys[key] = +new Date();
|
||||
localStorage.setItem(namespaceKey, JSON.stringify(keys));
|
||||
},
|
||||
|
||||
// remove all the references for the namespace
|
||||
invalidate: function () {
|
||||
var keys = this._keys();
|
||||
_.each(keys, function (v, k) {
|
||||
localStorage.removeItem(k);
|
||||
});
|
||||
localStorage.removeItem(namespaceKey);
|
||||
}
|
||||
};
|
||||
|
||||
// localstore-like cache wrapper
|
||||
var cache = {
|
||||
setItem: function (key, value) {
|
||||
localStorage.setItem(key, value);
|
||||
index.add(key);
|
||||
return this;
|
||||
},
|
||||
|
||||
// this is async in case the data needs to be compressed
|
||||
getItem: function (key, callback) {
|
||||
var val = localStorage.getItem(key);
|
||||
_.defer(function () {
|
||||
callback(val);
|
||||
});
|
||||
},
|
||||
|
||||
removeItem: function (key) {
|
||||
localStorage.removeItem(key);
|
||||
index.invalidate();
|
||||
}
|
||||
};
|
||||
|
||||
var cached = function (method, model, options) {
|
||||
var url = options.url || getValue(model, 'url') || urlError();
|
||||
var key = namespaceKey + '/' + url;
|
||||
|
||||
if (method === 'read') {
|
||||
var success = options.success;
|
||||
var cachedValue = null;
|
||||
|
||||
options.success = function (resp, status, xhr) {
|
||||
// if cached value is ok
|
||||
if (cachedValue && xhr.responseText === cachedValue) {
|
||||
return;
|
||||
}
|
||||
cache.setItem(key, xhr.responseText);
|
||||
success(resp, status, xhr);
|
||||
};
|
||||
|
||||
cache.getItem(key, function (val) {
|
||||
cachedValue = val;
|
||||
if (val) {
|
||||
success(JSON.parse(val), 'success');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cache.removeItem(key);
|
||||
}
|
||||
return (sync || Backbone.sync)(method, model, options);
|
||||
};
|
||||
|
||||
// create a public function to invalidate all the namespace
|
||||
// items
|
||||
cached.invalidate = function () {
|
||||
index.invalidate();
|
||||
};
|
||||
|
||||
// for testing and debugging porpuposes
|
||||
cached.cache = cache;
|
||||
|
||||
// have a global namespace -> sync function in order to avoid invalidation
|
||||
Backbone.cachedSync.surrogateKeys[surrogateKey] = cached;
|
||||
|
||||
return cached;
|
||||
};
|
||||
|
||||
Backbone.cachedSync.surrogateKeys = {};
|
||||
|
||||
Backbone.cachedSync.invalidateSurrogateKeys = function (keys) {
|
||||
_.each(keys, function (k) {
|
||||
var s = Backbone.cachedSync.surrogateKeys[k];
|
||||
if (s) {
|
||||
s.invalidate();
|
||||
} else {
|
||||
console.error('Backbone sync options: surrogate key not found: ' + k);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Backbone.syncAbort = function () {
|
||||
var self = arguments[1];
|
||||
if (self._xhr) {
|
||||
self._xhr.abort();
|
||||
}
|
||||
self._xhr = Backbone.sync.apply(this, arguments);
|
||||
self._xhr.always(function () { self._xhr = null; });
|
||||
return self._xhr;
|
||||
};
|
||||
|
||||
Backbone.delayedSaveSync = function (sync, delay) {
|
||||
var dsync = _.debounce(sync, delay);
|
||||
return function (method, model, options) {
|
||||
if (method === 'create' || method === 'update') {
|
||||
return dsync(method, model, options);
|
||||
} else {
|
||||
return sync(method, model, options);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Backbone.saveAbort = function () {
|
||||
var self = this;
|
||||
if (this._saving && this._xhr) {
|
||||
this._xhr.abort();
|
||||
}
|
||||
this._saving = true;
|
||||
var xhr = Backbone.Model.prototype.save.apply(this, arguments);
|
||||
this._xhr = xhr;
|
||||
xhr.always(function () { self._saving = false; });
|
||||
return xhr;
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user