lib/assets/javascripts/cartodb/table/basemap/basemap_dropdown/basemap_dropdown.js
/**
* User options dropdown (extends Dropdown)
*
* It shows the content in a dropdown (or dropup) with a special effect.
*
* Usage example:
*
* var user_menu = new cdb.admin.DropdownBasemap({
*
* });
*
*/
cdb.admin.DropdownBasemap = cdb.ui.common.Dropdown.extend({
className: 'dropdown basemap',
// TODO: remove gmaps code from here
defaults: {
speedIn: 50,
speedOut: 50,
basemaps_per_list: 8,
available_basemaps: {}
},
events: {
"click a.add" : "_openSelector",
"click" : "killEvent"
},
initialize: function() {
_.bindAll(this, "add", "setActiveBaselayer", "open", "hide", "_handleClick", "_keydown");
// Extend options
_.defaults(this.options, this.defaults);
// Dropdown template
this.template_base = cdb.templates.getTemplate(this.options.template_base);
// Bind to target
$(this.options.target).bind({"click": this._handleClick});
// Bind ESC key
$(document).bind('keydown', this._keydown);
// Is open flag
this.isOpen = false;
this.baseLayers = this.options.baseLayers;
this.user = this.options.user;
this._setupBindings();
},
_setupBindings: function() {
this._bindBaseLayers();
this._bindMapView();
},
_bindBaseLayers: function() {
if (this.baseLayers) {
this.baseLayers.bind('reset', this.render, this);
this.baseLayers.bind('add', this.add, this);
}
},
// Bind any change of mapview to base chooser
_bindMapView: function() {
var self = this;
this.model.layers.bind('change add reset',
function(a) {
self._checkPlainColor();
self.setActiveBaselayer(a);
});
},
/**
* Checks if new base layer loaded is a plain color type
* If so, it is applied to background map color view as the model
*/
_checkPlainColor: function() {
var baselayer = this.model.getBaseLayer();
if (baselayer && baselayer.get('type') == "Plain") {
if (baselayer.get('color')) {
this.background_color.model = baselayer;
} else {
this.background_image.model = baselayer;
}
}
},
_toggleAddBaseLayer: function() {
var
$add = this.$el.find(".add_basemap"),
$ul = this.$el.find(".custom ul.yours");
if ($ul.find("li").length <= this.defaults.basemaps_per_list) {
$add.removeClass("hidden");
$add.parent().append($add);
} else {
$add.addClass("hidden");
}
},
// returns true if google maps is enabled if google maps library loaded
googleMapsEnabled: function() {
return typeof(google) !== 'undefined' && typeof(google.maps) !== 'undefined';
},
add: function(lyr) {
//if (!lyr.get("urlTemplate")) return;
var v;
var type = lyr.get("type") && lyr.get("type").toLowerCase();
var proxy = lyr.get("proxy"); // If it comes from CartoDB proxy
if (lyr.get('className') === 'wms' || proxy ) {
v = new cdb.admin.BaseMapView({ model: lyr, map: this.model, className: "wms" });
} else if (lyr.get('className') === 'googlemaps') {
if (this.googleMapsEnabled()) {
v = new cdb.admin.GMapsBaseView({
model: lyr,
map: this.model
});
} else {
return;
}
} else {
// this is a temporal fix, if google maps feature flag is enabled show only maps in group google maps
if (this.user.featureEnabled('google_maps') && lyr.get('category') !== 'Google') {
return;
}
v = new cdb.admin.BaseMapView({ model: lyr, map: this.model });
}
lyr.bind("destroy", this._toggleAddBaseLayer, this);
this.addView(v);
var name = v.model.get("name");
if (!name) {
name = "Custom basemap " + v.model.get("order");
} else {
name.replace(/_/g, "");
}
v.model.set("name", name);
var $element = $(v.render().el);
var baseType = v.model.get("base_type") || "default";
baseType.toLowerCase();
var template = cdb.templates.getTemplate('table/views/basemap/basemap_dropdown_cat');
if (baseType) {
var category = lyr.get("category");
category = category || 'Others';
var listClassName = '.custom ul.' + category.toLowerCase();
var $ul = this.$(listClassName);
if (!$ul.length) {
this.$('.js-customBasemaps').prepend(
template({
category: category
})
);
$ul = this.$(listClassName);
}
$ul.append($element);
}
this._toggleAddBaseLayer();
},
_getCleanClassName: function(className) {
if (!className) return false;
return className
.replace(/default /g, '')
.replace(/\s+/g, '')
.replace(/[^a-zA-Z_0-9 ]/g, "")
.toLowerCase();
},
_updateTarget: function(subview) {
var className = subview.model.get("className");
var type = subview.model.get("type");
var color = subview.model.get("color");
var image = subview.model.get("image");
var kind = (color ? 'color' : undefined ) || ( image ? 'pattern' : undefined ) || '';
var className = this._getCleanClassName(className);
if (className === 'googlemaps') {
className = subview.model.get('base_type');
// Adding the custom base name to set properly
// the background image
var baseName = subview.model.get('baseName');
className += baseName ? ( '.' + baseName ) : '';
}
var $el = this.$el.find("a." + className);
if (kind) {
$el = this.$el.find("a." + className + '.' + kind);
}
this._setName($el);
this._setThumbnail($el, color, image);
},
_setName: function($el) {
var name = $el.attr("data-name");
if (!name) name = "Change basemap";
name = cdb.Utils.truncate(name, 40);
this.options.target.find(".info .name").html(name);
},
_setThumbnail: function($el, color, image) {
if (image) image = "url(" + image + ")" ;
var $thumb = $el.find(".thumb");
// Thanks Firefox
var bkg = image || $thumb.css("background-image");
var size = $thumb.css("background-size");
var pos = $thumb.css("background-position");
if (!bkg || bkg === "none") {
color = color || $thumb.css("background-color");
} else {
color = "#000";
}
this.options.target.find("> .thumb").css({
"background-image": bkg,
"background-position": pos,
"background-size": size,
"background-color": color
});
},
_addBackgroundBasemap: function() {
this.background_image = new cdb.admin.BackgroundMapImageView({ model: this.model.getBaseLayer(), map: this.model, user: this.options.user });
this.addView(this.background_image);
var $view = $(this.background_image.render().el);
var $ul = this.$el.find('.custom ul.custom');
$ul.append($view);
},
_addColorBasemap: function() {
this.background_color = new cdb.admin.BackgroundMapColorView({ model: this.model.getBaseLayer(), map: this.model });
this.addView(this.background_color);
var $view = $(this.background_color.render().el);
var $ul = this.$el.find('.custom ul.custom');
$ul.append($view);
this.background_color.delegateEvents();
},
_addAddBasemapLink: function() {
var $a = $('<li class="add_basemap hidden"><a class="add small" href="#add_basemap"><div class="thumb"></div></a></li>');
var $ul = this.$el.find('.custom ul.yours');
$ul.append($a);
},
show: function() {
var dfd = $.Deferred();
var self = this;
//sometimes this dialog is child of a node that is removed
//for that reason we link again DOM events just in case
this.delegateEvents();
this.$el
.css({
marginTop: self.options.vertical_position == "down" ? "-10px" : "10px",
opacity:0,
display:"block"
})
.animate({
margin: "0",
opacity: 1
}, {
"duration": this.options.speedIn,
"complete": function(){
dfd.resolve();
}
});
this.trigger("onDropdownShown", this.el);
this._toggleAddBaseLayer();
return dfd.promise();
},
open: function(ev,target) {
// Target
var $target = target && $(target) || this.options.target;
this.options.target = $target;
var vertical_position = this.options.target.position().top + this.options.vertical_offset;
var vertical_property = this.options.vertical_position == "up" ? "bottom" : "top";
this.$el.css(vertical_property, vertical_position);
this.$el.css({
left: this.options.horizontal_offset
})
.addClass(
// Add vertical and horizontal position class
(this.options.vertical_position == "up" ? "vertical_top" : "vertical_bottom" )
+ " " +
(this.options.horizontal_position == "right" ? "horizontal_right" : "horizontal_left" )
+ " " +
// Add tick class
"border tick_" + this.options.tick
)
// Show it
this.show();
// Dropdown open
this.isOpen = true;
},
hide: function(done) {
if (!this.isOpen) {
done && done();
return;
}
var self = this;
this.isOpen = false;
this.$el.animate({
marginTop: self.options.vertical_position == "down" ? "10px" : "-10px",
opacity: 0
}, this.options.speedOut, function(){
// And hide it
self.$el.hide();
});
this.trigger("onDropdownHidden",this.el);
},
_addBaseDefault: function() {
this.baseLayers.each(this.add);
},
/**
* When a new base layer is activated,
* we apply the select to the correct base layer button
*/
setActiveBaselayer: function(layer) {
for (var sv in this._subviews) {
var subview = this._subviews[sv];
if (subview.model && this.model.getBaseLayer && this.model.getBaseLayer().isEqual(subview.model)) {
// We have to CHANGE AND REMOVE this things, we can't
// control the basemap selected in this way :(
if (subview.model.get('type') != "Plain") {
subview.selectButton();
}
this._updateTarget(subview);
return;
}
}
},
/*
* Creates a modal dialog to let the user create a new basemap
*/
_openSelector: function(ev) {
ev.preventDefault();
var view = new cdb.editor.AddCustomBasemapView({
map: this.model,
baseLayers: this.baseLayers,
clean_on_hide: true,
enter_to_confirm: true
});
view.appendToBody();
},
/*
* Renders the basemap dropdown
*/
render: function() {
this.clearSubViews();
var googleMapsEnabled = this.googleMapsEnabled() || this.user.featureEnabled('google_maps');
this.$el.html(
this.template_base(
_.extend(this.options, {
googleMapsEnabled: googleMapsEnabled
})
)
);
this._addBaseDefault();
this._addColorBasemap();
this._addBackgroundBasemap();
if (!googleMapsEnabled) {
this._addAddBasemapLink();
}
return this;
},
clean: function() {
$(document).unbind('keydown', this._keydown);
cdb.ui.common.Dropdown.prototype.clean.call(this);
}
});