cartodb-4.42/lib/assets/javascripts/cartodb/app.js

239 lines
6.4 KiB
JavaScript
Raw Normal View History

2024-04-06 13:25:13 +08:00
//i18n
//
function _t(s) {
return s;
}
var i18n = {
// format('hello, {0}', 'rambo') -> "hello, rambo"
format: function (str) {
for(var i = 1; i < arguments.length; ++i) {
var attrs = arguments[i];
for(var attr in attrs) {
str = str.replace(RegExp('\\{' + attr + '\\}', 'g'), attrs[attr]);
}
}
return str;
}
};
(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) {
options.url = cdb.config.prefixUrl() + 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 {
cdb.log.debug("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;
};