lib/assets/javascripts/cartodb/table/menu_modules/codemirror_color_picker.js
/**
* Show color picker when user clicks over
* a color in the Codemirror editor.
*
* new cdb.admin.CodemirrorColorPicker({
* editor: codemirror-editor...
* })
*/
cdb.admin.CodemirrorColorPicker = cdb.core.View.extend({
initialize: function() {
this.editor = this.options.editor;
this._initBinds();
},
_initBinds: function() {
_.bindAll(this, '_onClick', '_destroyPicker', '_onBlur', '_replaceColor');
var self = this;
this.editor.on('mousedown', function(ed, e){
// Hack needed preventing cursor positioning bug
setTimeout(function(){ self._onClick(e) }, 50);
}, this);
this.editor.on('blur', this._onBlur, this);
this.editor.on('viewportChange', this._destroyPicker, this);
this.editor.on('scroll', this._destroyPicker, this);
},
_disableBinds: function() {
this.editor.off('mousedown');
this.editor.off('blur', this._onBlur, this);
this.editor.off('viewportChange', this._destroyPicker, this);
this.editor.off('scroll', this._destroyPicker, this);
cdb.god.unbind('closeDialogs', this._destroyPicker, this);
},
_onClick: function(e) {
e.preventDefault();
var cursor = this.editor.getCursor(true);
var token = this.editor.getTokenAt(cursor);
if (token.type === "color" && e.target.nodeName.toLowerCase() === "span") {
this._openColorPicker(e, cursor, token);
} else if (this.color_picker) {
this._destroyPicker();
}
},
_openColorPicker: function(e, cursor, token) {
this._destroyPicker();
this._createPicker(e);
$('body').append(this.color_picker.render().el);
this.color_picker.init(token.string);
},
_createPicker: function(e) {
this.color_picker = new cdb.admin.ColorPicker({
target: $(e.target),
extra_colors: this._getCurrentUsedColors()
}).bind("colorChosen", this._replaceColor, this);
this._bindPickerEvents();
this.addView(this.color_picker);
},
_getCurrentUsedColors: function() {
if (!this.model) return [];
// Tile style
var style = this.model.get("tile_style");
var cartoParser = new cdb.admin.CartoParser(style);
return cartoParser.colorsUsed( { mode: "hex" });
},
// If user clicks over the color picker
// we have to avoid blur event
_bindPickerEvents: function() {
if (this.color_picker) {
var self = this;
this.editor.on('blur', this._onBlur, this);
this.color_picker.$el.on("mousedown", function() {
self.editor.off('blur', self._onBlur, self);
});
this.color_picker.$el.on("mousemove", function() {
self.editor.off('blur', self._onBlur, self);
});
this.color_picker.$el.on("mouseup", function() {
self.editor.on('blur', self._onBlur, self);
});
setTimeout(function() {
cdb.god.bind('closeDialogs', self._destroyPicker, self);
},100)
}
},
_unbindPickerEvents: function() {
if (this.color_picker) {
this.editor.on('blur', this._onBlur, this);
this.color_picker.$el.off("mousedown mousemove mouseup");
cdb.god.unbind('closeDialogs', this._destroyPicker, this);
}
},
_destroyPicker: function() {
if (this.color_picker) {
this._unbindPickerEvents();
this.removeView(this.color_picker);
this.color_picker.hide();
delete this.color_picker;
}
},
_onBlur: function(ed) {
this._destroyPicker();
},
_replaceColor: function(color, close) {
var cursor = this.editor.getCursor();
var nameMatch = this._getMatch(cursor, "name");
var hexMatch = this._getMatch(cursor, "hex");
var match = nameMatch ? nameMatch : hexMatch ;
var start = {
line: cursor.line,
ch: match.start
};
var end = {
line: cursor.line,
ch: match.end
};
// Replace the color
this.editor.replaceRange(color, start, end);
// Focus the into the editor and set the cursor
// It will let user to save style
this.editor.focus();
this.editor.setCursor(end);
// Color chosen, let's trigger it!
this.trigger('colorChosen', this);
// Need to close picker?
if (close) this._destroyPicker();
},
_getMatch: function(cursor, type) {
if (!type) return;
var re;
switch (type.toLowerCase()) {
case "name":
re = new RegExp(color_keywords.join('|'),"g");
break;
case "hsl":
re = /hsla?\(\s*(\d{1,3})\s*,\s*(\d{1,3}\%)\s*,\s*(\d{1,3}\%)\s*(?:\s*,\s*(\d+(?:\.\d+)?)\s*)?\)/g;
break;
case "rgb":
re = /rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/;
break;
case "hex":
re = /#[a-fA-F0-9]{3,6}/g;
break;
default:
cdb.log.info("invalid color match selection");
return;
}
var line = this.editor.getLine(cursor.line);
var match = re.exec(line);
while (match) {
var val = match[0];
var len = val.length;
var start = match.index;
var end = match.index + len;
if (cursor.ch >= start && cursor.ch <= end) {
match = null;
return {
start: start,
end: end,
string: val
};
}
match = re.exec(line);
}
return;
},
clean: function() {
this._disableBinds();
cdb.core.View.prototype.clean.call(this);
}
});