352 lines
8.6 KiB
JavaScript
352 lines
8.6 KiB
JavaScript
cdb.geo.ui.Annotation = cdb.core.View.extend({
|
|
|
|
className: "cartodb-overlay overlay-annotation",
|
|
|
|
defaults: {
|
|
minZoom: 0,
|
|
maxZoom: 40,
|
|
style: {
|
|
textAlign: "left",
|
|
zIndex: 5,
|
|
color: "#ffffff",
|
|
fontSize: "13",
|
|
fontFamilyName: "Helvetica",
|
|
boxColor: "#333333",
|
|
boxOpacity: 0.7,
|
|
boxPadding: 10,
|
|
lineWidth: 50,
|
|
lineColor: "#333333"
|
|
}
|
|
},
|
|
|
|
template: cdb.core.Template.compile(
|
|
'<div class="content">\
|
|
<div class="text widget_text">{{{ text }}}</div>\
|
|
<div class="stick"><div class="ball"></div></div>\
|
|
</div>',
|
|
'mustache'
|
|
),
|
|
|
|
events: {
|
|
"click": "stopPropagation"
|
|
},
|
|
|
|
stopPropagation: function(e) {
|
|
e.stopPropagation();
|
|
},
|
|
|
|
initialize: function() {
|
|
|
|
this.template = this.options.template || this.template;
|
|
this.mapView = this.options.mapView;
|
|
|
|
this.mobileEnabled = /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
|
|
this._cleanStyleProperties(this.options.style);
|
|
|
|
_.defaults(this.options.style, this.defaults.style);
|
|
|
|
this._setupModels();
|
|
|
|
this._bindMap();
|
|
|
|
},
|
|
|
|
_setupModels: function() {
|
|
|
|
this.model = new cdb.core.Model({
|
|
display: true,
|
|
hidden: false,
|
|
text: this.options.text,
|
|
latlng: this.options.latlng,
|
|
minZoom: this.options.minZoom || this.defaults.minZoom,
|
|
maxZoom: this.options.maxZoom || this.defaults.maxZoom
|
|
});
|
|
|
|
this.model.on("change:display", this._onChangeDisplay, this);
|
|
this.model.on("change:text", this._onChangeText, this);
|
|
this.model.on('change:latlng', this._place, this);
|
|
|
|
this.model.on('change:minZoom', this._applyZoomLevelStyle, this);
|
|
this.model.on('change:maxZoom', this._applyZoomLevelStyle, this);
|
|
|
|
this.style = new cdb.core.Model(this.options.style);
|
|
|
|
this.style.on("change", this._applyStyle, this);
|
|
|
|
this.add_related_model(this.style);
|
|
|
|
},
|
|
|
|
_bindMap: function() {
|
|
|
|
this.mapView.map.bind('change', this._place, this);
|
|
this.mapView.map.bind('change:zoom', this._applyZoomLevelStyle, this);
|
|
this.mapView.bind('zoomstart', this.hide, this);
|
|
this.mapView.bind('zoomend', this.show, this);
|
|
|
|
},
|
|
|
|
_unbindMap: function() {
|
|
|
|
this.mapView.map.unbind('change', this._place, this);
|
|
this.mapView.map.unbind('change:zoom', this._applyZoomLevelStyle, this);
|
|
this.mapView.unbind('zoomstart', this.hide, this);
|
|
this.mapView.unbind('zoomend', this.show, this);
|
|
|
|
},
|
|
|
|
_onChangeDisplay: function() {
|
|
|
|
if (this.model.get("display")) this.show();
|
|
else this.hide();
|
|
|
|
},
|
|
|
|
_onChangeText: function() {
|
|
this.$el.find(".text").html(this._sanitizedText());
|
|
},
|
|
|
|
_sanitizedText: function() {
|
|
return cdb.core.sanitize.html(this.model.get("text"), this.model.get('sanitizeText'));
|
|
},
|
|
|
|
_getStandardPropertyName: function(name) {
|
|
if (!name) {
|
|
return;
|
|
}
|
|
|
|
var parts = name.split("-");
|
|
|
|
if (parts.length === 1) {
|
|
return name;
|
|
} else {
|
|
return parts[0] + _.map(parts.slice(1), function(l) {
|
|
return l.slice(0,1).toUpperCase() + l.slice(1);
|
|
}).join("");
|
|
}
|
|
},
|
|
|
|
_cleanStyleProperties: function(hash) {
|
|
|
|
var standardProperties = {};
|
|
|
|
_.each(hash, function(value, key) {
|
|
standardProperties[this._getStandardPropertyName(key)] = value;
|
|
}, this);
|
|
|
|
this.options.style = standardProperties;
|
|
|
|
},
|
|
|
|
_belongsToCanvas: function() {
|
|
|
|
var mobile = (this.options.device === "mobile") ? true : false;
|
|
return mobile === this.mobileEnabled;
|
|
},
|
|
|
|
show: function(callback) {
|
|
|
|
if (this.model.get("hidden") || !this._belongsToCanvas()) return;
|
|
|
|
var self = this;
|
|
|
|
this.$el.css({ opacity: 0, display: "inline-table" }); // makes the element to behave fine in the borders of the screen
|
|
this.$el.stop().animate({ opacity: 1 }, { duration: 150, complete: function() {
|
|
callback && callback();
|
|
}});
|
|
|
|
},
|
|
|
|
hide: function(callback) {
|
|
this.$el.stop().fadeOut(150, function() {
|
|
callback && callback();
|
|
});
|
|
},
|
|
|
|
_place: function() {
|
|
|
|
var latlng = this.model.get("latlng");
|
|
|
|
var lineWidth = this.style.get("lineWidth");
|
|
var textAlign = this.style.get("textAlign");
|
|
|
|
var pos = this.mapView.latLonToPixel(latlng);
|
|
|
|
if (pos) {
|
|
|
|
var top = pos.y - this.$el.height()/2;
|
|
var left = pos.x + lineWidth;
|
|
|
|
if (textAlign === "right") {
|
|
left = pos.x - this.$el.width() - lineWidth - this.$el.find(".ball").width();
|
|
}
|
|
|
|
this.$el.css({ top: top, left: left });
|
|
|
|
}
|
|
|
|
},
|
|
|
|
setMinZoom: function(zoom) {
|
|
|
|
this.model.set("minZoom", zoom);
|
|
|
|
},
|
|
|
|
setMaxZoom: function(zoom) {
|
|
|
|
this.model.set("maxZoom", zoom);
|
|
|
|
},
|
|
|
|
setPosition: function(latlng) {
|
|
|
|
this.model.set("latlng", latlng);
|
|
|
|
},
|
|
|
|
setText: function(text) {
|
|
|
|
this.model.set("text", text);
|
|
|
|
},
|
|
|
|
setStyle: function(property, value) {
|
|
|
|
var standardProperty = this._getStandardPropertyName(property);
|
|
|
|
if (standardProperty) {
|
|
this.style.set(standardProperty, value);
|
|
}
|
|
|
|
},
|
|
|
|
_applyStyle: function() {
|
|
|
|
var textColor = this.style.get("color");
|
|
var textAlign = this.style.get("textAlign");
|
|
var boxColor = this.style.get("boxColor");
|
|
var boxOpacity = this.style.get("boxOpacity");
|
|
var boxPadding = this.style.get("boxPadding");
|
|
var lineWidth = this.style.get("lineWidth");
|
|
var lineColor = this.style.get("lineColor");
|
|
var fontFamily = this.style.get("fontFamilyName");
|
|
|
|
this.$text = this.$el.find(".text");
|
|
|
|
this.$text.css({ color: textColor, textAlign: textAlign });
|
|
|
|
this.$el.find(".content").css("padding", boxPadding);
|
|
this.$text.css("font-size", this.style.get("fontSize") + "px");
|
|
this.$el.css("z-index", this.style.get("zIndex"));
|
|
|
|
this.$el.find(".stick").css({ width: lineWidth, left: -lineWidth });
|
|
|
|
var fontFamilyClass = "";
|
|
|
|
if (fontFamily == "Droid Sans") fontFamilyClass = "droid";
|
|
else if (fontFamily == "Vollkorn") fontFamilyClass = "vollkorn";
|
|
else if (fontFamily == "Open Sans") fontFamilyClass = "open_sans";
|
|
else if (fontFamily == "Roboto") fontFamilyClass = "roboto";
|
|
else if (fontFamily == "Lato") fontFamilyClass = "lato";
|
|
else if (fontFamily == "Graduate") fontFamilyClass = "graduate";
|
|
else if (fontFamily == "Gravitas One") fontFamilyClass = "gravitas_one";
|
|
else if (fontFamily == "Old Standard TT") fontFamilyClass = "old_standard_tt";
|
|
|
|
this.$el
|
|
.removeClass("droid")
|
|
.removeClass("vollkorn")
|
|
.removeClass("roboto")
|
|
.removeClass("open_sans")
|
|
.removeClass("lato")
|
|
.removeClass("graduate")
|
|
.removeClass("gravitas_one")
|
|
.removeClass("old_standard_tt");
|
|
|
|
this.$el.addClass(fontFamilyClass);
|
|
|
|
if (textAlign === "right") {
|
|
this.$el.addClass("align-right");
|
|
this.$el.find(".stick").css({ left: "auto", right: -lineWidth });
|
|
} else {
|
|
this.$el.removeClass("align-right");
|
|
}
|
|
|
|
this._place();
|
|
this._applyZoomLevelStyle();
|
|
|
|
},
|
|
|
|
_getRGBA: function(color, opacity) {
|
|
return 'rgba(' + parseInt(color.slice(-6,-4),16)
|
|
+ ',' + parseInt(color.slice(-4,-2),16)
|
|
+ ',' + parseInt(color.slice(-2),16)
|
|
+ ',' + opacity + ' )';
|
|
},
|
|
|
|
_applyZoomLevelStyle: function() {
|
|
|
|
var boxColor = this.style.get("boxColor");
|
|
var boxOpacity = this.style.get("boxOpacity");
|
|
var lineColor = this.style.get("lineColor");
|
|
|
|
var minZoom = this.model.get("minZoom");
|
|
var maxZoom = this.model.get("maxZoom");
|
|
|
|
var currentZoom = this.mapView.map.get("zoom");
|
|
|
|
if (currentZoom >= minZoom && currentZoom <= maxZoom) {
|
|
|
|
var rgbaLineCol = this._getRGBA(lineColor, 1);
|
|
var rgbaBoxCol = this._getRGBA(boxColor, boxOpacity);
|
|
|
|
this.$el.find(".text").animate({ opacity: 1 }, 150);
|
|
|
|
this.$el.css("background-color", rgbaBoxCol);
|
|
|
|
this.$el.find(".stick").css("background-color", rgbaLineCol);
|
|
this.$el.find(".ball").css("background-color", rgbaLineCol);
|
|
|
|
this.model.set("hidden", false);
|
|
this.model.set("display", true);
|
|
|
|
} else {
|
|
this.model.set("hidden", true);
|
|
this.model.set("display", false);
|
|
}
|
|
},
|
|
|
|
clean: function() {
|
|
this._unbindMap();
|
|
cdb.core.View.prototype.clean.call(this);
|
|
},
|
|
|
|
_fixLinks: function() {
|
|
|
|
this.$el.find("a").each(function(i, link) {
|
|
$(this).attr("target", "_top");
|
|
});
|
|
|
|
},
|
|
|
|
render: function() {
|
|
var d = _.clone(this.model.attributes);
|
|
d.text = this._sanitizedText();
|
|
this.$el.html(this.template(d));
|
|
|
|
this._fixLinks();
|
|
|
|
var self = this;
|
|
setTimeout(function() {
|
|
self._applyStyle();
|
|
self._applyZoomLevelStyle();
|
|
self.show();
|
|
}, 500);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
});
|