cdb.admin.overlays.Text = cdb.geo.ui.Text.extend({ className: "text overlay snap", template_name: 'table/views/overlays/text', events: { "mouseenter .text": "_onMouseEnter", "click .close": "_close", "click .content": "_onClickEdit", "click .text": "_onClickEdit", "dblclick .content": "_onDblClick", "dblclick .text": "_onDblClick", "keyup .text": "_onKeyUp", "paste .text": "_onPaste" }, form_data: [{ name: 'Text', form: { 'font-size': { type: 'simple_number', value: 12, min: 5, max: 50, inc: 2, disable_triggering: true }, 'color': { type: 'color', value: '#FFF', extra: { tick: "left", picker_horizontal_position: "left", picker_vertical_position: "down" }}, 'font-family-name': { type: 'select', value: "Helvetica", extra: ["Helvetica", "Droid Sans", "Vollkorn", "Roboto", "Open Sans", "Lato", "Graduate", "Gravitas One", "Old Standard TT"] }, 'text-align': { type: 'text_align', value: 'left' } } }, { name: 'Box', form: { 'box-color': { type: 'color', value: '#000', extra: { tick: "left", picker_horizontal_position: "left", picker_vertical_position: "down" }}, 'box-opacity': { type: 'simple_opacity', value: .7, min:0, max:1, inc: .1, disable_triggering: true }, 'box-padding': { type: 'simple_number_with_label', value: 10, min: 5, max: 200, inc: 1, label: "P", disable_triggering: true } } }, { name: 'Max Width', form: { 'box-width': { type: 'simple_number', value: 300, min: 50, max: 2000, inc: 10, disable_triggering: true }, } }], initialize: function() { _.bindAll(this, "_close", "_onChangeMode", "_onKeyDown"); this.template = this.getTemplate(this.template_name); this._setupModels(); }, // Setup the internal and custom model _setupModels: function() { var self = this; var extra = this.model.get("extra"); this.model.set({ text: extra.text }, { silent: true }); var applyStyle = function() { self._applyStyle(true); }; // Binding this.model.bind('remove', this.hide, this); this.model.bind('change:style', applyStyle, this); this.model.bind('change:text', this._setText, this); this.model.bind('change:display', this._onChangeDisplay, this); this.model.bind('change:extra', this._onChangeExtra, this); this.model.bind('change:selected', this._onChangeSelected, this); // Internal model to store the editing state this.editModel = new cdb.core.Model({ mode: "" }); this.editModel.bind('change:mode', this._onChangeMode, this); this.add_related_model(this.editModel); }, // Element events _onKeyUp: function(e) { if (this.timeout) { clearTimeout(this.timeout); } this.model.set({ text: this.$text.html() }, { silent: true }); }, _onClickEdit: function(e) { this.killEvent(e); cdb.god.trigger("closeOverlayDropdown"); $(document).bind('keydown', this._onKeyDown); this._savePosition(false); this.trigger("clickEdit", this.model, this.form_data); this.model.set("selected", true); }, _onKeyDown: function(e) { var selected = this.model.get("selected"); if (selected) { var editable = this.editModel.get("mode") == "editable"; var focus = this.$(".overlay_text").is(":focus"); // hitting the backspace removes the overlay if ($(e.target).hasClass("overlay_text") || $(e.target).hasClass("cartodb-map")) { if (e.keyCode === $.ui.keyCode.BACKSPACE && (!editable || !focus)) { this.killEvent(e); this._close(); } } if (!focus) { if (e.which == 67 && (e.ctrlKey || e.metaKey)) { this.trigger('duplicate', this.model, this); } } } if (e.keyCode === $.ui.keyCode.ESCAPE) { this.editModel.set("mode", ""); } }, _onPaste: function(e) { var self = this; setTimeout(function() { var text = cdb.Utils.stripHTML(self.model.get("text")); self.model.set("text", text) }, 200); }, _onDblClick: function(e) { this.killEvent(e); this._savePosition(true); }, _savePosition: function(editable) { var extra = this.model.get("extra"); var x = this.model.get("x"); var y = this.model.get("y"); var oldX = this.$el.position().left; var oldY = this.$el.position().top; var portrait_direction = extra.portrait_dominant_side; var landscape_direction = extra.landscape_dominant_side; if (y === 0 && portrait_dominant_side === "bottom") oldY = y; if (x === 0 && landscape_dominant_side === "right") oldX = x; var moved = true; // If we didn't move the overlay if (oldX === x && y === oldY || x === 0 && landscape_dominant_side === "right" && y === oldY || y == 0 && portrait_dominant_side === "bottom" && oldX === x) { moved = false; this.$el.addClass("selected"); if (editable) { this.editModel.set("mode", "editable"); return; } } x = this.$el.position().left; y = this.$el.position().top; var width = this.$el.width(); var height = this.$el.height(); var right = $(".cartodb-map").width() - x; var bottom = $(".cartodb-map").height() - y; var right_position = right - width; var bottom_position = bottom - height; var map_width = $(".cartodb-map").width(); var map_height = $(".cartodb-map").width(); var left_percentage = (x + (width/2)) / map_width * 100; var top_percentage = (y + (height/2)) / map_height * 100; var landscape_dominant_side = x <= right_position ? "left" : "right"; var portrait_dominant_side = y <= bottom_position ? "top" : "bottom"; // Default positions extra.default_position = false; extra.landscape_dominant_side = landscape_dominant_side; extra.portrait_dominant_side = portrait_dominant_side; extra.top_percentage = top_percentage; extra.left_percentage = left_percentage; extra.right_position = right_position; extra.bottom_position = bottom_position; extra.right = right; extra.bottom = bottom; extra.width = width; extra.height = height; this.model.set({ extra: extra }, { silent: true}); this.model.set({ x: x, y: y }); if (moved) { this.model.save(); } }, _onMouseDown: function() { }, _onMouseEnter: function() { this.$el.addClass("hover"); if (this.editModel.get("mode") === "editable") { if (this.timeout) clearTimeout(this.timeout); } }, show: function(animated) { this.$el.show(); if (true) this.$el.addClass('animated bounceIn'); }, hide: function(callback) { var self = this; this.$el .removeClass('animated bounceIn') .addClass('animated bounceOut') .removeClass('selected'); callback && _.isFunction(callback) && callback(); $(document).unbind('keydown', this._onKeyDown); cdb.god.unbind("closeDialogs", this._onCloseDialogs, this); // Give it some time to complete the animation setTimeout(function() { self.clean(); }, 550); }, _close: function(e) { this.killEvent(e); var self = this; this.hide(function() { self.trigger("remove", self); }); }, _onChangeDisplay: function() { var display = this.model.get("display"); if (display) { this.show(); } else { this.$el.hide(); } }, _onChangeExtra: function() { var extra = this.model.get("extra"); extra.text = this.model.get("text"); this.model.set({ extra: extra }, { silent: true }); }, /* * Applies style to the content of the widget */ _applyStyle: function(save) { var style = this.model.get("style"); var boxColor = style["box-color"]; var boxOpacity = style["box-opacity"]; var boxWidth = style["box-width"]; var boxPadding = style["box-padding"]; var fontFamily = style["font-family-name"]; this.$text.css(style); this.$(".content").css("padding", boxPadding); this.$text.css("font-size", style["font-size"] + "px"); this.$el.css("z-index", style["z-index"]); var rgbaCol = 'rgba(' + parseInt(boxColor.slice(-6,-4),16) + ',' + parseInt(boxColor.slice(-4,-2),16) + ',' + parseInt(boxColor.slice(-2),16) +', ' + boxOpacity + ' )'; this.$el.css("background-color", rgbaCol); this.$el.css("max-width", boxWidth); 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.css("width", "auto"); 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 (save) this.model.save(); }, _onChangeSelected: function() { var selected = this.model.get("selected"); if (selected) { this.$el.addClass("selected"); } else { this.$el.removeClass("selected"); this._disableEditingMode(); } }, _onChangeMode: function() { var mode = this.editModel.get("mode"); this.trigger('editing', mode === 'editable', this); if (mode === "editable") { this._enableEditingMode(); } else { this._disableEditingMode(); } }, _enableEditingMode: function() { this.$el .addClass("editable") .addClass("disabled"); this.$text.attr("contenteditable", true).focus(); var style = this.model.get("style"); var width = style["box-width"]; var text = this.model.get("text"); this.$el.css("width", "auto"); this.$el.css("max-width", width); this.$text.html(text); this.$(".hint").fadeIn(150); }, _isEmptyText: function(text) { var regexp = new RegExp(/^(
/g, ""); text = text.replace(/ /g, " "); text = text.replace(/<\/p>/g, ""); return text; }, _place: function() { var mode = this.editModel.get("mode"); var extra = this.model.get("extra"); if (mode === "editable" || !extra) { return; } var landscape_dominant_side = extra.landscape_dominant_side; var portrait_dominant_side = extra.portrait_dominant_side; if (portrait_dominant_side === 'bottom') { this.$el.offset({ bottom: extra.bottom_position }); this.$el.css({ top: "auto", bottom: extra.bottom_position }); } else { this.$el.offset({ top: this.model.get("y"), bottom: "auto" }); } if (landscape_dominant_side === 'right') { this.$el.offset({ right: extra.right_position }); this.$el.css({ left: "auto", right: extra.right_position }); } else { this.$el.offset({ left: this.model.get("x"), right: "auto" }); } }, _onCloseDialogs: function() { if (this.model.get("selected") !== undefined) this.model.set("selected", false); }, render: function() { this._place(); this.$el.append(this.template(this.model.attributes)); this.$text = this.$(".content div.text"); var text = this._transformToMarkdown(this.model.get("text")); this.$text.html(text); this._applyStyle(false); this._onChangeExtra(); this.$el.addClass(this.model.get("device")); cdb.god.unbind("closeDialogs", this._onCloseDialogs, this); cdb.god.bind("closeDialogs", this._onCloseDialogs, this); return this; } });