cdb
This commit is contained in:
71
lib/assets/javascripts/cdb/src/core/config.js
Normal file
71
lib/assets/javascripts/cdb/src/core/config.js
Normal 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"
|
||||
});
|
||||
|
||||
})();
|
||||
95
lib/assets/javascripts/cdb/src/core/decorator.js
Normal file
95
lib/assets/javascripts/cdb/src/core/decorator.js
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
70
lib/assets/javascripts/cdb/src/core/loader.js
Normal file
70
lib/assets/javascripts/cdb/src/core/loader.js
Normal 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]);
|
||||
}
|
||||
};
|
||||
|
||||
84
lib/assets/javascripts/cdb/src/core/log.js
Normal file
84
lib/assets/javascripts/cdb/src/core/log.js
Normal 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'});
|
||||
91
lib/assets/javascripts/cdb/src/core/model.js
Normal file
91
lib/assets/javascripts/cdb/src/core/model.js
Normal 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;
|
||||
}
|
||||
});
|
||||
})();
|
||||
165
lib/assets/javascripts/cdb/src/core/profiler.js
Normal file
165
lib/assets/javascripts/cdb/src/core/profiler.js
Normal 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);
|
||||
25
lib/assets/javascripts/cdb/src/core/sanitize.js
Normal file
25
lib/assets/javascripts/cdb/src/core/sanitize.js
Normal 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);
|
||||
133
lib/assets/javascripts/cdb/src/core/template.js
Normal file
133
lib/assets/javascripts/cdb/src/core/template.js
Normal 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 };
|
||||
})
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
124
lib/assets/javascripts/cdb/src/core/util.js
Normal file
124
lib/assets/javascripts/cdb/src/core/util.js
Normal 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();
|
||||
171
lib/assets/javascripts/cdb/src/core/view.js
Normal file
171
lib/assets/javascripts/cdb/src/core/view.js
Normal 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(" ");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
Reference in New Issue
Block a user