Source: temp/jsdocinputdirs/cercalia.contextmenu.js

/**
 * @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);
};