# Source: bemap-leaflet/bemap-map-leaflet.js

    /**
     * BeNomad BeMap JavaScript API - Map - Leaflet
     */

    //bemap.require('leaflet.js');

    /**
     * @classdesc
     * Base class for Leaflet.
     * @public
     * @constructor
     * @extends {bemap.Map}
     * @param {bemap.Context} context BeMap-JS-API Context.
     * @param {string} target HTML element.
     * @param options.
     */
    bemap.LeafletMap = function(context, target, options) {
      /**
       * @protected
       */
      this.target = target;

      bemap.Map.call(this, context, options);

      /**
       * @protected
       */
      this.native = L.map(target).setView([0, 0], 2);

    };
    bemap.inherits(bemap.LeafletMap, bemap.Map);

    /**
     * Add a layer to the Leaflet map.
     * @public
     * @param {bemap.Layer} layer
     * @param {object} options
     * @return {bemap.LeafletMap} this
     */
    bemap.LeafletMap.prototype.addLayer = function(layer, options) {
      if (layer !== null && bemap.inheritsof(layer, bemap.Layer)) {
        if (bemap.inheritsof(layer, bemap.BemapLayer)) {
          layer.native = L.tileLayer.wms(this.ctx.getBaseUrl() + 'wms?', {
            appid: this.ctx.login,
            appcode: this.ctx.password,
            minZoom: 2,
            maxZoom: 20,
            'geoserver': layer.geoserver ? layer.geoserver : this.ctx.getGeoserver(),
            'layers': layer.layers ? layer.layers : 'default',
            'styles': layer.styles ? layer.styles : '',
            'tiled': true,
            'TRANSPARENT': layer.transparent === true ? true : false,
            format: layer.format ? layer.format : 'image/png'
          });

        } else if (bemap.inheritsof(layer, bemap.WmsLayer)) {
          layer.native = L.tileLayer.wms(layer.url, {
            minZoom: 2,
            maxZoom: 20,
            'geoserver': layer.geoserver ? layer.geoserver : this.ctx.getGeoserver(),
            'layers': layer.layers ? layer.layers : 'default',
            'styles': layer.styles ? layer.styles : '',
            'tiled': true,
            'TRANSPARENT': layer.transparent === true ? true : false,
            format: layer.format ? layer.format : 'image/png'
          });

        } else if (bemap.inheritsof(layer, bemap.OsmLayer)) {
          console.warn("TODO OsmLayer");

        } else if (bemap.inheritsof(layer, bemap.VectorLayer)) {
          layer.native = L.layerGroup();

        } else if (bemap.inheritsof(layer, bemap.ClusterLayer)) {
          layer.native = new L.MarkerClusterGroup({
            showCoverageOnHover: false
            //Icon Style
            /*iconCreateFunction: function(cluster) {
              return L.divIcon({
                html: cluster.getChildCount(),
                className: 'mycluster',
                iconSize: null
              });

            }*/
          });

        } else {
          console.warn("Unsupport layer");
        }

        if (layer.native !== null) {
          // TODO
          //bemap.OlMap.prototype._addOwnToProperties(layer);

          layer.native.addTo(this.native);
          bemap.Map.prototype.addLayer.call(this, layer);
        }
      }

      if (layer.map === null) {
        layer.map = this;
      }

      return this;
    };

    /**
     * Move map to new coordinate.
     * @public
     * @param {double} lon Longitude in degres decimal (WGS84).
     * @param {double} lat Latitude in degres decimal (WGS84).
     * @param {int} zoom Zoom level (optional).
     * @param {Object} options Options (optional).
     * @param {String} options.animate Enable the animation.
     * @param {String} options.fly Enable the animation (same as animate).
     * @return {bemap.LeafletMap} this
     */
    bemap.LeafletMap.prototype.move = function(lon, lat, zoom, options) {
      if (zoom) {
        this.native.setView([lat, lon], zoom);
      } else {
        this.native.setView([lat, lon]);
      }

      return this;
    };

    /**
     * Set the center and the zoom of the map to fit the bounding box.
     * @param {bemap.BoundingBox} boundingBox the bounding box to fit.
     * @return {bemap.LeafletMap} this.
     */
    bemap.LeafletMap.prototype.moveToBoundingBox = function(boundingBox, options) {
      if (boundingBox && bemap.inheritsof(boundingBox, bemap.BoundingBox) && boundingBox.maxLon && boundingBox.maxLat && boundingBox.minLon && boundingBox.minLat) {
        var ext = L.latLngBounds(
          L.latLng(boundingBox.minLat, boundingBox.minLon),
          L.latLng(boundingBox.maxLat, boundingBox.maxLon)
        );

        if (options && (options.animate || options.fly)) {
          this.native.flyToBounds(ext);
        } else {
          this.native.fitBounds(ext);
        }
      }
      return this;
    };

    /**
     * Zoom on map, set new zoom level.
     * @public
     * @param {int} zoom Zoom level (optional).
     * @param {Object} options Options (optional).
     * @return {bemap.LeafletMap} this
     */
    bemap.LeafletMap.prototype.zoom = function(zoom, options) {
      this.native.setZoom(zoom);
      return this;
    };

    /**
     * Get current zoom level of map.
     * @public
     * @return {int} current zoom level of map.
     */
    bemap.LeafletMap.prototype.getZoom = function() {
      return this.native.getZoom();
    };

    /**
     * Refresh map.
     * @public
     * @abstract
     * @param {Object} options Options (optional).
     * @return {bemap.LeafletMap} this
     */
    bemap.LeafletMap.prototype.refresh = function(options) {
      this.native.invalidateSize();
      return this;
    };

    /**
     * Refresh all objects from a layer.
     * @public
     * @param {bemap.Layer} layer the layer object to refresh.
     * @return {bemap.LeafletMap} this
     */
    bemap.LeafletMap.prototype.refreshLayer = function(layer) {
      if (layer !== null && bemap.inheritsof(layer, bemap.Layer)) {
        layer.native.redraw();
      }
      return this;
    };

    /**
     * Remove all objects from a layer.
     * @public
     * @param {bemap.Layer} layer the layer object to clear.
     * @return {bemap.LeafletMap} this
     */
    bemap.LeafletMap.prototype.clearLayer = function(layer) {
      if (layer !== null && bemap.inheritsof(layer, bemap.Layer)) {
        layer.native.clearLayers();
      }
      return this;
    };

    /**
     * Set the visibility of the layer.
     * @public
     * @param {bemap.Layer} layer the layer of wich to set the visibility.
     * @param {boolean} visible.
     * @return {bemap.LeafletMap} return this.
     */
    bemap.LeafletMap.prototype.visibleLayer = function(layer, visible) {
      if (visible !== null && layer !== null && layer.map !== null && bemap.inheritsof(layer, bemap.Layer)) {
        if (visible) {
          layer.native.addTo(this.native);
        } else {
          layer.native.remove();
        }

        layer.visible = visible;
      }
      return this;
    };

    /**
     * Remove a layer from the map.
     * @param {bemap.Layer} layer layer to remove from the map.
     * @return {bemap.LeafletMap} this.
     */
    bemap.LeafletMap.prototype.removeLayer = function(layer) {
      if (layer !== null && bemap.inheritsof(layer, bemap.Layer)) {
        this.native.removeLayer(layer.native);
        layer.map = null;
      }
      return this;
    };

    /**
     * Set the listner when an specified eventType occur on bemap.LeafletMap.
     * @public
     * @param {bemap.Map.EventType} eventType Event type.
     * @param {function} callback Function will be called when the specified eventType is occur.
     * @param {object} options options.
     * @return {bemap.Listener} listner.
     */
    bemap.LeafletMap.prototype.on = function(eventType, callback, options) {
      var opts = options ? options : {};

      var nativeListener = this.native.on(eventType, function(evt) {
        var mapEvent = new bemap.MapEvent({
          native: evt,
          bemapObject: this,
          x: evt.containerPoint ? evt.containerPoint.x : undefined,
          y: evt.containerPoint ? evt.containerPoint.y : undefined,
          coordinate: evt.latlng ? new bemap.Coordinate(evt.latlng.lng, evt.latlng.lat) : undefined,
          properties: options,
          map: this
        });
        callback(mapEvent);
      });
      var listener = new bemap.Listener({
        native: nativeListener,
        callback: callback,
        key: eventType,
        bemapObject: this
      });
      return listener;
    };

    /**
     * Remove listener.
     * @protected
     * @param {bemap.Listener} listener Function will be called when the specified eventType is occur.
     * @param {object} options Options.
     * @return {bemap.LeafletMap} this.
     */
    bemap.LeafletMap.prototype.removeListener = function(listener, options) {
      if (listener && bemap.inheritsof(listener, bemap.Listener) && listener.bemapObject) {
        this.native.off(listener.native);
      }
      return this;
    };

    /**
     * Get the center of the map in bemap.Coordinate.
     * @return {bemap.Coordinate} the center of the map.
     */
    bemap.LeafletMap.prototype.getCenter = function() {
      var center = this.native.getCenter();
      return new bemap.Coordinate(center.lng, center.lat);
    };

    /**
     * Get the limits of the map on the current zoom.
     * @return {bemap.BoundingBox} the bounding box containing the limits.
     */
    bemap.LeafletMap.prototype.getBoundingBox = function() {
      var bounds = this.native.getBounds();
      var box = new bemap.BoundingBox(bounds.getWest(), bounds.getSouth(), bounds.getEast(), bounds.getNorth());
      return box;
    };