This commit is contained in:
zhongjin
2020-06-15 12:07:54 +08:00
parent 610ed21a90
commit a96ef233c9
444 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
/**
* global configuration
*/
(function() {
Config = Backbone.Model.extend({
VERSION: 2,
initialize: function() {
this.modules = new Backbone.Collection();
this.modules.bind('add', function(model) {
this.trigger('moduleLoaded');
this.trigger('moduleLoaded:' + model.get('name'));
}, this);
},
//error track
REPORT_ERROR_URL: '/api/v0/error',
ERROR_TRACK_ENABLED: false,
/**
* returns the base url to compose the final url
* http://user.carto.com/
*/
getSqlApiBaseUrl: function() {
var url;
if (this.get('sql_api_template')) {
url = this.get("sql_api_template").replace('{user}', this.get('user_name'));
} else {
url = this.get('sql_api_protocol') + '://' +
this.get('user_name') + '.' +
this.get('sql_api_domain') + ':' +
this.get('sql_api_port');
}
return url;
},
/**
* returns the full sql api url, including the api endpoint
* allos to specify the version
* http://user.carto.com/api/v1/sql
*/
getSqlApiUrl: function(version) {
version = version || 'v2';
return this.getSqlApiBaseUrl() + "/api/" + version + "/sql";
},
/**
* returns the maps api host, removing user template
* and the protocol.
* carto.com:3333
*/
getMapsApiHost: function() {
var url;
var mapsApiTemplate = this.get('maps_api_template');
if (mapsApiTemplate) {
url = mapsApiTemplate.replace(/https?:\/\/{user}\./, '');
}
return url;
}
});
cdb.config = new Config();
cdb.config.set({
cartodb_attributions: "© <a href=\"https://carto.com/attributions\" target=\"_blank\">CARTO</a>",
cartodb_logo_link: "http://www.carto.com"
});
})();

View File

@@ -0,0 +1,95 @@
/**
* Decorators to extend functionality of cdb related objects
*/
/**
* Adds .elder method to call for the same method of the parent class
* usage:
* insanceOfClass.elder('name_of_the_method');
*/
cdb.decorators.elder = (function() {
// we need to backup one of the backbone extend models
// (it doesn't matter which, they are all the same method)
var backboneExtend = Backbone.Router.extend;
var superMethod = function(method, options) {
var result = null;
if (this.parent != null) {
var currentParent = this.parent;
// we need to change the parent of "this", because
// since we are going to call the elder (super) method
// in the context of "this", if the super method has
// another call to elder (super), we need to provide a way of
// redirecting to the grandparent
this.parent = this.parent.parent;
var options = Array.prototype.slice.call(arguments, 1);
if (currentParent.hasOwnProperty(method)) {
result = currentParent[method].apply(this, options);
} else {
options.splice(0,0, method);
result = currentParent.elder.apply(this, options);
}
this.parent = currentParent;
}
return result;
}
var extend = function(protoProps, classProps) {
var child = backboneExtend.call(this, protoProps, classProps);
child.prototype.parent = this.prototype;
child.prototype.elder = function(method) {
var options = Array.prototype.slice.call(arguments, 1);
if (method) {
options.splice(0,0, method)
return superMethod.apply(this, options);
} else {
return child.prototype.parent;
}
}
return child;
};
var decorate = function(objectToDecorate) {
objectToDecorate.extend = extend;
objectToDecorate.prototype.elder = function() {};
objectToDecorate.prototype.parent = null;
}
return decorate;
})()
cdb.decorators.elder(Backbone.Model);
cdb.decorators.elder(Backbone.View);
cdb.decorators.elder(Backbone.Collection);
if(!window.JSON) {
// shims for ie7
window.JSON = {
stringify: function(param) {
if(typeof param == 'number' || typeof param == 'boolean') {
return param.toString();
} else if (typeof param =='string') {
return '"' + param.toString() + '"';
} else if(_.isArray(param)) {
var res = '[';
for(var n in param) {
if(n>0) res+=', ';
res += JSON.stringify(param[n]);
}
res += ']'
return res;
} else {
var res = '{';
for(var p in param) {
if(param.hasOwnProperty(p)) {
res += '"'+p+'": '+ JSON.stringify(param[p]);
}
}
res += '}'
return res;
}
// no, we're no gonna stringify regexp, fuckoff.
},
parse: function(param) {
return eval(param);
}
}
}

View File

@@ -0,0 +1,70 @@
var Loader = cdb.vis.Loader = cdb.core.Loader = {
queue: [],
current: undefined,
_script: null,
head: null,
loadScript: function(src) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = src;
script.async = true;
if (!Loader.head) {
Loader.head = document.getElementsByTagName('head')[0];
}
// defer the loading because IE9 loads in the same frame the script
// so Loader._script is null
setTimeout(function() {
Loader.head.appendChild(script);
}, 0);
return script;
},
get: function(url, callback) {
if (!Loader._script) {
Loader.current = callback;
Loader._script = Loader.loadScript(url + (~url.indexOf('?') ? '&' : '?') + 'callback=vizjson');
} else {
Loader.queue.push([url, callback]);
}
},
getPath: function(file) {
var scripts = document.getElementsByTagName('script'),
cartodbJsRe = /\/?cartodb[\-\._]?([\w\-\._]*)\.js\??/;
for (i = 0, len = scripts.length; i < len; i++) {
src = scripts[i].src;
matches = src.match(cartodbJsRe);
if (matches) {
var bits = src.split('/');
delete bits[bits.length - 1];
return bits.join('/') + file;
}
}
return null;
},
loadModule: function(modName) {
var file = "cartodb.mod." + modName + (cartodb.DEBUG ? ".uncompressed.js" : ".js");
var src = this.getPath(file);
if (!src) {
cartodb.log.error("can't find cartodb.js file");
}
Loader.loadScript(src);
}
};
window.vizjson = function(data) {
Loader.current && Loader.current(data);
// remove script
Loader.head.removeChild(Loader._script);
Loader._script = null;
// next element
var a = Loader.queue.shift();
if (a) {
Loader.get(a[0], a[1]);
}
};

View File

@@ -0,0 +1,84 @@
/**
* logging
*/
(function() {
// error management
cdb.core.Error = Backbone.Model.extend({
url: cdb.config.REPORT_ERROR_URL,
initialize: function() {
this.set({browser: JSON.stringify($.browser) });
}
});
cdb.core.ErrorList = Backbone.Collection.extend({
model: cdb.core.Error,
enableTrack: function() {
var old_onerror = window.onerror;
window.onerror = function(msg, url, line) {
cdb.errors.create({
msg: msg,
url: url,
line: line
});
if (old_onerror)
old_onerror.apply(window, arguments);
};
}
});
/** contains all error for the application */
cdb.errors = new cdb.core.ErrorList();
// error tracking!
if(cdb.config.ERROR_TRACK_ENABLED) {
cdb.errors.enableTrack();
}
// logging
var _fake_console = function() {};
_fake_console.prototype.error = function(){};
_fake_console.prototype.log= function(){};
//IE7 love
if(typeof console !== "undefined") {
_console = console;
try {
_console.log.apply(_console, ['cartodb.js ' + cartodb.VERSION])
} catch(e) {
_console = new _fake_console();
}
} else {
_console = new _fake_console();
}
cdb.core.Log = Backbone.Model.extend({
error: function() {
_console.error.apply(_console, arguments);
if(cdb.config.ERROR_TRACK_ENABLED) {
cdb.errors.create({
msg: Array.prototype.slice.call(arguments).join('')
});
}
},
log: function() {
_console.log.apply(_console, arguments);
},
info: function() {
_console.log.apply(_console, arguments);
},
debug: function() {
if (cdb.DEBUG) _console.log.apply(_console, arguments);
}
});
})();
cdb.log = new cdb.core.Log({tag: 'cdb'});

View File

@@ -0,0 +1,91 @@
(function() {
cdb._debugCallbacks= function(o) {
var callbacks = o._callbacks;
for(var i in callbacks) {
var node = callbacks[i];
console.log(" * ", i);
var end = node.tail;
while ((node = node.next) !== end) {
console.log(" - ", node.context, (node.context && node.context.el) || 'none');
}
}
}
/**
* Base Model for all CartoDB model.
* DO NOT USE Backbone.Model directly
* @class cdb.core.Model
*/
var Model = cdb.core.Model = Backbone.Model.extend({
initialize: function(options) {
_.bindAll(this, 'fetch', 'save', 'retrigger');
return Backbone.Model.prototype.initialize.call(this, options);
},
/**
* We are redefining fetch to be able to trigger an event when the ajax call ends, no matter if there's
* a change in the data or not. Why don't backbone does this by default? ahh, my friend, who knows.
* @method fetch
* @param args {Object}
*/
fetch: function(args) {
var self = this;
// var date = new Date();
this.trigger('loadModelStarted');
$.when(this.elder('fetch', args)).done(function(ev){
self.trigger('loadModelCompleted', ev);
// var dateComplete = new Date()
// console.log('completed in '+(dateComplete - date));
}).fail(function(ev) {
self.trigger('loadModelFailed', ev);
})
},
/**
* Changes the attribute used as Id
* @method setIdAttribute
* @param attr {String}
*/
setIdAttribute: function(attr) {
this.idAttribute = attr;
},
/**
* Listen for an event on another object and triggers on itself, with the same name or a new one
* @method retrigger
* @param ev {String} event who triggers the action
* @param obj {Object} object where the event happens
* @param obj {Object} [optional] name of the retriggered event;
* @todo [xabel]: This method is repeated here and in the base view definition. There's should be a way to make it unique
*/
retrigger: function(ev, obj, retrigEvent) {
if(!retrigEvent) {
retrigEvent = ev;
}
var self = this;
obj.bind && obj.bind(ev, function() {
self.trigger(retrigEvent);
}, self)
},
/**
* We need to override backbone save method to be able to introduce new kind of triggers that
* for some reason are not present in the original library. Because you know, it would be nice
* to be able to differenciate "a model has been updated" of "a model is being saved".
* TODO: remove jquery from here
* @param {object} opt1
* @param {object} opt2
* @return {$.Deferred}
*/
save: function(opt1, opt2) {
var self = this;
if(!opt2 || !opt2.silent) this.trigger('saving');
var promise = Backbone.Model.prototype.save.apply(this, arguments);
$.when(promise).done(function() {
if(!opt2 || !opt2.silent) self.trigger('saved');
}).fail(function() {
if(!opt2 || !opt2.silent) self.trigger('errorSaving')
})
return promise;
}
});
})();

View File

@@ -0,0 +1,165 @@
/*
# metrics profiler
## timing
```
var timer = Profiler.metric('resource:load')
time.start();
...
time.end();
```
## counters
```
var counter = Profiler.metric('requests')
counter.inc(); // 1
counter.inc(10); // 11
counter.dec() // 10
counter.dec(10) // 0
```
## Calls per second
```
var fps = Profiler.metric('fps')
function render() {
fps.mark();
}
```
*/
(function(exports) {
var MAX_HISTORY = 1024;
function Profiler() {}
Profiler.metrics = {};
Profiler._backend = null;
Profiler.get = function(name) {
return Profiler.metrics[name] || {
max: 0,
min: Number.MAX_VALUE,
avg: 0,
total: 0,
count: 0,
last: 0,
history: typeof(Float32Array) !== 'undefined' ? new Float32Array(MAX_HISTORY) : []
};
};
Profiler.backend = function (_) {
Profiler._backend = _;
}
Profiler.new_value = function (name, value, type, defer) {
type = type || 'i';
var t = Profiler.metrics[name] = Profiler.get(name);
t.max = Math.max(t.max, value);
t.min = Math.min(t.min, value);
t.total += value;
++t.count;
t.avg = t.total / t.count;
t.history[t.count%MAX_HISTORY] = value;
if (!defer) {
Profiler._backend && Profiler._backend([type, name, value]);
} else {
var n = new Date().getTime()
// don't allow to send stats quick
if (n - t.last > 1000) {
Profiler._backend && Profiler._backend([type, name, t.avg]);
t.last = n;
}
}
};
Profiler.print_stats = function () {
for (k in Profiler.metrics) {
var t = Profiler.metrics[k];
console.log(" === " + k + " === ");
console.log(" max: " + t.max);
console.log(" min: " + t.min);
console.log(" avg: " + t.avg);
console.log(" count: " + t.count);
console.log(" total: " + t.total);
}
};
function Metric(name) {
this.t0 = null;
this.name = name;
this.count = 0;
}
Metric.prototype = {
//
// start a time measurement
//
start: function() {
this.t0 = +new Date();
return this;
},
// elapsed time since start was called
_elapsed: function() {
return +new Date() - this.t0;
},
//
// finish a time measurement and register it
// ``start`` should be called first, if not this
// function does not take effect
//
end: function(defer) {
if (this.t0 !== null) {
Profiler.new_value(this.name, this._elapsed(), 't', defer);
this.t0 = null;
}
},
//
// increments the value
// qty: how many, default = 1
//
inc: function(qty) {
qty = qty === undefined ? 1: qty;
Profiler.new_value(this.name, qty, 'i');
},
//
// decrements the value
// qty: how many, default = 1
//
dec: function(qty) {
qty = qty === undefined ? 1: qty;
Profiler.new_value(this.name, qty, 'd');
},
//
// measures how many times per second this function is called
//
mark: function() {
++this.count;
if(this.t0 === null) {
this.start();
return;
}
var elapsed = this._elapsed();
if(elapsed > 1) {
Profiler.new_value(this.name, this.count);
this.count = 0;
this.start();
}
}
};
Profiler.metric = function(name) {
return new Metric(name);
};
exports.Profiler = Profiler;
})(cdb.core);

View File

@@ -0,0 +1,25 @@
(function(exports, w) {
exports.sanitize = w.html;
/**
* Sanitize inputHtml of unsafe HTML tags & attributes
* @param {String} inputHtml
* @param {Function,false,null,undefined} optionalSanitizer By default undefined, for which the default sanitizer will be used.
* Pass a function (that takes inputHtml) to sanitize yourself, or false/null to skip sanitize call.
*/
exports.sanitize.html = function(inputHtml, optionalSanitizer) {
if (!inputHtml) return;
if (optionalSanitizer === undefined) {
return exports.sanitize.sanitize(inputHtml, function(url) {
// Return all URLs for <a href=""> (javascript: and data: URLs are removed prior to this fn is called)
return url;
});
} else if (typeof optionalSanitizer === 'function') {
return optionalSanitizer(inputHtml);
} else { // alt sanitization set to false/null/other, treat as if caller takes responsibility to sanitize output
return inputHtml;
}
};
})(cdb.core, window);

View File

@@ -0,0 +1,133 @@
/**
* template system
* usage:
var tmpl = new cdb.core.Template({
template: "hi, my name is {{ name }}",
type: 'mustache' // undescore by default
});
console.log(tmpl.render({name: 'rambo'})));
// prints "hi, my name is rambo"
you could pass the compiled tempalte directly:
var tmpl = new cdb.core.Template({
compiled: function() { return 'my compiled template'; }
});
*/
cdb.core.Template = Backbone.Model.extend({
initialize: function() {
this.bind('change', this._invalidate);
this._invalidate();
},
url: function() {
return this.get('template_url');
},
parse: function(data) {
return {
'template': data
};
},
_invalidate: function() {
this.compiled = null;
if(this.get('template_url')) {
this.fetch();
}
},
compile: function() {
var tmpl_type = this.get('type') || 'underscore';
var fn = cdb.core.Template.compilers[tmpl_type];
if(fn) {
return fn(this.get('template'));
} else {
cdb.log.error("can't get rendered for " + tmpl_type);
}
return null;
},
/**
* renders the template with specified vars
*/
render: function(vars) {
var c = this.compiled = this.compiled || this.get('compiled') || this.compile();
var rendered = c(vars);
return rendered;
},
asFunction: function() {
return _.bind(this.render, this);
}
}, {
compilers: {
'underscore': _.template,
'mustache': typeof(Mustache) === 'undefined' ?
null :
// Replacement for Mustache.compile, which was removed in version 0.8.0
function compile(template) {
Mustache.parse(template);
return function (view, partials) {
return Mustache.render(template, view, partials);
};
}
},
compile: function(tmpl, type) {
var t = new cdb.core.Template({
template: tmpl,
type: type || 'underscore'
});
return _.bind(t.render, t);
}
}
);
cdb.core.TemplateList = Backbone.Collection.extend({
model: cdb.core.Template,
getTemplate: function(template_name) {
if (this.namespace) {
template_name = this.namespace + template_name;
}
var t = this.find(function(t) {
return t.get('name') === template_name;
});
if(t) {
return _.bind(t.render, t);
}
cdb.log.error(template_name + " not found");
return null;
}
});
/**
* global variable
*/
cdb.templates = new cdb.core.TemplateList();
/**
* load JST templates.
* rails creates a JST variable with all the templates.
* This functions loads them as default into cbd.template
*/
cdb._loadJST = function() {
if(typeof(window.JST) !== undefined) {
cdb.templates.reset(
_(JST).map(function(tmpl, name) {
return { name: name, compiled: tmpl };
})
);
}
};

View File

@@ -0,0 +1,124 @@
cdb.core.util = {};
cdb.core.util.isCORSSupported = function() {
return 'withCredentials' in new XMLHttpRequest();
};
cdb.core.util.array2hex = function(byteArr) {
var encoded = []
for(var i = 0; i < byteArr.length; ++i) {
encoded.push(String.fromCharCode(byteArr[i] + 128));
}
return cdb.core.util.btoa(encoded.join(''));
};
cdb.core.util.btoa = function(data) {
if (typeof window['btoa'] == 'function') {
return cdb.core.util.encodeBase64Native(data);
};
return cdb.core.util.encodeBase64(data);
};
cdb.core.util.encodeBase64Native = function (input) {
return btoa(input);
};
// ie7 btoa,
// from http://phpjs.org/functions/base64_encode/
cdb.core.util.encodeBase64 = function (data) {
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
ac = 0,
enc = "",
tmp_arr = [];
if (!data) {
return data;
}
do { // pack three octets into four hexets
o1 = data.charCodeAt(i++);
o2 = data.charCodeAt(i++);
o3 = data.charCodeAt(i++);
bits = o1 << 16 | o2 << 8 | o3;
h1 = bits >> 18 & 0x3f;
h2 = bits >> 12 & 0x3f;
h3 = bits >> 6 & 0x3f;
h4 = bits & 0x3f;
// use hexets to index into b64, and append result to encoded string
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
} while (i < data.length);
enc = tmp_arr.join('');
var r = data.length % 3;
return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
};
cdb.core.util.uniqueCallbackName = function(str) {
cdb.core.util._callback_c = cdb.core.util._callback_c || 0;
++cdb.core.util._callback_c;
return cdb.core.util.crc32(str) + "_" + cdb.core.util._callback_c;
};
cdb.core.util.crc32 = function(str) {
var crcTable = cdb.core.util._crcTable || (cdb.core.util._crcTable = cdb.core.util._makeCRCTable());
var crc = 0 ^ (-1);
for (var i = 0, l = str.length; i < l; ++i ) {
crc = (crc >>> 8) ^ crcTable[(crc ^ str.charCodeAt(i)) & 0xFF];
}
return (crc ^ (-1)) >>> 0;
};
cdb.core.util._makeCRCTable = function() {
var c;
var crcTable = [];
for(var n = 0; n < 256; ++n){
c = n;
for(var k = 0; k < 8; ++k){
c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
crcTable[n] = c;
}
return crcTable;
};
cdb.core.util._inferBrowser = function(ua){
var browser = {};
ua = ua || window.navigator.userAgent;
function detectIE() {
var msie = ua.indexOf('MSIE ');
var trident = ua.indexOf('Trident/');
if (msie > -1 || trident > -1) return true;
return false;
};
function getIEVersion(){
if (!document.compatMode) return 5
if (!window.XMLHttpRequest) return 6
if (!document.querySelector) return 7;
if (!document.addEventListener) return 8;
if (!window.atob) return 9;
if (document.all) return 10;
else return 11;
};
if(detectIE()){
browser.ie = {version: getIEVersion()}
}
else if(ua.indexOf('Edge/') > -1) browser.edge = ua;
else if(ua.indexOf('Chrome') > -1) browser.chrome = ua;
else if(ua.indexOf('Firefox') > -1) browser.firefox = ua;
else if(ua.indexOf("Opera") > -1) browser.opera = ua;
else if(ua.indexOf("Safari") > -1) browser.safari = ua;
return browser;
}
cdb.core.util.browser = cdb.core.util._inferBrowser();

View File

@@ -0,0 +1,171 @@
(function() {
/**
* Base View for all CartoDB views.
* DO NOT USE Backbone.View directly
*/
var View = cdb.core.View = Backbone.View.extend({
classLabel: 'cdb.core.View',
constructor: function(options) {
this._models = [];
this._subviews = {};
Backbone.View.call(this, options);
View.viewCount++;
View.views[this.cid] = this;
this._created_at = new Date();
cdb.core.Profiler.new_value('total_views', View.viewCount);
},
add_related_model: function(m) {
if(!m) throw "added non valid model"
this._models.push(m);
},
addView: function(v) {
this._subviews[v.cid] = v;
v._parent = this;
},
removeView: function(v) {
delete this._subviews[v.cid];
},
clearSubViews: function() {
_(this._subviews).each(function(v) {
v.clean();
});
this._subviews = {};
},
/**
* this methid clean removes the view
* and clean and events associated. call it when
* the view is not going to be used anymore
*/
clean: function() {
var self = this;
this.trigger('clean');
this.clearSubViews();
// remove from parent
if(this._parent) {
this._parent.removeView(this);
this._parent = null;
}
this.remove();
this.unbind();
// remove this model binding
if (this.model && this.model.unbind) this.model.unbind(null, null, this);
// remove model binding
_(this._models).each(function(m) {
m.unbind(null, null, self);
});
this._models = [];
View.viewCount--;
delete View.views[this.cid];
return this;
},
/**
* utility methods
*/
getTemplate: function(tmpl) {
if(this.options.template) {
return _.template(this.options.template);
}
return cdb.templates.getTemplate(tmpl);
},
show: function() {
this.$el.show();
},
hide: function() {
this.$el.hide();
},
/**
* Listen for an event on another object and triggers on itself, with the same name or a new one
* @method retrigger
* @param ev {String} event who triggers the action
* @param obj {Object} object where the event happens
* @param obj {Object} [optional] name of the retriggered event;
*/
retrigger: function(ev, obj, retrigEvent) {
if(!retrigEvent) {
retrigEvent = ev;
}
var self = this;
obj.bind && obj.bind(ev, function() {
self.trigger(retrigEvent);
}, self)
// add it as related model//object
this.add_related_model(obj);
},
/**
* Captures an event and prevents the default behaviour and stops it from bubbling
* @method killEvent
* @param event {Event}
*/
killEvent: function(ev) {
if(ev && ev.preventDefault) {
ev.preventDefault();
};
if(ev && ev.stopPropagation) {
ev.stopPropagation();
};
},
/**
* Remove all the tipsy tooltips from the document
* @method cleanTooltips
*/
cleanTooltips: function() {
this.$('.tipsy').remove();
}
}, {
viewCount: 0,
views: {},
/**
* when a view with events is inherit and you want to add more events
* this helper can be used:
* var MyView = new core.View({
* events: cdb.core.View.extendEvents({
* 'click': 'fn'
* })
* });
*/
extendEvents: function(newEvents) {
return function() {
return _.extend(newEvents, this.constructor.__super__.events);
};
},
/**
* search for views in a view and check if they are added as subviews
*/
runChecker: function() {
_.each(cdb.core.View.views, function(view) {
_.each(view, function(prop, k) {
if( k !== '_parent' &&
view.hasOwnProperty(k) &&
prop instanceof cdb.core.View &&
view._subviews[prop.cid] === undefined) {
console.log("=========");
console.log("untracked view: ");
console.log(prop.el);
console.log('parent');
console.log(view.el);
console.log(" ");
}
});
});
}
});
})();