Ext.apply(
    OpenLayers.Lang.en ,
     {
        remove_layer: "remove layer",
        move_layer_up: "move layer up",
        move_layer_down: "move layer down",
        decrease_opacity:"decrease opacity",
        increase_opacity:"increase opacity",
        opacity:"opacity"
     }
); 

Ext.apply(
    OpenLayers.Lang.fr, 
    {
        remove_layer: "supprimer la couche",
        move_layer_up: "monter",
        move_layer_down: "descendre",
        decrease_opacity:"diminuer l'opacité",
        increase_opacity:"augmenter l'opacité",
        opacity: "opacité"
    }
); 

OpenLayers.Console = {
    error: function(){},
    info: function(){},
    warn: function(){}
};

// this code is to enable/disable tool or toggle type controls :
// see http://trac.openlayers.org/ticket/1553
OpenLayers.Control.prototype.enabled = false;
OpenLayers.Control.prototype.EVENT_TYPES = ["activate", "deactivate", "enable", "disable"];

OpenLayers.Control.prototype.enable = function() {
    if (this.enabled) {
        return false;
    }
    this.enabled = true;
    this.events.triggerEvent("enable");
    return true;
};
    
OpenLayers.Control.prototype.disable = function() {
    if (this.enabled) {
        this.enabled = false;
        this.events.triggerEvent("disable");
        return true;
    }
    return false;
};


OpenLayers.Control.OverviewMap.prototype.destroy = function() {
    if (!this.mapDiv) { // we've already been destroyed
        return;
    }
    this.handlers.click.destroy();

    // I added this because it sometimes caused pbs upon destroyal:
    if (this.extentRectangle) {
        this.mapDiv.removeChild(this.extentRectangle);
        this.extentRectangle = null;
    }
    this.rectEvents.destroy();
    this.rectEvents = null;

    this.ovmap.destroy();
    this.ovmap = null;
        
    this.element.removeChild(this.mapDiv);
    this.mapDiv = null;

    this.div.removeChild(this.element);
    this.element = null;

    if (this.maximizeDiv) {
        OpenLayers.Event.stopObservingElement(this.maximizeDiv);
        this.div.removeChild(this.maximizeDiv);
        this.maximizeDiv = null;
    }
        
    if (this.minimizeDiv) {
        OpenLayers.Event.stopObservingElement(this.minimizeDiv);
        this.div.removeChild(this.minimizeDiv);
        this.minimizeDiv = null;
    }
        
    this.map.events.un({
        "moveend": this.update,
        "changebaselayer": this.baseLayerDraw,
        scope: this
    });

    OpenLayers.Control.prototype.destroy.apply(this, arguments);    
};

// I rewrote this control, so that it triggers events in order to activate/deactivate itself when needed.
OpenLayers.Control.ZoomToMaxExtent = OpenLayers.Class(OpenLayers.Control, {
    /**
     * Property: type
     * TYPE_BUTTON.
     */
    type: OpenLayers.Control.TYPE_BUTTON,
    
    initialize: function (options) {
        // parent
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        this.activate();
    },
    
    /*
     * Method: trigger
     * Do the zoom.
     */
    trigger: function() {
        if (this.map) {
            this.map.zoomToMaxExtent();

            // when successfully triggered, this control must deactivate itself
            this.deactivate();
            
            // once the map has moved again, we must reactivate it ... 
            this.map.events.register('moveend', this, this.activate);
        
            // when this control is activated again, we must deregister the previous listener (if it exists) :
            this.events.register('activate', this, function(){
                this.map.events.unregister('moveend', this, this.activate);
            });
        }    
    },
    
     /**
      * Method: destroy
      * Destroy control.
      */
    destroy: function() {
        if (this.map) {
            this.map.events.unregister('moveend', this, this.activate);
            this.events.unregister('activate', this, function(){
                this.map.events.unregister('moveend', this, this.activate);
            });
        }
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    
    CLASS_NAME: "OpenLayers.Control.ZoomToMaxExtent"
});


OpenLayers.Layer.Vector.prototype.getFeatureByFid = function(fid) {
    var feature = null;
    for (var i=0; i<this.features.length; ++i) {
        if (this.features[i].fid == fid) {
            feature = this.features[i];
            break;
        }
    }
    return feature;
};


// Ajoute une marge d'erreur au calcul de range
OpenLayers.Layer.prototype.calculateInRange = function() {
    var inRange = false;
    if (this.alwaysInRange) {
        inRange = true;
    } else {
        if (this.map) {
            var resolution = this.map.getResolution();
            
            
            inRange = ( (resolution >= this.minResolution - 1) &&
                        (resolution <= this.maxResolution + 1) );
        }
    }
    return inRange;
};

OpenLayers.Format.WKT.prototype.extract.point = function(point) {
    var x = Math.round(point.x);
    var y = Math.round(point.y);
    return x + ' ' + y;
};

OpenLayers.Popup.AutoSizeFramedCloud = OpenLayers.Class(
    OpenLayers.Popup.FramedCloud, {
        'autoSize': true,
        'fixedRelativePosition': true, // in order to be able to clic on the icons ...
        'relativePosition': 'bl', // idem
        'minSize': new OpenLayers.Size(150, 50),
        'maxSize': new OpenLayers.Size(600, 660)
});

OpenLayers.Geometry.Polygon.prototype.getReadableArea = function(format) {
    var a = Math.round(this.getArea());
    if (format){
        var ahtml = (a > 999999) ? Math.round(a/1000000) + ' k' : a ;
        return 'Aire du polygone : ' + ahtml + 'm<sup>2</sup>';
    }
    return a;
};

OpenLayers.Geometry.Polygon.prototype.getReadablePerimeter = function(format) {
    var l = Math.round(this.getLength());
    if (format){
        var lhtml = (l > 9999) ? Math.round(l/1000) + ' km' : l + ' m';
        return 'Longueur de la ligne : '+ lhtml;
    }
    return l;
};

OpenLayers.Geometry.LineString.prototype.getReadableLength = function(format) {
    var l = Math.round(this.getLength());
    if (format){
        var lhtml = (l > 9999) ? Math.round(l/1000) + ' km' : l + ' m';
        return 'Longueur de la ligne : '+ lhtml;
    }
    return l;
};

OpenLayers.Feature.Vector.prototype.getType = function() {
    var type = this.geometry.CLASS_NAME.split('.')[2];
    switch (type)
    {
        case 'Point':
            return 'Point';
        case 'LineString':
            return 'Line';
        case 'Polygon':
            return 'Polygon';
    }
    return null;
};

OpenLayers.Feature.Vector.prototype.measureFeature = function(full){
    var type = this.geometry.CLASS_NAME.split('.')[2];
    var html = '';
    switch (type)
    {
        case 'LineString':
                html = this.geometry.getReadableLength(true);
                break;
        case 'Polygon':
                html = this.geometry.getReadableArea(true);
                if (full) {
                    html += '<br/>Perimetre : ' + this.geometry.getReadablePerimeter(true);
                }
                break;
        default:
                break;
    }
    return html;
};

OpenLayers.Map.prototype.zoomToBB = function(options) {
    this.zoomToExtent(
        new OpenLayers.Bounds(
            options.left, 
            options.bottom, 
            options.right, 
            options.top)
    );
};

// We create an OL control to activate/deactivate link with table or infobox popup :
OpenLayers.Control.InfoTool = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_TOGGLE,
    selectControl: null,
    layer: null,
    activatedFunction: function() {},
    deactivatedFunction: function() {},
    
    initialize: function (options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
        
        this.selectControl = new OpenLayers.Control.SelectFeature(
            this.layer, {
                multiple: false,
                title: 'SelectFeatureInfoTool',
                callbacks: {
                    'click': OpenLayers.Function.bind(this.click, this)
                }
            }
        );
        this.layer.map.addControl(this.selectControl);
        this.selectControl.activate();
        
        this.title = Bdn.lang.InfotoolTooltip; //"Outil d'information sur la station";
    },
    
    click: function(feature) {
        if (this.active) {
            this.activatedFunction(feature);
        } else { 
            this.deactivatedFunction(feature);
        }
    },
    
    destroy: function() {
        this.layer = null;
        this.selectControl.destroy();
        OpenLayers.Control.prototype.destroy.apply(this, []);
    },
    
    CLASS_NAME: "OpenLayers.Control.InfoTool"
});

/* 
 * Loading indicator ... this code from bartvde should become an addin 
 * I slightly modified it to display a "waiting" mouse cursor
 * See http://trac.openlayers.org/ticket/102
 */
OpenLayers.Control.LoadingPanel = OpenLayers.Class(OpenLayers.Control, {
     /**
      * Property: counter
      * {Integer} A counter for the number of layers loading.
      */
    counter: 0,

     /**
      * Property: maximized
      * {Boolean} A boolean indicating whether or not the control is maximized
     */
    maximized: false,

     /**
      * Property: layer
      * {OpenLayers.Layer}
     */
    layer: null,

     /**
      * Constructor: OpenLayers.Control.LoadingPanel
      * Display a panel across the map that says 'loading'.
      *
      * Parameters:
      * layer - {<OpenLayers.Layer.Grid>} Some layer which implements the loadstart and loadend
      *    properties. At the moment, this is only subclasses of the Grid layer.
      * options - {Object} additional options.
      */
    initialize: function(layer, options) {
        OpenLayers.Control.prototype.initialize.apply(this, [options]);

        if (!layer) {
            this.map.events.register('addlayer', this, this.addLayer);
            for (var i = 0; i < this.map.layers.length; i++) {
                var layer = this.map.layers[i];
                layer.events.register('loadstart', this, this.increaseCounter);
                layer.events.register('loadend', this, this.decreaseCounter);
            }
        } else {
            this.layer = layer;
            layer.events.register('loadstart', this, this.maximizeControl);
            layer.events.register('loadend', this, this.minimizeControl);
        }
    },

     /**
      * Method: addLayer
      * Attach event handlers when new layer gets added to the map
     */
    addLayer: function(evt) {
        var layer = this.map.layers[this.map.layers.length-1];
        layer.events.register('loadstart', this, this.increaseCounter);
        layer.events.register('loadend', this, this.decreaseCounter);
    },

     /**
      * Method: increaseCounter
      * Increase the counter and show control
     */
    increaseCounter: function() {
        this.counter++;
        if (this.counter > 0) {
            if (!this.maximized) {
                this.maximizeControl();
            }
        }
    },

     /**
      * Method: decreaseCounter
      * Decrease the counter and hide the control if finished
     */
    decreaseCounter: function() {
        if (this.counter > 0) {
            this.counter--;
        }
        if (this.counter === 0) {
            if (this.maximized) {
                this.minimizeControl();
            }
        }
    },

     /**
      * Method: draw
      * Create and return the element to be splashed over the map.
      */
    draw: function () {
        return null;
    },

     /**
      * Method: minimizeControl
      * Set the display properties of the control to make it disappear.
      */
    minimizeControl: function(e) {
        this.maximized = false;
        document.body.style.cursor = 'auto';
        if (e != null) {
            OpenLayers.Event.stop(e);
        }
    },

     /**
      * Method: maximizeControl
      * Make the control visible.
      */
    maximizeControl: function(e) {
        document.body.style.cursor = 'wait';
        this.maximized = true;
        if (e != null) {
            OpenLayers.Event.stop(e);
        }
    },

     /**
      * Method: destroy
      * Destroy control.
      */
    destroy: function() {
        if (this.layer) {
            this.layer.events.unregister('loadstart', this, this.maximizeControl);
            this.layer.events.unregister('loadend', this, this.minimizeControl);
        } else {
            if (this.map) {
                this.map.events.unregister('addlayer', this, this.addLayer);
                if (this.map.layers) {
                    for (var i = 0; i < this.map.layers.length; i++) {
                        var layer = this.map.layers[i];
                        layer.events.unregister('loadstart', this, this.increaseCounter);
                        layer.events.unregister('loadend', this, this.decreaseCounter);
                    }
                }
            }
        }
        OpenLayers.Control.prototype.destroy.apply(this, arguments);
    },
    CLASS_NAME: "OpenLayers.Control.LoadingPanel"
});

// We create a new OL Control in order to zoom to a vector layer's features:
OpenLayers.Control.ZoomToFeatures = OpenLayers.Class(OpenLayers.Control, {
    type: OpenLayers.Control.TYPE_BUTTON,
    
    initialize: function (options) {
        // parent
        OpenLayers.Control.prototype.initialize.apply(this, [options]);
    
        if (this.map && this.layer && this.layer.features && this.layer.features.length) {
            this.activate();
        } else {
            this.deactivate();
        }
    
        // register an event on the vector layer it is attached to : if features inside, enable it and make this enable its button... (button state must listen to activate / deactivate events from the control)
        this.layer.events.register('featuresadded', this, function(){
            this.activate(); // Here, I'm using activate/deactivate for button-type controls because they play the same role as enable/disable (see ticket http://trac.openlayers.org/ticket/1553)
        });
        // and, by reciprocity : 
        this.layer.events.register('featuresremoved', this, function(){
            if (this.layer && this.layer.features && !this.layer.features.length && this.events) { // this.events to prevent bug on destroy
                this.deactivate();
            }
        });
    },
    
    trigger: function() {
        if (!this.maxZoomLevel){
            this.maxZoomLevel = this.map.scales.length-1;
        }
        if (this.map && this.layer && this.layer.features && this.layer.features.length) {
            var features = this.layer.features;
            var bounds = features[0].geometry.getBounds();
            for (var i=1; i<features.length; i++) {
                bounds.extend(features[i].geometry.getBounds());
            }
            if ((bounds.getWidth() === 0) && (bounds.getHeight() === 0)){
                this.map.setCenter(bounds.getCenterLonLat(), this.maxZoomLevel);
            } else {
                this.map.zoomToExtent(bounds);
            }
                        
            // when successfully triggered, this control must deactivate itself
            this.deactivate();
            // once the map has moved again, we must reactivate it ... 
            this.map.events.register('moveend', this, function(){
                if (this.layer.features && this.layer.features.length) {
                    this.activate();
                }
            });
        }
    },
    
    destroy: function() {
        this.layer.events.un({
            "featuresadded": function(){this.activate();},
            "featuresremoved": function(){if (this.layer && this.layer.features && !this.layer.features.length && this.events){this.deactivate();}},
            scope: this
        });
        this.map.events.un({
            "moveend": function(){
                if (this.layer.features && this.layer.features.length) {
                    this.activate();
                }
            },
            scope: this
        });
        this.layer = null;
        OpenLayers.Control.prototype.destroy.apply(this, []);
    },
    
    CLASS_NAME: "OpenLayers.Control.ZoomToFeatures"
});


// begin bugfix http://trac.openlayers.org/ticket/1562
    OpenLayers.Control.ModifyFeature.prototype.deactivate = function() {
        var deactivated = false;
        // the return from the controls is unimportant in this case
        if(OpenLayers.Control.prototype.deactivate.apply(this, arguments)) {
            this.layer.removeFeatures(this.vertices, {silent: true});
            this.layer.removeFeatures(this.virtualVertices, {silent: true});
            this.vertices = [];
            this.dragControl.deactivate();
            if(this.feature && this.feature.geometry) {
                this.selectControl.unselect.apply(this.selectControl,
                                                  [this.feature]);
            }
            this.selectControl.deactivate();
            this.handlers.keyboard.deactivate();
            deactivated = true;
        }
        return deactivated;
    };
    
    OpenLayers.Control.ModifyFeature.prototype.unselectFeature = function(object) {
        this.layer.removeFeatures(this.vertices, {silent: true});
        this.vertices = [];
        this.layer.destroyFeatures(this.virtualVertices, {silent: true});
        this.virtualVertices = [];
        if(this.dragHandle) {
            this.layer.destroyFeatures([this.dragHandle], {silent: true});
            delete this.dragHandle;
        }
        if(this.radiusHandle) {
            this.layer.destroyFeatures([this.radiusHandle], {silent: true});
            delete this.radiusHandle;
        }
        this.feature = null;
        this.dragControl.deactivate();
        this.onModificationEnd(object.feature);
        this.layer.events.triggerEvent("afterfeaturemodified", 
                                       {feature: object.feature});
    };
    
    OpenLayers.Control.ModifyFeature.prototype.dragVertex = function(vertex) {
        /**
         * Five cases:
         * 1) dragging a simple point
         * 2) dragging a virtual vertex
         * 3) dragging a drag handle
         * 4) dragging a radius handle
         * 5) dragging a real vertex
         */
        if(this.feature.geometry.CLASS_NAME == "OpenLayers.Geometry.Point") {
            // dragging a simple point
            if(this.feature != vertex) {
                this.feature = vertex;
            }
        } else {
            if(vertex._index) {
                // dragging a virtual vertex
                vertex.geometry.parent.addComponent(vertex.geometry,
                                                    vertex._index);
                // move from virtual to real vertex
                delete vertex._index;
                OpenLayers.Util.removeItem(this.virtualVertices, vertex);
                this.vertices.push(vertex);
            } else if(vertex == this.dragHandle) {
                // dragging a drag handle
                this.layer.removeFeatures(this.vertices, {silent: true});
                this.vertices = [];
                if(this.radiusHandle) {
                    this.layer.destroyFeatures([this.radiusHandle], {silent: true});
                    this.radiusHandle = null;
                }
            }
            // dragging a radius handle - no special treatment
            // dragging a real vertex - no special treatment
            if(this.virtualVertices.length > 0) {
                this.layer.destroyFeatures(this.virtualVertices, {silent: true});
                this.virtualVertices = [];
            }
            this.layer.drawFeature(this.feature, this.selectControl.renderIntent);
        }
        // keep the vertex on top so it gets the mouseout after dragging
        // this should be removed in favor of an option to draw under or
        // maintain node z-index
        this.layer.drawFeature(vertex);
    };
    
    
    OpenLayers.Control.ModifyFeature.prototype.resetVertices = function() {
        // if coming from a drag complete we're about to destroy the vertex
        // that was just dragged. For that reason, the drag feature control
        // will never detect a mouse-out on that vertex, meaning that the drag
        // handler won't be deactivated. This can cause errors because the drag
        // feature control still has a feature to drag but that feature is
        // destroyed. To prevent this, we call outFeature on the drag feature
        // control if the control actually has a feature to drag.
        if(this.dragControl.feature) {
            this.dragControl.outFeature(this.dragControl.feature);
        }
        if(this.vertices.length > 0) {
            this.layer.removeFeatures(this.vertices, {silent: true});
            this.vertices = [];
        }
        if(this.virtualVertices.length > 0) {
            this.layer.removeFeatures(this.virtualVertices, {silent: true});
            this.virtualVertices = [];
        }
        if(this.dragHandle) {
            this.layer.destroyFeatures([this.dragHandle], {silent: true});
            this.dragHandle = null;
        }
        if(this.radiusHandle) {
            this.layer.destroyFeatures([this.radiusHandle], {silent: true});
            this.radiusHandle = null;
        }
        if(this.feature &&
           this.feature.geometry.CLASS_NAME != "OpenLayers.Geometry.Point") {
            if((this.mode & OpenLayers.Control.ModifyFeature.DRAG)) {
                this.collectDragHandle();
            }
            if((this.mode & (OpenLayers.Control.ModifyFeature.ROTATE |
                             OpenLayers.Control.ModifyFeature.RESIZE))) {
                this.collectRadiusHandle();
            }
            if((this.mode & OpenLayers.Control.ModifyFeature.RESHAPE)) {
                this.collectVertices();
            }
        }
    };
    
    
    OpenLayers.Layer.Vector.prototype.removeFeatures = function(features, options) {
        if (!(features instanceof Array)) {
            features = [features];
        }
        if (features.length <= 0) {
            return;
        }

        var notify = !options || !options.silent;

        for (var i = features.length - 1; i >= 0; i--) {
            var feature = features[i];

            if (notify) {
                this.events.triggerEvent("beforefeatureremoved", {
                    feature: feature
                });
            }

            this.features = OpenLayers.Util.removeItem(this.features, feature);
            // feature has no layer at this point
            feature.layer = null;

            if (feature.geometry) {
                this.renderer.eraseGeometry(feature.geometry);
            }
                    
            //in the case that this feature is one of the selected features, 
            // remove it from that array as well.
            if (OpenLayers.Util.indexOf(this.selectedFeatures, feature) != -1){
                OpenLayers.Util.removeItem(this.selectedFeatures, feature);
            }

            if (notify) {
                this.events.triggerEvent("featureremoved", {
                    feature: feature
                });
            }
        }

        if (notify) {
            this.events.triggerEvent("featuresremoved", {features: features});
        }
    };
    
    
    OpenLayers.Layer.Vector.prototype.destroyFeatures = function(features, options) {
        var all = (features == undefined); // evaluates to true if
                                           // features is null
        if(all) {
            features = this.features;
        }
        this.removeFeatures(features, options);
        for (var i = 0; i < features.length; i++) {
            features[i].destroy();
        }
    };
// end bugfix http://trac.openlayers.org/ticket/1562