/**
* @classdesc
* Class for creating a context menu. This control permits: <br/>
* - Draw a menu in a LatLng position.<br/>
* - Add / Remove options in menu.<br/>
* - Enable / Disable options of the menu.<br/>
* - Show / Hide options of the menu.
* @class
* @constructor
* @param {cercaliax.ContextMenuOptions} opt_options
*/
cercalia.ContextMenu = function(opt_options) {
/**
* Nombre de la classe
* @private
* @type {string}
*/
this.CLASS_NAME_ = "cercalia.ContextMenu";
var options = opt_options || {};
/* Variables opcions */
/**
* @private
* @type {string}
*/
this.id_ = options.id ? options.id : ("contextMenu_" + Math.random().toString(36).substring(7));
/**
* @private
* @type {cercalia.Map}
*/
this.map_ = options.map ? options.map : null;
/**
* @private
* @type {cercalia.LonLat}
*/
this.fixedPosition_ = options.fixedPosition ? options.fixedPosition : null;
/* Variables privadas */
/**
* @private
* @type {ol.Overlay}
*/
this.contextMenuOverlay_ = null;
/**
* @private
* @type {Array.<Object>}
*/
this.optionsList_ = [];
/**
* @private
* @type {Boolean}
*/
this.visible_ = false;
// initialize
this.initialize_();
};
/**
* Initalize the context menu.
* @private
*/
cercalia.ContextMenu.prototype.initialize_ = function() {
this.contextMenuOverlay_ = new ol.Overlay({
element: this.drawMenu_()
});
this.contextMenuOverlay_.id = this.id_;
// Set map.
var firstMap = this.map_;
this.map_ = null;
if (firstMap != null) this.setMap(firstMap);
};
/**
* Returns true if the context menu is visible.
* @returns {Boolean}
*/
cercalia.ContextMenu.prototype.isVisible = function() {
return this.visible_;
};
/**
* Add a new option in the context menu at the end or in a specific position. Returns options identifier.
* If the id options exist removes previous option.
* @param {string} id Option's identifier.
* @param {string} name Text will be displayed in option.
* @param {Function} callback Function will be fired when users click the option.
* @param {number} position The position where option will be added.
* @returns {string} Options identifier.
*/
cercalia.ContextMenu.prototype.addOption = function(id, name, callback, position) {
if (!id) {
id = "contextmenuOption_" + name.replace(/\s/g, "").toLowerCase();
}
if(this.existOption(id)) {
this.removeOption(id);
}
var option = {
"id": id,
"title": name,
"callback": callback
};
if (position) {
this.optionsList_.splice(position, 0, option);
} else {
this.optionsList_.push(option);
}
this.redraw();
return id;
};
/**
* Close the menu.
*/
cercalia.ContextMenu.prototype.close = function() {
cercalia.jQuery('#' + this.id_).fadeOut();
this.visible_ = false;
};
/**
* Returns true if the option exists.
* @param {string} id Option's identifier.
* @returns {Boolean} True if exists.
*/
cercalia.ContextMenu.prototype.existOption = function(id) {
return this.findPositionById_(id) != -1;
};
/**
* Returns the option if exists.
* @param {string} id Option's identifier.
* @returns {Object} The option or null if not exist.
*/
cercalia.ContextMenu.prototype.findOptionById = function(id) {
var position = this.findPositionById_(id);
if(position == -1) {
return null;
} else {
return this.optionsList_[position];
}
};
/**
* Open the menu. If menu isn't position fixed needs a position to be displayed.
* @param {Array.<number>|cercalia.LonLat|undefined} coordinates If menu isn't position fixed, menu will be displayed in ths position.
* @param {string|undefined} projection If menu isn't position fixed, and coordinates are Array of numbers, the projection will be necesary to transform the position.
*/
cercalia.ContextMenu.prototype.open = function(coordinates, projection) {
if (this.map_) {
var latLon;
if (!this.fixedPosition_) {
if (!coordinates) {
console.error("No fixed context menu. Needs a cercalia.LonLat and its projection.");
return;
} else {
if(coordinates instanceof Array) {
if (!projection) projection = 'EPSG:4326';
var latLonCoords = ol.proj.transform(coordinates, projection, 'EPSG:4326');
latLon = new cercalia.LonLat(latLonCoords[0], latLonCoords[1], 'EPSG:4326');
} else if(coordinates.getClass && coordinates.getClass() == "cercalia.LonLat") {
latLon = coordinates;
coordinates = ol.proj.transform([coordinates.getLon(), coordinates.getLat()], 'EPSG:4326', this.map_.getProjectionCode());
} else {
console.error("Coordinates format not supported.");
return;
}
}
} else {
coordinates = ol.proj.transform([this.fixedPosition_.getLon(), this.fixedPosition_.getLat()], 'EPSG:4326', this.map_.getProjectionCode());
latLon = this.fixedPosition_;
}
this.contextMenuOverlay_.setPosition(coordinates);
cercalia.jQuery('#' + this.id_).data('lonLat', latLon).fadeIn();
this.visible_ = true;
} else {
alert("ContextMenu: map not defined.");
}
};
/**
* Redraws the context menu.
*/
cercalia.ContextMenu.prototype.redraw = function() {
this.contextMenuOverlay_.setElement(this.drawMenu_());
};
/**
* Remove an option from the context menu and redraw it.
* @param {string} id Option's identifier.
*/
cercalia.ContextMenu.prototype.removeOption = function(id) {
var position = this.findPositionById_(id);
if(position != -1) {
this.optionsList_.splice(position, 1);
this.redraw();
}
};
/**
* Set map of context menu
* @param {cercalia.Map} map New map.
*/
cercalia.ContextMenu.prototype.setMap = function(map) {
if (this.map_ != null) {
cercalia.jQuery(this.map_.getMap().getViewport()).unbind('contextmenu');
this.map_.getMap().removeOverlay(this.contextMenuOverlay_);
}
this.map_ = map;
this.map_.getMap().addOverlay(this.contextMenuOverlay_);
};
/**
* Build the HTML code of menu.
* @private
* @returns {string}
*/
cercalia.ContextMenu.prototype.drawMenu_ = function() {
var self = this;
var contextMenuHTML = cercalia.jQuery("<ul />")
.attr('id', this.id_)
.addClass('cercalia-contextmenu ui-corner-all ui-widget ui-state-default');
for ( var index in this.optionsList_) {
var option = this.optionsList_[index];
var optionHTML = cercalia.jQuery("<li data-action='" + option.id + "' class='" + option.id + "' />")
.addClass("cercalia-box-sizing")
.html(cercalia.i18n._(option.title))
.button();
if (option.callback) {
optionHTML.click({ callback: option.callback }, function(evt) {
evt.data.callback(evt, cercalia.jQuery('#' + self.id_).data('lonLat'));
self.close();
});
}
if (this.isFirstNode_(index) && this.isLastNode_(index)) {
optionHTML.addClass("cercalia-contextmenu-last ui-no-corner-all");
} else if (this.isFirstNode_(index)) {
optionHTML.addClass("ui-corner-top ui-no-corner-bottom");
} else if (this.isLastNode_(index)) {
optionHTML.addClass("cercalia-contextmenu-last ui-corner-bottom ui-no-corner-top");
} else {
optionHTML.addClass("ui-no-corner-all");
}
contextMenuHTML.append(optionHTML);
}
return contextMenuHTML.get();
};
/**
* Returns the position of an option
* @private
* @param {string} id Option's identifier.
* @returns {number} The position of the option or -1 if not exist.
*/
cercalia.ContextMenu.prototype.findPositionById_ = function(id) {
for(var index in this.optionsList_) {
if(this.optionsList_[index].id == id) {
return index;
}
}
return -1;
};
/**
* Check if the option is the first option in the menu.
* @private
* @param {number} index Position of the option.
* @returns {Boolean}
*/
cercalia.ContextMenu.prototype.isFirstNode_ = function(index) {
return index == 0;
};
/**
* Check if the option is the last option in the menu.
* @private
* @param {number} index Position of the option.
* @returns {Boolean}
*/
cercalia.ContextMenu.prototype.isLastNode_ = function(index) {
return index == this.optionsList_ - 1;
};
/**
* Check if the option is the middle option in the menu.
* @private
* @param {number} index Position of the option.
* @returns {Boolean}
*/
cercalia.ContextMenu.prototype.isMiddleNode_ = function(index) {
return !this.isFirstNode_(index) && !this.isLastNode_(index);
};