[OpenLayers-Commits] r1764 - in sandbox/bertil: . lib lib/OpenLayers lib/OpenLayers/Control lib/OpenLayers/Control/Edition lib/OpenLayers/Feature lib/OpenLayers/Feature/Geometry lib/OpenLayers/Layer lib/OpenLayers/Layer/WMS lib/OpenLayers/Marker lib/OpenLayers/Popup lib/OpenLayers/Renderer lib/OpenLayers/Tile lib/Rico

commits at openlayers.org commits at openlayers.org
Mon Nov 6 08:32:49 EST 2006


Author: bertil
Date: 2006-11-06 08:32:47 -0500 (Mon, 06 Nov 2006)
New Revision: 1764

Added:
   sandbox/bertil/lib/
   sandbox/bertil/lib/OpenLayers.js
   sandbox/bertil/lib/OpenLayers/
   sandbox/bertil/lib/OpenLayers/Ajax.js
   sandbox/bertil/lib/OpenLayers/BaseTypes.js
   sandbox/bertil/lib/OpenLayers/Control.js
   sandbox/bertil/lib/OpenLayers/Control/
   sandbox/bertil/lib/OpenLayers/Control/ArgParser.js
   sandbox/bertil/lib/OpenLayers/Control/Edition.js
   sandbox/bertil/lib/OpenLayers/Control/Edition/
   sandbox/bertil/lib/OpenLayers/Control/Edition/DrawCurvePath.js
   sandbox/bertil/lib/OpenLayers/Control/Edition/DrawLinearPath.js
   sandbox/bertil/lib/OpenLayers/Control/Edition/Mode.js
   sandbox/bertil/lib/OpenLayers/Control/Edition/Selection.js
   sandbox/bertil/lib/OpenLayers/Control/EditionToolbar.js
   sandbox/bertil/lib/OpenLayers/Control/KeyboardDefaults.js
   sandbox/bertil/lib/OpenLayers/Control/LayerSwitcher.js
   sandbox/bertil/lib/OpenLayers/Control/MouseDefaults.js
   sandbox/bertil/lib/OpenLayers/Control/MousePosition.js
   sandbox/bertil/lib/OpenLayers/Control/MouseToolbar.js
   sandbox/bertil/lib/OpenLayers/Control/OverviewMap.js
   sandbox/bertil/lib/OpenLayers/Control/PanZoom.js
   sandbox/bertil/lib/OpenLayers/Control/PanZoomBar.js
   sandbox/bertil/lib/OpenLayers/Control/Permalink.js
   sandbox/bertil/lib/OpenLayers/Control/Scale.js
   sandbox/bertil/lib/OpenLayers/Control/snapping.backup
   sandbox/bertil/lib/OpenLayers/Events.js
   sandbox/bertil/lib/OpenLayers/Feature.js
   sandbox/bertil/lib/OpenLayers/Feature/
   sandbox/bertil/lib/OpenLayers/Feature/Geometry.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/Component.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/CubicBezierCurve.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/CubicBezierSurface.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/LineSegment.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/LineString.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/LinearRing.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/Point.js
   sandbox/bertil/lib/OpenLayers/Feature/Geometry/Polygon.js
   sandbox/bertil/lib/OpenLayers/Feature/WFS.js
   sandbox/bertil/lib/OpenLayers/Geometry.js
   sandbox/bertil/lib/OpenLayers/Icon.js
   sandbox/bertil/lib/OpenLayers/Layer.js
   sandbox/bertil/lib/OpenLayers/Layer/
   sandbox/bertil/lib/OpenLayers/Layer/Boxes.js
   sandbox/bertil/lib/OpenLayers/Layer/Canvas.js
   sandbox/bertil/lib/OpenLayers/Layer/EventPane.js
   sandbox/bertil/lib/OpenLayers/Layer/FixedZoomLevels.js
   sandbox/bertil/lib/OpenLayers/Layer/GeoRSS.js
   sandbox/bertil/lib/OpenLayers/Layer/Google.js
   sandbox/bertil/lib/OpenLayers/Layer/Grid.js
   sandbox/bertil/lib/OpenLayers/Layer/HTTPRequest.js
   sandbox/bertil/lib/OpenLayers/Layer/Image.js
   sandbox/bertil/lib/OpenLayers/Layer/KaMap.js
   sandbox/bertil/lib/OpenLayers/Layer/MapServer.js
   sandbox/bertil/lib/OpenLayers/Layer/Markers.js
   sandbox/bertil/lib/OpenLayers/Layer/MultiMap.js
   sandbox/bertil/lib/OpenLayers/Layer/Text.js
   sandbox/bertil/lib/OpenLayers/Layer/Vector.js
   sandbox/bertil/lib/OpenLayers/Layer/VirtualEarth.js
   sandbox/bertil/lib/OpenLayers/Layer/WFS.js
   sandbox/bertil/lib/OpenLayers/Layer/WMS.js
   sandbox/bertil/lib/OpenLayers/Layer/WMS/
   sandbox/bertil/lib/OpenLayers/Layer/WMS/Untiled.js
   sandbox/bertil/lib/OpenLayers/Layer/WorldWind.js
   sandbox/bertil/lib/OpenLayers/Layer/Yahoo.js
   sandbox/bertil/lib/OpenLayers/Map.js
   sandbox/bertil/lib/OpenLayers/Marker.js
   sandbox/bertil/lib/OpenLayers/Marker/
   sandbox/bertil/lib/OpenLayers/Marker/Box.js
   sandbox/bertil/lib/OpenLayers/Popup.js
   sandbox/bertil/lib/OpenLayers/Popup/
   sandbox/bertil/lib/OpenLayers/Popup/Anchored.js
   sandbox/bertil/lib/OpenLayers/Popup/AnchoredBubble.js
   sandbox/bertil/lib/OpenLayers/Projection.js
   sandbox/bertil/lib/OpenLayers/Renderer.js
   sandbox/bertil/lib/OpenLayers/Renderer/
   sandbox/bertil/lib/OpenLayers/Renderer/Svg.js
   sandbox/bertil/lib/OpenLayers/Renderer/Vml.js
   sandbox/bertil/lib/OpenLayers/SingleFile.js
   sandbox/bertil/lib/OpenLayers/Style.js
   sandbox/bertil/lib/OpenLayers/Tile.js
   sandbox/bertil/lib/OpenLayers/Tile/
   sandbox/bertil/lib/OpenLayers/Tile/Image.js
   sandbox/bertil/lib/OpenLayers/Tile/WFS.js
   sandbox/bertil/lib/OpenLayers/Util.js
   sandbox/bertil/lib/Rico/
   sandbox/bertil/lib/Rico/Color.js
   sandbox/bertil/lib/Rico/Corner.js
   sandbox/bertil/lib/prototype.js
Log:
new lib

Added: sandbox/bertil/lib/OpenLayers/Ajax.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Ajax.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Ajax.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,308 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+OpenLayers.ProxyHost = "";
+//OpenLayers.ProxyHost = "examples/proxy.cgi?url=";
+
+/**
+* Ajax reader for OpenLayers
+*
+*@uri url to do remote XML http get
+*@param 'get' format params (x=y&a=b...)
+*@who object to handle callbacks for this request
+*@complete  the function to be called on success 
+*@failure  the function to be called on failure
+*
+* example usage from a caller:
+*
+*   caps: function(request) {
+*    -blah-  
+*   },
+*
+*   OpenLayers.loadURL(url,params,this,caps);
+*
+* Notice the above example does not provide an error handler; a default empty
+* handler is provided which merely logs the error if a failure handler is not 
+* supplied
+*
+*/
+
+
+/** 
+* @param {} request
+*/
+OpenLayers.nullHandler = function(request) {
+    alert("Unhandled request return " + request.statusText);
+};
+
+/** Background load a document
+*
+* @param {String} uri URI of source doc
+* @param {String} params Params on get (doesnt seem to work)
+* @param {Object} caller object which gets callbacks
+* @param {Function} onComplete callback for success
+* @param {Function} onFailure callback for failure
+*
+* Both callbacks optional (though silly)
+*/
+OpenLayers.loadURL = function(uri, params, caller,
+                                  onComplete, onFailure) {
+
+    if (OpenLayers.ProxyHost && uri.startsWith("http")) {
+        uri = OpenLayers.ProxyHost + escape(uri);
+    }
+
+    var success = (onComplete) ? onComplete.bind(caller)
+                                : OpenLayers.nullHandler;
+
+    var failure = (onFailure) ? onFailure.bind(caller)
+                           : OpenLayers.nullHandler;
+
+    // from prototype.js
+    new OpenLayers.Ajax.Request(uri, 
+                     {   method: 'get', 
+                         parameters: params,
+                         onComplete: success, 
+                         onFailure: failure
+                      }
+                     );
+};
+
+/** Parse XML into a doc structure
+* @param {String} text
+*
+* @returns Parsed Ajax Response ??
+* @type ?
+*/
+OpenLayers.parseXMLString = function(text) {
+
+    //MS sucks, if the server is bad it dies
+    var index = text.indexOf('<');
+    if (index > 0) {
+        text = text.substring(index);
+    }
+
+    var ajaxResponse = OpenLayers.Util.Try(
+        function() {
+            var xmldom = new ActiveXObject('Microsoft.XMLDOM');
+            xmldom.loadXML(text);
+            return xmldom;
+        },
+        function() {
+            return new DOMParser().parseFromString(text, 'text/xml');
+        },
+        function() {
+            var req = new XMLHttpRequest();
+            req.open("GET", "data:" + "text/xml" +
+                     ";charset=utf-8," + encodeURIComponent(text), false);
+            if (req.overrideMimeType) {
+                req.overrideMimeType("text/xml");
+            }
+            req.send(null);
+            return req.responseXML;
+        }
+    );
+
+    return ajaxResponse;
+};
+
+OpenLayers.Ajax = {
+  emptyFunction: function () {},
+
+  getTransport: function() {
+    return OpenLayers.Util.Try(
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+      function() {return new XMLHttpRequest()}
+    ) || false;
+  },
+
+  activeRequestCount: 0
+};
+
+OpenLayers.Ajax.Responders = {
+  responders: [],
+
+  register: function(responderToAdd) {
+    for (var i = 0; i < this.responders.length; i++)
+        if (responderToAdd == this.responders[i])
+            return;
+    this.responders.push(responderToAdd);
+  },
+
+  dispatch: function(callback, request, transport, json) {
+    for (var i = 0; i < this.responders.length; i++) {
+      responder = this.responders[i];
+      if (responder[callback] && typeof responder[callback] == 'function') {
+        try {
+          responder[callback].apply(responder, [request, transport, json]);
+        } catch (e) {}
+      }
+    }
+  }
+};
+
+OpenLayers.Ajax.Responders.register({
+  onCreate: function() {
+    OpenLayers.Ajax.activeRequestCount++;
+  },
+
+  onComplete: function() {
+    OpenLayers.Ajax.activeRequestCount--;
+  }
+});
+
+OpenLayers.Ajax.Base = function() {};
+OpenLayers.Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      parameters:   ''
+    }
+    OpenLayers.Util.extend(this.options, options || {});
+  },
+
+  responseIsSuccess: function() {
+    return this.transport.status == undefined
+        || this.transport.status == 0
+        || (this.transport.status >= 200 && this.transport.status < 300);
+  },
+
+  responseIsFailure: function() {
+    return !this.responseIsSuccess();
+  }
+}
+
+OpenLayers.Ajax.Request = OpenLayers.Class.create();
+OpenLayers.Ajax.Request.Events =
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+OpenLayers.Ajax.Request.prototype = OpenLayers.Class.inherit( OpenLayers.Ajax.Base, {
+  initialize: function(url, options) {
+    this.transport = OpenLayers.Ajax.getTransport();
+    this.setOptions(options);
+    this.request(url);
+  },
+
+  request: function(url) {
+    var parameters = this.options.parameters || '';
+    if (parameters.length > 0) parameters += '&_=';
+
+    try {
+      this.url = url;
+      if (this.options.method == 'get' && parameters.length > 0)
+        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
+
+      OpenLayers.Ajax.Responders.dispatch('onCreate', this, this.transport);
+
+      this.transport.open(this.options.method, this.url,
+        this.options.asynchronous);
+
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+
+      this.setRequestHeaders();
+
+      var body = this.options.postBody ? this.options.postBody : parameters;
+      this.transport.send(this.options.method == 'post' ? body : null);
+
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  setRequestHeaders: function() {
+    var requestHeaders =
+      ['X-Requested-With', 'XMLHttpRequest',
+       'X-Prototype-Version', 'OpenLayers'];
+
+    if (this.options.method == 'post') {
+      requestHeaders.push('Content-type',
+        'application/x-www-form-urlencoded');
+
+      /* Force "Connection: close" for Mozilla browsers to work around
+       * a bug where XMLHttpReqeuest sends an incorrect Content-length
+       * header. See Mozilla Bugzilla #246651.
+       */
+      if (this.transport.overrideMimeType)
+        requestHeaders.push('Connection', 'close');
+    }
+
+    if (this.options.requestHeaders)
+      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
+
+    for (var i = 0; i < requestHeaders.length; i += 2)
+      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
+  },
+
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+
+  header: function(name) {
+    try {
+      return this.transport.getResponseHeader(name);
+    } catch (e) {}
+  },
+
+  evalJSON: function() {
+    try {
+      return eval(this.header('X-JSON'));
+    } catch (e) {}
+  },
+
+  evalResponse: function() {
+    try {
+      return eval(this.transport.responseText);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+  },
+
+  respondToReadyState: function(readyState) {
+    var event = OpenLayers.Ajax.Request.Events[readyState];
+    var transport = this.transport, json = this.evalJSON();
+
+    if (event == 'Complete') {
+      try {
+        (this.options['on' + this.transport.status]
+         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
+         || OpenLayers.Ajax.emptyFunction)(transport, json);
+      } catch (e) {
+        this.dispatchException(e);
+      }
+
+      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
+        this.evalResponse();
+    }
+
+    try {
+      (this.options['on' + event] || OpenLayers.Ajax.emptyFunction)(transport, json);
+      OpenLayers.Ajax.Responders.dispatch('on' + event, this, transport, json);
+    } catch (e) {
+      this.dispatchException(e);
+    }
+
+    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
+    if (event == 'Complete')
+      this.transport.onreadystatechange = OpenLayers.Ajax.emptyFunction;
+  },
+
+  dispatchException: function(exception) {
+    (this.options.onException || OpenLayers.Ajax.emptyFunction)(this, exception);
+    OpenLayers.Ajax.Responders.dispatch('onException', this, exception);
+  }
+});
+
+OpenLayers.Ajax.getElementsByTagNameNS  = function(parentnode, nsuri, nsprefix, tagname) {
+    return parentnode.getElementsByTagNameNS ?
+        parentnode.getElementsByTagNameNS(nsuri, tagname)
+        : parentnode.getElementsByTagName(nsprefix + ':' + tagname);
+}

Added: sandbox/bertil/lib/OpenLayers/BaseTypes.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/BaseTypes.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/BaseTypes.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,888 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/* OpenLayers.Class metaclass */
+OpenLayers.Class = {
+    isPrototype: function () {}, // magic anonymous value
+
+    create: function() {
+        return function() {
+            if (arguments && arguments[0] != OpenLayers.Class.isPrototype)
+                this.initialize.apply(this, arguments);
+        }
+    },
+ 
+    inherit: function () {
+        var superClass = arguments[0];
+        var proto = new superClass(OpenLayers.Class.isPrototype);
+        for (var i = 1; i < arguments.length; i++) {
+            if (typeof arguments[i] == "function") {
+                var mixin = arguments[i];
+                arguments[i] = new mixin(OpenLayers.Class.isPrototype);
+            }
+            OpenLayers.Util.extend(proto, arguments[i]);
+        }
+        return proto;
+    }
+};
+
+/*
+    OpenLayers.Class.inherit( OpenLayers.Layer.Grid, OpenLayers.Layer.HTTPRequest, {
+        some stuff
+    });
+*/
+
+/*********************
+ *                   *
+ *      PIXEL        * 
+ *                   * 
+ *********************/
+
+/**
+ * @class 
+ * 
+ * This class represents a screen coordinate, in x and y coordinates
+ */
+OpenLayers.Pixel = OpenLayers.Class.create();
+OpenLayers.Pixel.prototype = {
+    
+    /** @type float */
+    x: 0.0,
+
+    /** @type float */
+    y: 0.0,
+    
+    /** 
+    * @constructor
+    *
+    * @param {float} x
+    * @param {float} y
+    */
+    initialize: function(x, y) {
+        this.x = parseFloat(x);
+        this.y = parseFloat(y);
+    },
+    
+    /**
+    * @return string representation of Pixel. ex: "x=200.4,y=242.2"
+    * @type str
+    */
+    toString:function() {
+        return ("x=" + this.x + ",y=" + this.y);
+    },
+
+    /**
+     * @type OpenLayers.Pixel
+     */
+    clone:function() {
+        return new OpenLayers.Pixel(this.x, this.y); 
+    },
+    
+    /** 
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return whether or not the point passed in as parameter is equal to this
+    *          note that if px passed in is null, returns false
+    * @type bool
+    */
+    equals:function(px) {
+        var equals = false;
+        if (px != null) {
+            equals = ((this.x == px.x && this.y == px.y) ||
+                      (isNaN(this.x) && isNaN(this.y) && isNaN(px.x) && isNaN(px.y)));
+        }
+        return equals;
+    },
+
+    /**
+    * @param {int} x
+    * @param {int} y
+    * 
+    * @return a new Pixel with this pixel's x&y augmented by the 
+    *         values passed in.
+    * @type OpenLayers.Pixel
+    */
+    add:function(x, y) {
+        return new OpenLayers.Pixel(this.x + x, this.y + y);
+    },
+
+    /**
+    * @param {OpenLayers.Pixel} px
+    * 
+    * @return a new Pixel with this pixel's x&y augmented by the 
+    *         x&y values of the pixel passed in.
+    * @type OpenLayers.Pixel
+    */
+    offset:function(px) {
+        var newPx = this.clone();
+        if (px) {
+            newPx = this.add(px.x, px.y);
+        }
+        return newPx;
+    },
+    
+    /** @final @type str */
+    CLASS_NAME: "OpenLayers.Pixel"
+};
+
+
+/*********************
+ *                   *
+ *      SIZE         * 
+ *                   * 
+ *********************/
+
+
+/**
+* @class 
+* 
+* This class represents a width and height pair
+*/
+OpenLayers.Size = OpenLayers.Class.create();
+OpenLayers.Size.prototype = {
+
+    /** @type float */
+    w: 0.0,
+    
+    /** @type float */
+    h: 0.0,
+
+
+    /** 
+    * @constructor
+    * 
+    * @param {float} w 
+    * @param {float} h 
+    */
+    initialize: function(w, h) {
+        this.w = parseFloat(w);
+        this.h = parseFloat(h);
+    },
+
+    /** 
+    * @return String representation of OpenLayers.Size object. 
+    *         (ex. <i>"w=55,h=66"</i>)
+    * @type String
+    */
+    toString:function() {
+        return ("w=" + this.w + ",h=" + this.h);
+    },
+
+    /** 
+     * @return New OpenLayers.Size object with the same w and h values
+     * @type OpenLayers.Size
+     */
+    clone:function() {
+        return new OpenLayers.Size(this.w, this.h);
+    },
+
+    /** 
+    * @param {OpenLayers.Size} sz
+    * @returns Boolean value indicating whether the passed-in OpenLayers.Size 
+    *          object has the same w and h components as this
+    *          note that if sz passed in is null, returns false
+    *
+    * @type bool
+    */
+    equals:function(sz) {
+        var equals = false;
+        if (sz != null) {
+            equals = ((this.w == sz.w && this.h == sz.h) ||
+                      (isNaN(this.w) && isNaN(this.h) && isNaN(sz.w) && isNaN(sz.h)));
+        }
+        return equals;
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Size"
+};
+
+/*********************
+ *                   *
+ *      LONLAT       * 
+ *                   * 
+ *********************/
+
+
+/**
+* @class 
+* 
+* This class represents a longitude and latitude pair
+*/
+OpenLayers.LonLat = OpenLayers.Class.create();
+OpenLayers.LonLat.prototype = {
+
+    /** @type float */
+    lon: 0.0,
+    
+    /** @type float */
+    lat: 0.0,
+
+    /**
+    * @constructor
+    * 
+    * @param {float} lon
+    * @param {float} lat
+    */
+    initialize: function(lon, lat) {
+        this.lon = parseFloat(lon);
+        this.lat = parseFloat(lat);
+    },
+    
+    /** 
+    * @return String representation of OpenLayers.LonLat object. 
+    *         (ex. <i>"lon=5,lat=42"</i>)
+    * @type String
+    */
+    toString:function() {
+        return ("lon=" + this.lon + ",lat=" + this.lat);
+    },
+
+    /** 
+    * @return Shortened String representation of OpenLayers.LonLat object. 
+    *         (ex. <i>"5, 42"</i>)
+    * @type String
+    */
+    toShortString:function() {
+        return (this.lon + ", " + this.lat);
+    },
+
+    /** 
+     * @return New OpenLayers.LonLat object with the same lon and lat values
+     * @type OpenLayers.LonLat
+     */
+    clone:function() {
+        return new OpenLayers.LonLat(this.lon, this.lat);
+    },
+
+    /** 
+    * @param {float} lon
+    * @param {float} lat
+    *
+    * @return A new OpenLayers.LonLat object with the lon and lat passed-in
+    *         added to this's. 
+    * @type OpenLayers.LonLat
+    */
+    add:function(lon, lat) {
+        return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
+    },
+
+    /** 
+    * @param {OpenLayers.LonLat} ll
+    * @returns Boolean value indicating whether the passed-in OpenLayers.LonLat
+    *          object has the same lon and lat components as this
+    *          note that if ll passed in is null, returns false
+    *
+    * @type bool
+    */
+    equals:function(ll) {
+        var equals = false;
+        if (ll != null) {
+            equals = ((this.lon == ll.lon && this.lat == ll.lat) ||
+                      (isNaN(this.lon) && isNaN(this.lat) && isNaN(ll.lon) && isNaN(ll.lat)));
+        }
+        return equals;
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.LonLat"
+};
+
+/** Alternative constructor that builds a new OpenLayers.LonLat from a 
+*    parameter string
+* 
+* @constructor
+* 
+* @param {String} str Comma-separated Lon,Lat coordinate string. 
+*                     (ex. <i>"5,40"</i>)
+*
+* @returns New OpenLayers.LonLat object built from the passed-in String.
+* @type OpenLayers.LonLat
+*/
+OpenLayers.LonLat.fromString = function(str) {
+    var pair = str.split(",");
+    return new OpenLayers.LonLat(parseFloat(pair[0]), 
+                                 parseFloat(pair[1]));
+};
+
+
+
+/*********************
+ *                   *
+ *      BOUNDS       * 
+ *                   * 
+ *********************/
+
+
+
+
+/**
+* @class 
+* 
+* This class represents a bounding box. 
+* Data stored as left, bottom, right, top floats
+*/
+OpenLayers.Bounds = OpenLayers.Class.create();
+OpenLayers.Bounds.prototype = {
+
+    /** @type float */
+    left: 0.0,
+
+    /** @type float */
+    bottom: 0.0,
+
+    /** @type float */
+    right: 0.0,
+
+    /** @type float */
+    top: 0.0,    
+
+    /**
+    * @constructor
+    *
+    * @param {float} left
+    * @param {float} bottom
+    * @param {float} right
+    * @param {float} top
+    *
+    */
+    initialize: function(left, bottom, right, top) {
+        this.left = parseFloat(left);
+        this.bottom = parseFloat(bottom);
+        this.right = parseFloat(right);
+        this.top = parseFloat(top);
+    },
+
+    /**
+     * @returns A fresh copy of the bounds
+     * @type OpenLayers.Bounds
+     */
+    clone:function() {
+        return new OpenLayers.Bounds(this.left, this.bottom, 
+                                     this.right, this.top);
+    },
+
+    /** 
+    * @param {OpenLayers.Bounds} bounds
+    * @returns Boolean value indicating whether the passed-in OpenLayers.Bounds
+    *          object has the same left, right, top, bottom components as this
+    *           note that if bounds passed in is null, returns false
+    *
+    * @type bool
+    */
+    equals:function(bounds) {
+        var equals = false;
+        if (bounds != null) {
+            equals = ((this.left == bounds.left) && 
+                      (this.right == bounds.right) &&
+                      (this.top == bounds.top) && 
+                      (this.bottom == bounds.bottom));
+        }
+        return equals;
+    },
+
+    /** 
+    * @return String representation of OpenLayers.Bounds object. 
+    *         (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
+    * @type String
+    */
+    toString:function() {
+        return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
+                 + " right-top=(" + this.right + "," + this.top + ")" );
+    },
+
+    /** 
+     * @param {int} decimal How many significant digits in the bbox coords?
+     *                      Default is 6
+     * 
+     * @returns Simple String representation of OpenLayers.Bounds object.
+     *         (ex. <i>"5,42,10,45"</i>)
+     * @type String
+     */
+    toBBOX:function(decimal) {
+        if (decimal== null) {
+            decimal = 6; 
+        }
+        var mult = Math.pow(10, decimal);
+        var bbox = Math.round(this.left * mult) / mult + "," + 
+                   Math.round(this.bottom * mult) / mult + "," + 
+                   Math.round(this.right * mult) / mult + "," + 
+                   Math.round(this.top * mult) / mult;
+
+        return bbox;
+    },
+    
+    /**
+    * @returns The width of the bounds
+    * @type float
+    */
+    getWidth:function() {
+        return (this.right - this.left);
+    },
+
+    /**
+    * @returns The height of the bounds
+    * @type float
+    */
+    getHeight:function() {
+        return (this.top - this.bottom);
+    },
+
+    /**
+    * @returns An OpenLayers.Size which represents the size of the box
+    * @type OpenLayers.Size
+    */
+    getSize:function() {
+        return new OpenLayers.Size(this.getWidth(), this.getHeight());
+    },
+
+    /**
+    * @returns An OpenLayers.Pixel which represents the center of the bounds
+    * @type OpenLayers.Pixel
+    */
+    getCenterPixel:function() {
+        return new OpenLayers.Pixel( (this.left + this.right) / 2,
+                                     (this.bottom + this.top) / 2);
+    },
+
+    /**
+    * @returns An OpenLayers.LonLat which represents the center of the bounds
+    * @type OpenLayers.LonLat
+    */
+    getCenterLonLat:function() {
+        return new OpenLayers.LonLat( (this.left + this.right) / 2,
+                                      (this.bottom + this.top) / 2);
+    },
+
+    /**
+    * @param {float} x
+    * @param {float} y
+    *
+    * @returns A new OpenLayers.Bounds whose coordinates are the same as this, 
+    *          but shifted by the passed-in x and y values
+    * @type OpenLayers.Bounds
+    */
+    add:function(x, y) {
+        return new OpenLayers.Bounds(this.left + x, this.bottom + y,
+                                     this.right + x, this.top + y);
+    },
+
+    /**
+     * @param {OpenLayers.LonLat} ll
+     * @param {Boolean} inclusive Whether or not to include the border. 
+     *                            Default is true
+     *
+     * @return Whether or not the passed-in lonlat is within this bounds
+     * @type Boolean
+     */
+    containsLonLat:function(ll, inclusive) {
+        return this.contains(ll.lon, ll.lat, inclusive);
+    },
+
+    /**
+     * @param {OpenLayers.Pixel} px
+     * @param {Boolean} inclusive Whether or not to include the border. 
+     *                            Default is true
+     *
+     * @return Whether or not the passed-in pixel is within this bounds
+     * @type Boolean
+     */
+    containsPixel:function(px, inclusive) {
+        return this.contains(px.x, px.y, inclusive);
+    },
+    
+    /**
+    * @param {float} x
+    * @param {float} y
+    * @param {Boolean} inclusive Whether or not to include the border. 
+    *                            Default is true
+    *
+    * @return Whether or not the passed-in coordinates are within this bounds
+    * @type Boolean
+    */
+    contains:function(x, y, inclusive) {
+    
+        //set default
+        if (inclusive == null) {
+            inclusive = true;
+        }
+        
+        var contains = false;
+        if (inclusive) {
+            contains = ((x >= this.left) && (x <= this.right) && 
+                        (y >= this.bottom) && (y <= this.top));
+        } else {
+            contains = ((x > this.left) && (x < this.right) && 
+                        (y > this.bottom) && (y < this.top));
+        }              
+        return contains;
+    },
+
+    /**
+     * @param {OpenLayers.Bounds} bounds
+     * @param {Boolean} inclusive Whether or not to include the border. 
+     *                            Default is true
+     *
+     * @return Whether or not the passed-in OpenLayers.Bounds object intersects
+     *         this bounds. Simple math just check if either contains the other, 
+     *         allowing for partial.
+     * @type Boolean
+     */
+    intersectsBounds:function(bounds, inclusive) {
+
+        if (inclusive == null) {
+            inclusive = true;
+        }
+        var inBottom = (bounds.bottom == this.bottom && bounds.top == this.top) ?
+                    true : (((bounds.bottom > this.bottom) && (bounds.bottom < this.top)) || 
+                           ((this.bottom > bounds.bottom) && (this.bottom < bounds.top))); 
+        var inTop = (bounds.bottom == this.bottom && bounds.top == this.top) ?
+                    true : (((bounds.top > this.bottom) && (bounds.top < this.top)) ||
+                           ((this.top > bounds.bottom) && (this.top < bounds.top))); 
+        var inRight = (bounds.right == this.right && bounds.left == this.left) ?
+                    true : (((bounds.right > this.left) && (bounds.right < this.right)) ||
+                           ((this.right > bounds.left) && (this.right < bounds.right))); 
+        var inLeft = (bounds.right == this.right && bounds.left == this.left) ?
+                    true : (((bounds.left > this.left) && (bounds.left < this.right)) || 
+                           ((this.left > bounds.left) && (this.left < bounds.right))); 
+
+        return (this.containsBounds(bounds, true, inclusive) ||
+                bounds.containsBounds(this, true, inclusive) ||
+                ((inTop || inBottom ) && (inLeft || inRight )));
+    },
+    
+    /**
+    * @param {OpenLayers.Bounds} bounds
+    * @param {Boolean} partial If true, only part of passed-in 
+    *                          OpenLayers.Bounds needs be within this bounds. 
+    *                          If false, the entire passed-in bounds must be
+    *                          within. Default is false
+    * @param {Boolean} inclusive Whether or not to include the border. 
+    *                            Default is true
+    *
+    * @return Whether or not the passed-in OpenLayers.Bounds object is 
+    *         contained within this bounds. 
+    * @type Boolean
+    */
+    containsBounds:function(bounds, partial, inclusive) {
+
+        //set defaults
+        if (partial == null) {
+            partial = false;
+        }
+        if (inclusive == null) {
+            inclusive = true;
+        }
+
+        var inLeft;
+        var inTop;
+        var inRight;
+        var inBottom;
+        
+        if (inclusive) {
+            inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
+            inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
+            inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
+            inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
+        } else {
+            inLeft = (bounds.left > this.left) && (bounds.left < this.right);
+            inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
+            inRight= (bounds.right > this.left) && (bounds.right < this.right);
+            inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
+        }
+        
+        return (partial) ? (inTop || inBottom ) && (inLeft || inRight ) 
+                         : (inTop && inLeft && inBottom && inRight);
+    },
+
+    /** 
+     * @param {OpenLayers.LonLat} lonlat
+     *
+     * @returns The quadrant ("br" "tr" "tl" "bl") of the bounds in which 
+     *           the coordinate lies.
+     * @type String
+     */
+    determineQuadrant: function(lonlat) {
+    
+        var quadrant = "";
+        var center = this.getCenterLonLat();
+        
+        quadrant += (lonlat.lat < center.lat) ? "b" : "t";
+        quadrant += (lonlat.lon < center.lon) ? "l" : "r";
+    
+        return quadrant; 
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Bounds"
+};
+
+/** Alternative constructor that builds a new OpenLayers.Bounds from a 
+*    parameter string
+* 
+* @constructor
+* 
+* @param {String} str Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
+*
+* @returns New OpenLayers.Bounds object built from the passed-in String.
+* @type OpenLayers.Bounds
+*/
+OpenLayers.Bounds.fromString = function(str) {
+    var bounds = str.split(",");
+    return OpenLayers.Bounds.fromArray(bounds);
+};
+
+/** Alternative constructor that builds a new OpenLayers.Bounds
+*    from an array
+* 
+* @constructor
+* 
+* @param {Array} bbox Array of bounds values (ex. <i>[5,42,10,45]</i>)
+*
+* @returns New OpenLayers.Bounds object built from the passed-in Array.
+* @type OpenLayers.Bounds
+*/
+OpenLayers.Bounds.fromArray = function(bbox) {
+    return new OpenLayers.Bounds(parseFloat(bbox[0]),
+                                 parseFloat(bbox[1]),
+                                 parseFloat(bbox[2]),
+                                 parseFloat(bbox[3]));
+};
+
+/** Alternative constructor that builds a new OpenLayers.Bounds
+*    from an OpenLayers.Size
+* 
+* @constructor
+* 
+* @param {OpenLayers.Size} size
+*            
+* @returns New OpenLayers.Bounds object built with top and left set to 0 and
+*           bottom right taken from the passed-in OpenLayers.Size.
+* @type OpenLayers.Bounds
+*/
+OpenLayers.Bounds.fromSize = function(size) {
+    return new OpenLayers.Bounds(0,
+                                 size.h,
+                                 size.w,
+                                 0);
+};
+/**
+ * @param {String} quadrant 
+ * 
+ * @returns The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
+ *           you pass in "bl" it returns "tr", if you pass in "br" it 
+ *           returns "tl", etc.
+ * @type String
+ */
+OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
+    var opp = "";
+    
+    opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
+    opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
+    
+    return opp;
+};
+
+
+/*********************
+ *                   *
+ *      ELEMENT      * 
+ *                   * 
+ *********************/
+
+OpenLayers.Element = {
+  visible: function(element) {
+    return $(element).style.display != 'none';
+  },
+
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      OpenLayers.Element[OpenLayers.Element.visible(element) ? 'hide' : 'show'](element);
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight;
+  },
+
+  getDimensions: function(element) {
+    element = $(element);
+    if (OpenLayers.Element.getStyle(element, 'display') != 'none')
+      return {width: element.offsetWidth, height: element.offsetHeight};
+
+    // All *Width and *Height properties give 0 on elements with display none,
+    // so enable the element temporarily
+    var els = element.style;
+    var originalVisibility = els.visibility;
+    var originalPosition = els.position;
+    els.visibility = 'hidden';
+    els.position = 'absolute';
+    els.display = '';
+    var originalWidth = element.clientWidth;
+    var originalHeight = element.clientHeight;
+    els.display = 'none';
+    els.position = originalPosition;
+    els.visibility = originalVisibility;
+    return {width: originalWidth, height: originalHeight};
+  },
+
+  getStyle: function(element, style) {
+    element = $(element);
+    var value = element.style[style.camelize()];
+    if (!value) {
+      if (document.defaultView && document.defaultView.getComputedStyle) {
+        var css = document.defaultView.getComputedStyle(element, null);
+        value = css ? css.getPropertyValue(style) : null;
+      } else if (element.currentStyle) {
+        value = element.currentStyle[style.camelize()];
+      }
+    }
+
+    if (window.opera && OpenLayers.Util.indexOf(['left', 'top', 'right', 'bottom'],style) != -1)
+      if (OpenLayers.Element.getStyle(element, 'position') == 'static') value = 'auto';
+
+    return value == 'auto' ? null : value;
+  }
+
+};
+
+/*********************
+ *                   *
+ *      STRING       * 
+ *                   * 
+ *********************/
+
+/**
+* @param {String} sStart
+* 
+* @returns Whether or not this string starts with the string passed in.
+* @type Boolean
+*/
+String.prototype.startsWith = function(sStart) {
+    return (this.substr(0,sStart.length) == sStart);
+};
+
+/**
+* @param {String} str
+* 
+* @returns Whether or not this string contains with the string passed in.
+* @type Boolean
+*/
+String.prototype.contains = function(str) {
+    return (this.indexOf(str) != -1);
+};
+
+/**
+* @returns A trimmed version of the string - all leading and 
+*          trailing spaces removed
+* @type String
+*/
+String.prototype.trim = function() {
+    
+    var b = 0;
+    while(this.substr(b,1) == " ") {
+        b++;
+    }
+    
+    var e = this.length - 1;
+    while(this.substr(e,1) == " ") {
+        e--;
+    }
+    
+    return this.substring(b, e+1);
+};
+
+
+String.indexOf = function(object) {
+ for (var i = 0; i < this.length; i++)
+     if (this[i] == object) return i;
+ return -1;
+};
+
+String.prototype.camelize = function() {
+    var oStringList = this.split('-');
+    if (oStringList.length == 1) return oStringList[0];
+
+    var camelizedString = this.indexOf('-') == 0
+      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
+      : oStringList[0];
+
+    for (var i = 1, len = oStringList.length; i < len; i++) {
+      var s = oStringList[i];
+      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
+    }
+
+    return camelizedString;
+};
+
+
+/*********************
+ *                   *
+ *      NUMBER       * 
+ *                   * 
+ *********************/
+
+/** NOTE: Works only with integer values does *not* work with floats!
+ * 
+ * @param {int} sig
+ * 
+ * @returns The number, rounded to the specified number of significant digits.
+ *          If null, 0, or negaive value passed in, returns 0
+ * @type int
+ */
+Number.prototype.limitSigDigs = function(sig) {
+    var number = (sig > 0) ? this.toString() : 0;
+    if (sig < number.length) {
+        var exp = number.length - sig;
+        number = Math.round( this / Math.pow(10, exp)) * Math.pow(10, exp);
+    }
+    return parseInt(number);
+}
+
+
+/*********************
+ *                   *
+ *      FUNCTION     * 
+ *                   * 
+ *********************/
+
+Function.prototype.bind = function() {
+  var __method = this, args = [], object = arguments[0];
+  for (var i = 1; i < arguments.length; i++)
+    args.push(arguments[i]);
+  return function(moreargs) {
+    for (var i = 0; i < arguments.length; i++)
+      args.push(arguments[i]);
+    return __method.apply(object, args);
+  }
+};
+
+Function.prototype.bindAsEventListener = function(object) {
+  var __method = this;
+  return function(event) {
+    return __method.call(object, event || window.event);
+  }
+};

Added: sandbox/bertil/lib/OpenLayers/Control/ArgParser.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/ArgParser.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/ArgParser.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,114 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.ArgParser = OpenLayers.Class.create();
+OpenLayers.Control.ArgParser.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type OpenLayers.LonLat */
+    center: null,
+    
+    /** @type int */
+    zoom: null,
+
+    /** @type Array */
+    layers: null,
+
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} element
+     * @param {String} base
+     */
+    initialize: function(element, base) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /** Set the map property for the control. 
+     * 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        //make sure we dont already have an arg parser attached
+        for(var i=0; i< this.map.controls.length; i++) {
+            var control = this.map.controls[i];
+            if ( (control != this) &&
+                 (control.CLASS_NAME == "OpenLayers.Control.ArgParser") ) {
+                break;
+            }
+        }
+        if (i == this.map.controls.length) {
+
+            var args = OpenLayers.Util.getArgs();
+            if (args.lat && args.lon) {
+                this.center = new OpenLayers.LonLat(parseFloat(args.lon),
+                                                    parseFloat(args.lat));
+                if (args.zoom) {
+                    this.zoom = parseInt(args.zoom);
+                }
+    
+                // when we add a new baselayer to see when we can set the center
+                this.map.events.register('changebaselayer', this, 
+                                         this.setCenter);
+                this.setCenter();
+            }
+    
+            if (args.layers) {
+                this.layers = args.layers;
+    
+                // when we add a new layer, set its visibility 
+                this.map.events.register('addlayer', this, 
+                                         this.configureLayers);
+                this.configureLayers();
+            }
+        }
+    },
+   
+    /** As soon as a baseLayer has been loaded, we center and zoom
+     *   ...and remove the handler.
+     */
+    setCenter: function() {
+        
+        if (this.map.baseLayer) {
+            //dont need to listen for this one anymore
+            this.map.events.unregister('changebaselayer', this, 
+                                       this.setCenter);
+                                       
+            this.map.setCenter(this.center, this.zoom);
+        }
+    },
+
+    /** As soon as all the layers are loaded, cycle through them and 
+     *   hide or show them. 
+     */
+    configureLayers: function() {
+
+        if (this.layers.length == this.map.layers.length) { 
+            this.map.events.unregister('addlayer', this, this.configureLayers);
+
+            for(var i=0; i < this.layers.length; i++) {
+                
+                var layer = this.map.layers[i];
+                var c = this.layers.charAt(i);
+                
+                if (c == "B") {
+                    this.map.setBaseLayer(layer);
+                } else if ( (c == "T") || (c == "F") ) {
+                    layer.setVisibility(c == "T");
+                }
+            }
+        }
+    },     
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.ArgParser"
+});

Added: sandbox/bertil/lib/OpenLayers/Control/Edition/DrawCurvePath.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/Edition/DrawCurvePath.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/Edition/DrawCurvePath.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,163 @@
+/* Copyright (c) 2006 CampToCamp SA, Bertil Chapuis, published under the BSD license. */
+
+/**
+ * @class
+ *
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.DrawCurvePath = OpenLayers.Class.create();
+OpenLayers.Control.DrawCurvePath.prototype =
+    OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type Boolean */
+    performedDrag: false,
+
+    layer: null,
+
+    geometryClass: null,
+
+    geometry: null,
+
+    /**
+     * @constructor
+     */
+    initialize: function(LinearPathClass) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.geometryClass = LinearPathClass;
+    },
+
+    setLayer: function(layer){
+        if(layer)
+        this.layer = layer;
+        else
+        for(var i = 0; i < this.map.layers.length; i++) {
+        if(this.map.layers[i].CLASS_NAME == "OpenLayers.Layer.Vector")
+            this.layer = this.map.layers[i];
+        }
+    },
+
+    observe: function(){
+        this.setLayer();
+        this.defaultKeyPress.bindAsEventListener(this);
+        OpenLayers.Event.observe(window, "keypress",this.defaultKeyPress , false);
+        this.geometry = new this.geometryClass();
+    },
+
+    dispose: function(){
+        OpenLayers.Event.stopObserving(window, "keypress",this.defaultKeyPress , false);
+        this.geometry = null;
+    },
+
+    draw: function() {
+
+    },
+
+    analyseDrawingEvent: function() {
+
+    },
+
+    defaultClick: function (evt) {
+        OpenLayers.Event.stop(evt);
+    },
+
+    defaultDblClick: function (evt) {
+
+        var lonlat = this.layer.map.getLonLatFromLayerPx(evt.xy);
+        var point = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat);
+
+        this.geometry.path[this.geometry.path.length-1] = point;
+        this.geometry.path[this.geometry.path.length-2] = point;
+
+        this.layer.addFeatures(this.geometry);
+        this.layer.removeTmpFeature();
+
+        this.geometry = new this.geometryClass();
+
+        OpenLayers.Event.stop(evt);
+    },
+
+    defaultMouseDown: function (evt) {
+
+        OpenLayers.Event.stop(evt);
+        this.mouseDown = true;
+
+        var lonlat = this.layer.map.getLonLatFromLayerPx(evt.xy);
+        var point = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat)
+
+        if(this.geometry.path.length == 0)
+        this.geometry.path.push(point);
+
+        this.geometry.path.push(point);
+        this.geometry.path.push(point);
+        this.geometry.path.push(point);
+
+        this.layer.setTmpFeature(this.geometry);
+    },
+
+    defaultMouseMove: function (evt) {
+
+        var lonlat = this.layer.map.getLonLatFromLayerPx(evt.xy);
+        var point = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat);
+
+
+        if(this.mouseDown){
+            this.geometry.path[this.geometry.path.length-1] = point;
+            this.geometry.path[this.geometry.path.length-2] = point;
+            this.geometry.path[this.geometry.path.length-3] = point;
+            if(this.geometry.path.length>4){
+            var point2 = this.geometry.path[this.geometry.path.length-4];
+            var point3 = new OpenLayers.Feature.Geometry.Point(point2.lon-(point.lon-point2.lon), point2.lat-(point.lat-point2.lat));
+
+            this.geometry.path[this.geometry.path.length-5] = point3;
+            }
+        }else{
+             this.geometry.path[this.geometry.path.length-1] = point;
+            this.geometry.path[this.geometry.path.length-2] = point;
+        }
+
+        this.layer.setTmpFeature(this.geometry);
+    },
+
+    defaultMouseUp: function (evt) {
+        this.mouseDown = false;
+    },
+
+    defaultMouseOut: function (evt) {
+
+    },
+
+    defaultWheelUp: function()  {
+
+    },
+
+    defaultWheelDown: function()  {
+
+    },
+
+    defaultKeyPress: function(evt) {
+
+        switch (event.keyCode) {
+        case "asdf":
+        break;
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.DrawLinearPath"
+    });
+
+OpenLayers.Control.DrawCubicBezierCurve = OpenLayers.Class.create();
+OpenLayers.Control.DrawCubicBezierCurve.prototype =
+    OpenLayers.Class.inherit( OpenLayers.Control.DrawCurvePath, {
+
+    image: "stock_draw-curve.png",
+
+    size: new OpenLayers.Size(24,24),
+
+    initialize: function() {
+        OpenLayers.Control.DrawCurvePath.prototype.initialize.apply(this, [OpenLayers.Feature.Geometry.CubicBezierCurve].concat(arguments));
+    }
+    });
+
+
+

Added: sandbox/bertil/lib/OpenLayers/Control/Edition/DrawLinearPath.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/Edition/DrawLinearPath.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/Edition/DrawLinearPath.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,196 @@
+/* Copyright (c) 2006 CampToCamp SA, Bertil Chapuis, published under the BSD license. */
+
+/**
+ * @class
+ *
+ * @requires OpenLayers/Control.js
+***/
+OpenLayers.Control.Edition.DrawLinearPath = OpenLayers.Class.create();
+OpenLayers.Control.Edition.DrawLinearPath.prototype = OpenLayers.Class.inherit( OpenLayers.Control.Edition, {
+
+    /** @type String */
+    image: "stock_draw-polygon.gif",
+
+    /** @type OpenLayers.Size */
+    size: new OpenLayers.Size(24,24),
+
+    /** @type Array */
+    path: [],
+
+    /**
+     * @constructor
+    ***/
+    initialize: function() {
+        OpenLayers.Control.Edition.prototype.initialize.apply(this, arguments);
+    },
+    
+    turnOff: function(){
+        OpenLayers.Control.Edition.prototype.turnOff.apply(this, arguments);
+        this.eraseTmpElements();
+        this.path = [];
+    },
+    
+    calculatePoint: function(px){
+        var lonlat = this.map.getLonLatFromLayerPx(px);
+        var tmpPoint = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat);
+        
+        //calculate snapping point
+        if(this.snappingMode && this.backgroundGeometry){
+
+            if(geometry.CLASS_NAME == "OpenLayers.Feature.Geometry.Point"){
+                this.backgroundElement = geometry;
+            } else {
+                for(var i = 0; i < geometry.path.length-1; i++ ){
+                    
+                    var point1 = geometry.path[i];
+                    var point2 = geometry.path[i + 1];
+                    
+                    var segment = new OpenLayers.Feature.Geometry.LineSegment(point1, point2);
+                    segment.style = {
+                        fillColor: "red",
+                        fillOpacity: 0.2,
+                        strokeColor: "yellow",
+                        strokeWidth: 0.5,
+                        strokeUnit: "%",
+                        strokeOpacity: 1
+                    },
+                    this.layer.renderer.drawLineString(LineSegment, "tmpLineSegment");
+                    
+                    var m1 = (point2.y - point1.y) / (point2.x - point1.x);
+                    var p1 = point1.y - m1 * point1.x;
+
+                    var m2 = - m1;
+                    var p2 = tmpPoint.y - m2 * tmpPoint.x;
+
+                    var x = (p2 - p1) / (2 * m1);
+                    var y = (m1 * x + p1);
+
+                    var point = new OpenLayers.Feature.Geometry.Point(x, y);
+                    
+                }
+            }           
+           
+        //calculate controlled point
+        }else if(this.controllingMode && this.path.length > 0){
+            
+            var lastPoint = this.path.last();
+            
+            var width = tmpPoint.x-lastPoint.x;
+            var height = tmpPoint.y-lastPoint.y;
+            var hyp = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
+            var alpha = Math.acos(width/(hyp)) * 180.0/Math.PI;
+
+            var step = 22.5;
+
+            for(var degree = 0; degree <= 180; degree += step ){
+                if((alpha > (degree-step/2)) && (alpha < (degree+step/2))){
+                    var y = (height<0)?-Math.sin(degree/180*Math.PI)*hyp+lastPoint.y:Math.sin(degree/180*Math.PI)*hyp+lastPoint.y;
+                    var x = Math.cos(degree/180*Math.PI)*hyp+lastPoint.x;
+                    var point = OpenLayers.Feature.Geometry.Point(x, y);
+                }
+            }
+        //calculate normal point
+        }else{
+            var point = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat);
+        }
+        return point;
+    },
+
+    finalizeGeometry: function(){
+        if(this.path.first() == this.path.last()){
+            var geometry = new OpenLayers.Feature.Geometry.LinearRing(this.path);
+        }else{
+            var geometry = new OpenLayers.Feature.Geometry.LineString(this.path);
+        }
+        this.layer.addFeatures(geometry);
+    },
+
+    eraseTmpElements: function(){
+        for(var i = 0; i < this.path.length; i++) {
+            this.layer.renderer.eraseElement(this.path[i].id);
+        }
+        this.layer.renderer.eraseElement("tmpLine");
+        this.layer.renderer.eraseElement("tmpPath");
+        this.layer.renderer.eraseElement("tmpPoint");
+    },
+
+    defaultEdition: function (evt) {
+        switch (evt.type){
+            case "mouseout":
+            this.snappingGeometry = null;
+            break;
+            default:
+            if(evt.geometry && evt.geometry != this.snappingGeometry){
+            this.snappingGeometry = evt.geometry;
+            }
+            break;
+        }
+    },
+
+    defaultDblClick: function (evt) {
+        this.defaultMouseDown(evt);
+        this.eraseTmpElements();
+        if(this.path.length > 1){
+            this.finalizeGeometry();
+        }
+        
+        this.path.clear();
+    },
+
+    defaultMouseDown: function (evt) {
+        // Copyright Fred
+        if (this.lastDown &&
+            this.lastDown.x  == evt.xy.x && this.lastDown.y == evt.xy.y) {
+            return;
+        }
+
+        this.mouseDown = true;
+        this.lastDown = evt.xy;
+
+        var point = this.calculatePoint(evt.xy);
+
+    this.path.push(point);
+        this.layer.renderer.drawControlPoint(point, point.id);
+        this.layer.renderer.drawLineString(this.path, "tmpPath");
+    },
+
+    defaultMouseMove: function (evt) {
+        var point = this.calculatePoint(evt.xy);
+
+        if(this.mouseDown){
+            this.defaultMouseDown(evt);
+        }else{
+            if(this.path.length > 0){
+                this.layer.renderer.drawControlPoint(point, "tmpPoint");
+                this.layer.renderer.drawLineString([this.path.last(), point], "tmpLine");
+            }
+        }
+    },
+
+    defaultMouseUp: function (evt) {
+        this.mouseDown = false;
+    },
+
+    defaultKeyDown: function(evt){
+        OpenLayers.Control.Edition.prototype.defaultKeyDown.apply(this, arguments);               
+        switch (evt.keyCode){
+            case OpenLayers.Event.KEY_RETURN:
+                this.eraseTmpElements();
+                if(this.path.length > 1){
+                    this.finalizeGeometry();
+                }           
+                this.path.clear();
+                break;
+            case OpenLayers.Event.KEY_BACKSPACE:
+            case OpenLayers.Event.KEY_DELETE:
+            case OpenLayers.Event.KEY_ESC:
+                this.eraseTmpElements();
+                this.path.clear();
+                OpenLayers.Event.stop(evt);
+                break;
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.DrawLinearPath"
+});

Added: sandbox/bertil/lib/OpenLayers/Control/Edition/Mode.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/Edition/Mode.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/Edition/Mode.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,10 @@
+OpenLayers.Control.Edition.Mode = Class.create();
+OpenLayers.Control.Edition.Mode.prototype = {
+    initialize: function(){
+        
+    },
+    
+    calculatePosition: function() {
+        
+    }
+}

Added: sandbox/bertil/lib/OpenLayers/Control/Edition/Selection.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/Edition/Selection.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/Edition/Selection.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,127 @@
+/* Copyright (c) 2006 CampToCamp SA, Bertil Chapuis, published under the BSD license. */
+
+OpenLayers.Control.Edition.Selection = OpenLayers.Class.create();
+OpenLayers.Control.Edition.Selection.prototype = OpenLayers.Class.inherit( OpenLayers.Control.Edition, {
+
+    image: "mouse-cursor-white.gif",
+
+    size: new OpenLayers.Size(22,40), 
+
+    controlPoints: [],
+
+    selection: [],
+
+    initialize: function() {
+        OpenLayers.Control.Edition.prototype.initialize.apply(this, arguments);
+        
+    },
+
+    turnOff: function(){
+        OpenLayers.Control.Edition.prototype.turnOff.apply(this, arguments);
+        this.eraseTmpElements();
+        this.controlPoints = [];
+        this.selection = [];
+    },
+    
+    drawTmpElements: function(){
+        for(var iSelection = 0; iSelection < this.selection.length; iSelection++) {
+            if (this.selection[iSelection].path){
+                for(var iPath = 0; iPath < this.selection[iSelection].path.length; iPath++) {
+                    var point = this.selection[iSelection].path[iPath];
+                    this.layer.renderer.drawControlPoint(point, point.id);
+                }
+            }
+        }
+    },
+
+    eraseTmpElements: function(){
+        if(this.selection.length > 0){
+            for(var iSelection = 0; iSelection < this.selection.length; iSelection++) {
+                for(var iPath = 0; iPath < this.selection[iSelection].path.length; iPath++) {
+                   this.layer.renderer.eraseElement(this.selection[iSelection].path[iPath].id);
+                }
+            }
+        }
+    },
+
+    editionMouseDown: function(evt) {
+    
+        if (evt.geometry && evt.geometry.isControlPoint){
+    
+            if (this.shiftDown){
+                if (this.controlPoints.indexOf(evt.geometry) < 0){
+                    this.controlPoints.push(evt.geometry);
+                } else {
+                    this.controlPoints = this.controlPoints.without(evt.geometry);
+                }
+            } else {
+               this.controlPoints = [evt.geometry];
+            }
+    
+        } else if (evt.geometry){
+    
+            this.eraseTmpElements();
+            if (this.shiftDown){
+                if (this.selection.indexOf(evt.geometry) < 0){
+                    this.selection.push(evt.geometry);
+                } else {
+                    this.selection = this.selection.without(evt.geometry);
+                }
+            } else {
+                this.selection = [evt.geometry];
+            }
+            this.drawTmpElements();
+            this.controlPoints = [];
+            for(var iSelection = 0; iSelection < this.selection.length; iSelection++) {
+                if(this.selection.CLASS_NAME == "OpenLayers.Feature.Geometry.Point"){
+                    this.controlPoints = this.controlPoints.push(this.selection[iSelection]);
+                } else {
+                    this.controlPoints = this.controlPoints.concat(this.selection[iSelection].path);
+                }
+            }
+    
+        } else {
+            if (!this.shiftDown){
+                this.eraseTmpElements();
+                this.selection = [];
+                this.controlPoints = [];
+            }
+        }
+    },
+
+    editionMouseMove: function (evt) {
+        if (this.mouseDown){
+                this.selectionChange = true;
+                var lonlat = this.map.getLonLatFromLayerPx(evt.xy);
+                this.controlPoints[0].setX(lonlat.lon);
+                this.controlPoints[0].setY(lonlat.lat);
+                this.layer.renderer.drawControlPoint(this.controlPoints[0], this.controlPoints[0].id);
+                this.layer.renderer.drawGeometry(this.selection[0], "tmpGeometry");
+        }
+    },
+
+    editionMouseUp: function (evt) {
+        if (this.selectionChange){
+        this.layer.renderer.eraseElement("tmpGeometry");
+        this.layer.updateFeatures(this.selection);
+        }
+    },
+
+    defaultKeyDown: function(evt){
+        OpenLayers.Control.Edition.prototype.defaultKeyDown.apply(this, arguments);
+        
+        switch (evt.keyCode){
+        case OpenLayers.Event.KEY_BACKSPACE:
+        case OpenLayers.Event.KEY_DELETE:
+            this.eraseTmpElements();
+            this.layer.removeFeatures(this.selection);
+            this.selection.clear();
+            OpenLayers.Event.stop(evt);
+            break;
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.Selection"
+});
+

Added: sandbox/bertil/lib/OpenLayers/Control/Edition.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/Edition.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/Edition.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,309 @@
+/* Copyright (c) 2006 CampToCamp SA, Bertil Chapuis, published under the BSD license. */
+
+/**
+ * @class
+ *
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.Edition = OpenLayers.Class.create();
+OpenLayers.Control.Edition.prototype = OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    performedDrag: false,
+
+    /** @type OpenLayers.Layer.Vector */
+    layer: null,
+    
+    snappingMode: false,
+    
+    controllingMode: false,
+
+    /**
+     * @constructor
+     *
+     * @param {String} name
+     * @param {Object} options Hashtable of extra options to tag onto the layer
+     */
+    initialize: function() {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * @param {Object.Layer.Vector} layer
+     */
+    setLayer: function(layer){
+        if(layer){
+            this.layer = layer;
+        }else{
+            for(var i = 0; i < this.map.layers.length; i++) {
+                if(this.map.layers[i].CLASS_NAME == "OpenLayers.Layer.Vector"){
+                    this.layer = this.map.layers[i];
+                }
+            }
+        }
+    },
+
+    /**
+     *
+     */
+    turnOn: function(){
+        
+        this.setLayer();
+
+        this.defaultClick = this.defaultClick.bindAsEventListener(this);
+        this.defaultDblClick = this.defaultDblClick.bindAsEventListener(this);
+        this.defaultMouseDown = this.defaultMouseDown.bindAsEventListener(this);
+        this.defaultMouseUp = this.defaultMouseUp.bindAsEventListener(this);
+        this.defaultMouseMove = this.defaultMouseMove.bindAsEventListener(this);
+        this.defaultMouseOut = this.defaultMouseOut.bindAsEventListener(this);
+
+        this.map.events.register( "click", this, this.defaultClick);
+        this.map.events.register( "dblclick", this, this.defaultDblClick);
+        this.map.events.register( "mousedown", this, this.defaultMouseDown);
+        this.map.events.register( "mouseup", this, this.defaultMouseUp);
+        this.map.events.register( "mousemove", this, this.defaultMouseMove);
+        this.map.events.register( "mouseout", this, this.defaultMouseOut);
+
+        this.defaultKeyDown = this.defaultKeyDown.bindAsEventListener(this);
+        this.defaultKeyUp = this.defaultKeyUp.bindAsEventListener(this);
+        OpenLayers.Event.observe(document, "keydown", this.defaultKeyDown);
+        OpenLayers.Event.observe(document, "keyup", this.defaultKeyUp);
+
+        this.defaultEdition = this.defaultEdition.bindAsEventListener(this);
+        this.map.events.register("edition", this, this.defaultEdition);
+    },
+
+    /**
+     *
+     */
+    turnOff: function(){
+        this.map.events.unregister( "click", this, this.defaultClick);
+        this.map.events.unregister( "dblclick", this, this.defaultDblClick);
+        this.map.events.unregister( "mousedown", this, this.defaultMouseDown);
+        this.map.events.unregister( "mouseup", this, this.defaultMouseUp);
+        this.map.events.unregister( "mousemove", this, this.defaultMouseMove);
+        this.map.events.unregister( "mouseout", this, this.defaultMouseOut);
+        
+        OpenLayers.Event.stopObserving(document, "keydown", this.defaultKeyDown);
+        OpenLayers.Event.stopObserving(document, "keyup", this.defaultKeyUp);
+                
+        this.map.events.unregister("edition", map, this.defaultEdition);
+    },
+    
+    calculatePoint: function(px){
+        
+        
+        return point;
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    defaultEdition: function (evt) {
+        this.snappingMode = true;   
+        
+        var lonlat = this.map.getLonLatFromLayerPx(evt.xy);
+        var tmpPoint = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat);
+        
+        //calculate snapping point        
+        if(this.snappingMode && evt.geometry){
+            if (evt.geometry.isSnappingElement) {
+                    
+                    var m1 = evt.geometry.m;
+                    var p1 = evt.geometry.p;
+                    
+                    var m2 = - m1;
+                    var p2 = tmpPoint.y - m2 * tmpPoint.x;
+
+                    var x = (p2 - p1) / (2 * m1);
+                    var y = (m1 * x + p1);
+                
+            } else {
+                for (var i = 0; i < evt.geometry.path.length - 1; i++) {
+                    var segPoint1 = evt.geometry.path[i];
+                    var segPoint2 = evt.geometry.path[i + 1];
+                                   
+                    var m1 = (segPoint2.y - segPoint1.y) / (segPoint2.x - segPoint1.x);
+                    var p1 = segPoint1.y - m1 * segPoint1.x;
+    
+                    var m2 = - m1;
+                    var p2 = tmpPoint.y - m2 * tmpPoint.x;
+    
+                    var x = (p2 - p1) / (2 * m1);
+                    var y = (m1 * x + p1);
+                    
+                    var segPoint3 = new OpenLayers.Feature.Geometry.Point(x, y);
+                    
+                    var segment = new OpenLayers.Feature.Geometry.LineSegment(segPoint1, segPoint2);
+                    segment.m = m1;
+                    segment.p = p1;
+                    
+                }
+            }
+  
+        //calculate controlled point
+        } else if (this.controllingMode && this.path.length > 0) {
+            
+            var lastPoint = this.path.last();
+            
+
+            var width = tmpPoint.x-lastPoint.x;
+            var height = tmpPoint.y-lastPoint.y;
+            var hyp = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
+            var alpha = Math.acos(width/(hyp)) * 180.0/Math.PI;
+
+            var step = 22.5;
+
+            for(var degree = 0; degree <= 180; degree += step ){
+                if((alpha > (degree-step/2)) && (alpha < (degree+step/2))){
+                    var y = (height<0)?-Math.sin(degree/180*Math.PI)*hyp+lastPoint.y:Math.sin(degree/180*Math.PI)*hyp+lastPoint.y;
+                    var x = Math.cos(degree/180*Math.PI)*hyp+lastPoint.x;
+                    var point = OpenLayers.Feature.Geometry.Point(x, y);
+                }
+            }
+        //calculate normal point
+        }else{
+            var point = new OpenLayers.Feature.Geometry.Point(lonlat.lon, lonlat.lat);
+        }
+        
+        switch (evt.type){
+            case "mouseout":
+                this.editionMouseOut(evt);
+                break;
+            case "mousedown":
+                this.editionMouseDown(evt);
+                break;
+            case "mouseup":
+                this.editionMouseUp(evt);
+                break;
+            case "mousemove":
+                this.editionMouseMove(evt);
+                break;
+            case "click":
+                this.editionClick(evt);
+                break;
+            case "dblclick":
+                this.editionDblClick(evt);
+                break;
+        }
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    defaultClick: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    defaultDblClick: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    defaultMouseDown: function(evt) {
+        this.mouseDown = true;
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    defaultMouseMove: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    defaultMouseUp: function (evt) {
+        this.mouseDown = false;
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    defaultMouseOut: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    defaultWheelUp: function()  {
+
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    defaultWheelDown: function()  {},
+
+    /**
+     * @param {Event} evt
+     */
+    defaultKeyDown: function(evt){
+        switch (evt.keyCode){
+            case OpenLayers.Event.KEY_LEFT:
+                this.map.pan(-50, 0);
+                break;
+            case OpenLayers.Event.KEY_RIGHT:
+                this.map.pan(50, 0);
+                break;
+            case OpenLayers.Event.KEY_UP:
+                this.map.pan(0, -50);
+                break;
+            case OpenLayers.Event.KEY_DOWN:
+                this.map.pan(0, 50);
+                break;
+            case OpenLayers.Event.KEY_SHIFT:
+                this.shiftDown = true;
+                this.controlledMode = true;
+                break;
+            case OpenLayers.Event.KEY_CAPSLOCK:
+                this.capsLock = !this.capsLock;
+                this.snappingMode = !this.snappingMode;
+                break;
+        }
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    defaultKeyUp: function(evt){
+        switch (evt.keyCode){
+            case OpenLayers.Event.KEY_SHIFT:  
+                this.shiftDown = false;
+                this.controlledMode = false;
+                break;
+        }
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    editionClick: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    editionDblClick: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    editionMouseDown: function(evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    editionMouseMove: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    editionMouseUp: function (evt) {},
+
+    /**
+     * @param {Event} evt
+     */
+    editionMouseOut: function (evt) {},
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.Selection"
+});
+

Added: sandbox/bertil/lib/OpenLayers/Control/EditionToolbar.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/EditionToolbar.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/EditionToolbar.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,179 @@
+/* Copyright (c) 2006 CampToCamp SA, Bertil Chapuis, published under the BSD license. */
+
+/**
+ * @class
+ *
+ * @requires OpenLayers/Control.js
+***/
+OpenLayers.Control.EditionToolbar = OpenLayers.Class.create();
+OpenLayers.Control.EditionToolbar.prototype = OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    map: null,
+    
+    sz: null,
+    
+    xy: null,
+    
+    activeTool: null,
+    
+    tools: null,
+    
+    initialize: function(options){},
+
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+    },
+    
+    setTool: function(tool){
+        if(this.activeTool){
+            this.activeTool.turnOff();
+        }
+        this.activeTool = tool;
+        this.activeTool.turnOn();
+    },
+    
+    addTools: function(tools) {
+        if(!(tools instanceof Array)) {
+            tools = [tools];
+        }
+    
+        if(this.tools) {
+            this.tools = this.tools.concat(tools);
+        } else {
+    	   this.tools = [].concat(tools);
+        }
+    },
+    
+    draw: function() {
+        
+        for(var i = 0; i < this.tools.length; i++) {
+    	   this.tools[i].map = this.map;
+    	   console.log(this.tools[i]);
+        }
+   
+        OpenLayers.Control.prototype.draw.apply(this);
+        this.div.style.position = "absolute";
+        this.div.style.top = 140;
+        this.div.style.left = 16;
+        
+        for(var i = 0; i < this.tools.length; i++) {
+    	   this._drawTool(this.tools[i]);
+        }
+        
+        if(!this.activeTool) {
+    	   //this.setTool(this.tools[0]);
+        }
+    
+        return this.div;
+    },
+    
+    _drawTool: function(tool){
+        var imgLocation = OpenLayers.Util.getImagesLocation()+tool.image
+        var toolDiv = OpenLayers.Util.createDiv(tool.id, null, tool.size, imgLocation, "relative");
+        toolDiv.model = tool;
+    
+        OpenLayers.Event.observe(toolDiv, "click", this._toolClick.bindAsEventListener(this));
+    
+        this.div.appendChild(toolDiv);
+    
+        return tool;
+    },
+    
+    _toolClick: function(evt){
+        this.setTool(OpenLayers.Event.element(evt).model);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    _toolMouseDown: function(evt){
+    
+        OpenLayers.Event.stop(evt);
+    
+    },
+    
+/*    
+    defaultClick: function(evt){
+        this.activeTool.defaultClick(evt);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    defaultDblClick: function(evt){
+        this.activeTool.defaultDblClick(evt);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    defaultMouseDown: function(evt){
+    
+        this.activeTool.defaultMouseDown(evt);
+    
+        OpenLayers.Event.stop(evt);
+    
+    },
+    
+    defaultMouseMove: function(evt){
+        this.activeTool.defaultMouseMove(evt);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    defaultMouseUp: function(evt){
+        this.activeTool.defaultMouseUp(evt);
+        OpenLayers.Event.stop(evt);
+    
+    },
+    
+    defaultMouseOut: function(evt){
+        this.activeTool.defaultMouseOut(evt);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    defaultWheelUp: function(evt){
+        this.activeTool.defaultWheelUp(evt);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    defaultWheelDown: function(evt){
+        this.activeTool.defaultWheelDown(evt);
+        OpenLayers.Event.stop(evt);
+    },
+    
+    onWheelEvent: function(e){
+        // first determine whether or not the wheeling was inside the map
+        var inMap = false;
+        var elem = OpenLayers.Event.element(e);
+        while(elem != null) {
+        	if (this.map && elem == this.map.div) {
+        	    inMap = true;
+        	    break;
+        	}
+            elem = elem.parentNode;
+        }
+    
+        if (inMap) {
+    
+    	var delta = 0;
+    	if (!e) {
+    	    e = window.event;
+    	}
+        if (e.wheelDelta) {
+    	    delta = e.wheelDelta/120;
+    	    if (window.opera) {
+    		    delta = -delta;
+    	    }
+    	} else if (e.detail) {
+    	    delta = -e.detail / 3;
+    	}
+        if (delta) {
+    	    if (delta < 0) {
+    		    this.activeTool.defaultWheelDown();
+    	    } else {
+    		    this.activeTool.defaultWheelUp();
+    	    }
+    	}
+    
+    	OpenLayers.Event.stop(e);
+        }
+    },
+*/    
+    
+    CLASS_NAME: "OpenLayer.Control.EditionToolbar"
+    
+    });

Added: sandbox/bertil/lib/OpenLayers/Control/KeyboardDefaults.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/KeyboardDefaults.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/KeyboardDefaults.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,56 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.KeyboardDefaults = OpenLayers.Class.create();
+OpenLayers.Control.KeyboardDefaults.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type int */
+    slideFactor: 50,
+
+    /**
+     * @constructor
+     */
+    initialize: function() {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+    
+    /**
+     * 
+     */
+    draw: function() {
+        OpenLayers.Event.observe(document, 
+                      'keypress', 
+                      this.defaultKeyDown.bind(this));
+    },
+    
+    /**
+    * @param {Event} evt
+    */
+    defaultKeyDown: function (evt) {
+        switch(evt.keyCode) {
+            case OpenLayers.Event.KEY_LEFT:
+                this.map.pan(-50, 0);
+                break;
+            case OpenLayers.Event.KEY_RIGHT: 
+                this.map.pan(50, 0);
+                break;
+            case OpenLayers.Event.KEY_UP:
+                this.map.pan(0, -50);
+                break;
+            case OpenLayers.Event.KEY_DOWN:
+                this.map.pan(0, 50);
+                break;
+        }
+    },
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.KeyboardDefaults"
+});

Added: sandbox/bertil/lib/OpenLayers/Control/LayerSwitcher.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/LayerSwitcher.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/LayerSwitcher.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,443 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+/** 
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.LayerSwitcher = OpenLayers.Class.create();
+OpenLayers.Control.LayerSwitcher.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type String */
+    activeColor: "darkblue",
+    
+
+  // DOM Elements
+  
+    /** @type DOMElement */
+    layersDiv: null,
+    
+    /** @type DOMElement */
+    baseLayersDiv: null,
+
+    /** @type Array */
+    baseLayerInputs: null,
+    
+    
+    /** @type DOMElement */
+    dataLbl: null,
+    
+    /** @type DOMElement */
+    dataLayersDiv: null,
+
+    /** @type Array */
+    dataLayerInputs: null,
+
+
+    /** @type DOMElement */
+    minimizeDiv: null,
+
+    /** @type DOMElement */
+    maximizeDiv: null,
+    
+    /** @type Boolean */
+    ascending: true,
+ 
+    /**
+    * @constructor
+    */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /** 
+     * @param {OpenLayers.Map} map
+     */
+    setMap: function(map) {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+
+        this.map.events.register("addlayer", this, this.redraw);
+        this.map.events.register("changelayer", this, this.redraw);
+        this.map.events.register("removelayer", this, this.redraw);
+        this.map.events.register("changebaselayer", this, this.redraw);
+    },
+
+    /**
+    * @returns A reference to the DIV DOMElement containing the switcher tabs
+    * @type DOMElement
+    */  
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this);
+
+        // create layout divs
+        this.loadContents();
+
+        // set mode to minimize
+        this.minimizeControl();
+        
+        // populate div with current info
+        this.redraw();    
+
+        return this.div;
+    },
+
+    /** Goes through and takes the current state of the Map and rebuilds the
+     *   control to display that state. Groups base layers into a radio-button
+     *   group and lists each data layer with a checkbox.
+     * 
+     * @returns A reference to the DIV DOMElement containing the control
+     * @type DOMElement
+     */  
+    redraw: function() {
+
+        //clear out previous layers 
+        this.baseLayersDiv.innerHTML = "";
+        this.baseLayerInputs = new Array();
+        
+        this.dataLayersDiv.innerHTML = "";
+        this.dataLayerInputs = new Array();
+        
+        var containsOverlays = false;
+        
+        var layers = this.map.layers.slice();
+        if (!this.ascending) { layers.reverse(); }
+        for( var i = 0; i < layers.length; i++) {
+            var layer = layers[i];
+            var baseLayer = layer.isBaseLayer;
+
+            if (baseLayer || layer.displayInLayerSwitcher) {
+
+                if (!baseLayer) {
+                    containsOverlays = true;
+                }
+
+                // only check a baselayer if it is *the* baselayer, check data
+                //  layers if they are visible
+                var checked = (baseLayer) ? (layer == this.map.baseLayer)
+                                          : layer.getVisibility();
+    
+                // create input element
+                var inputElem = document.createElement("input");
+                inputElem.id = "input_" + layer.name;
+                inputElem.name = (baseLayer) ? "baseLayers" : layer.name;
+                inputElem.type = (baseLayer) ? "radio" : "checkbox";
+                inputElem.value = layer.name;
+                inputElem.checked = checked;
+                inputElem.defaultChecked = checked;
+                inputElem.layer = layer;
+                inputElem.control = this;
+
+                if (!baseLayer && !layer.inRange) {
+                    inputElem.disabled = true;
+                }
+                OpenLayers.Event.observe(inputElem, "mouseup", 
+                              this.onInputClick.bindAsEventListener(inputElem));
+                
+                // create span
+                var labelSpan = document.createElement("span");
+                if (!baseLayer && !layer.inRange) {
+                    labelSpan.style.color = "gray";
+                }
+                labelSpan.innerHTML = layer.name;
+                labelSpan.style.verticalAlign = (baseLayer) ? "bottom" : "baseline";
+                OpenLayers.Event.observe(labelSpan, "click", 
+                              this.onInputClick.bindAsEventListener(inputElem));
+                // create line break
+                var br = document.createElement("br");
+    
+                
+                var groupArray = (baseLayer) ? this.baseLayerInputs
+                                             : this.dataLayerInputs;
+                groupArray.push(inputElem);
+                                                     
+    
+                var groupDiv = (baseLayer) ? this.baseLayersDiv
+                                           : this.dataLayersDiv;
+                groupDiv.appendChild(inputElem);
+                groupDiv.appendChild(labelSpan);
+                groupDiv.appendChild(br);
+            }
+        }
+
+        // if no overlays, dont display the overlay label
+        this.dataLbl.style.display = (containsOverlays) ? "" : "none";        
+
+        return this.div;
+    },
+
+    /** A label has been clicked, check or uncheck its corresponding input
+     * 
+     * @private
+     * 
+     * @param {Event} e
+     */
+
+    onInputClick: function(e) {
+        if (!this.disabled) {
+            if (this.type == "radio") {
+                this.checked = true;
+                this.layer.map.setBaseLayer(this.layer, true);
+                this.layer.map.events.triggerEvent("changebaselayer");
+            } else {
+                this.checked = !this.checked;
+                this.control.updateMap();
+            }
+        }
+        OpenLayers.Event.stop(e);
+    },
+    
+    /** Need to update the map accordingly whenever user clicks in either of
+     *   the layers.
+     * 
+     * @private
+     * 
+     * @param {Event} e
+     */
+    onLayerClick: function(e) {
+        this.updateMap();
+    },
+
+
+    /** Cycles through the loaded data and base layer input arrays and makes
+     *   the necessary calls to the Map object such that that the map's 
+     *   visual state corresponds to what the user has selected in the control
+     * 
+     * @private
+     */
+    updateMap: function() {
+
+        // set the newly selected base layer        
+        for(var i=0; i < this.baseLayerInputs.length; i++) {
+            var input = this.baseLayerInputs[i];   
+            if (input.checked) {
+                this.map.setBaseLayer(input.layer, false);
+            }
+        }
+
+        // set the correct visibilities for the overlays
+        for(var i=0; i < this.dataLayerInputs.length; i++) {
+            var input = this.dataLayerInputs[i];   
+            input.layer.setVisibility(input.checked, true);
+        }
+
+    },
+
+    /** Set up the labels and divs for the control
+     * 
+     * @param {Event} e
+     */
+    maximizeControl: function(e) {
+
+        //HACK HACK HACK - find a way to auto-size this layerswitcher
+        this.div.style.width = "20em";
+        this.div.style.height = "";
+
+        this.showControls(false);
+
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+    
+    /** Hide all the contents of the control, shrink the size, 
+     *   add the maximize icon
+     * 
+     * @param {Event} e
+     */
+    minimizeControl: function(e) {
+
+        this.div.style.width = "0px";
+        this.div.style.height = "0px";
+
+        this.showControls(true);
+
+        if (e != null) {
+            OpenLayers.Event.stop(e);                                            
+        }
+    },
+
+    /** Hide/Show all LayerSwitcher controls depending on whether we are
+     *   minimized or not
+     * 
+     * @private
+     * 
+     * @param {Boolean} minimize
+     */
+    showControls: function(minimize) {
+
+        this.maximizeDiv.style.display = minimize ? "" : "none";
+        this.minimizeDiv.style.display = minimize ? "none" : "";
+
+        this.layersDiv.style.display = minimize ? "none" : "";
+    },
+    
+    /** Set up the labels and divs for the control
+     * 
+     */
+    loadContents: function() {
+
+        //configure main div
+        this.div.style.position = "absolute";
+        this.div.style.top = "10px";
+        this.div.style.right = "0px";
+        this.div.style.left = "";
+        this.div.style.fontFamily = "sans-serif";
+        this.div.style.fontWeight = "bold";
+        this.div.style.marginTop = "3px";
+        this.div.style.marginLeft = "3px";
+        this.div.style.marginBottom = "3px";
+        this.div.style.fontSize = "smaller";   
+        this.div.style.color = "white";   
+        this.div.style.backgroundColor = "transparent";
+    
+        OpenLayers.Event.observe(this.div, "mouseup", 
+                      this.mouseUp.bindAsEventListener(this));
+        OpenLayers.Event.observe(this.div, "click",
+                      this.ignoreEvent);
+        OpenLayers.Event.observe(this.div, "mousedown",
+                      this.mouseDown.bindAsEventListener(this));
+        OpenLayers.Event.observe(this.div, "dblclick", this.ignoreEvent);
+
+
+        // layers list div        
+        this.layersDiv = document.createElement("div");
+        this.layersDiv.id = "layersDiv";
+        this.layersDiv.style.paddingTop = "5px";
+        this.layersDiv.style.paddingLeft = "10px";
+        this.layersDiv.style.paddingBottom = "5px";
+        this.layersDiv.style.paddingRight = "75px";
+        this.layersDiv.style.backgroundColor = this.activeColor;        
+
+        // had to set width/height to get transparency in IE to work.
+        // thanks -- http://jszen.blogspot.com/2005/04/ie6-opacity-filter-caveat.html
+        //
+        this.layersDiv.style.width = "100%";
+        this.layersDiv.style.height = "100%";
+
+
+        var baseLbl = document.createElement("div");
+        baseLbl.innerHTML = "<u>Base Layer</u>";
+        baseLbl.style.marginTop = "3px";
+        baseLbl.style.marginLeft = "3px";
+        baseLbl.style.marginBottom = "3px";
+        
+        this.baseLayersDiv = document.createElement("div");
+        this.baseLayersDiv.style.paddingLeft = "10px";
+        /*OpenLayers.Event.observe(this.baseLayersDiv, "click", 
+                      this.onLayerClick.bindAsEventListener(this));
+        */
+                     
+
+        this.dataLbl = document.createElement("div");
+        this.dataLbl.innerHTML = "<u>Overlays</u>";
+        this.dataLbl.style.marginTop = "3px";
+        this.dataLbl.style.marginLeft = "3px";
+        this.dataLbl.style.marginBottom = "3px";
+        
+        this.dataLayersDiv = document.createElement("div");
+        this.dataLayersDiv.style.paddingLeft = "10px";
+
+        if (this.ascending) {
+            this.layersDiv.appendChild(baseLbl);
+            this.layersDiv.appendChild(this.baseLayersDiv);
+            this.layersDiv.appendChild(this.dataLbl);
+            this.layersDiv.appendChild(this.dataLayersDiv);
+        } else {
+            this.layersDiv.appendChild(this.dataLbl);
+            this.layersDiv.appendChild(this.dataLayersDiv);
+            this.layersDiv.appendChild(baseLbl);
+            this.layersDiv.appendChild(this.baseLayersDiv);
+        }    
+ 
+        this.div.appendChild(this.layersDiv);
+
+        Rico.Corner.round(this.div, {corners: "tl bl",
+                                      bgColor: "transparent",
+                                      color: this.activeColor,
+                                      blend: false});
+
+        Rico.Corner.changeOpacity(this.layersDiv, 0.75);
+
+        var imgLocation = OpenLayers.Util.getImagesLocation();
+        var sz = new OpenLayers.Size(18,18);        
+
+        // maximize button div
+        var img = imgLocation + 'layer-switcher-maximize.png';
+        this.maximizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_MaximizeDiv", 
+                                    null, 
+                                    sz, 
+                                    img, 
+                                    "absolute");
+        this.maximizeDiv.style.top = "5px";
+        this.maximizeDiv.style.right = "0px";
+        this.maximizeDiv.style.left = "";
+        this.maximizeDiv.style.display = "none";
+        OpenLayers.Event.observe(this.maximizeDiv, 
+                      "click", 
+                      this.maximizeControl.bindAsEventListener(this));
+        
+        this.div.appendChild(this.maximizeDiv);
+
+        // minimize button div
+        var img = imgLocation + 'layer-switcher-minimize.png';
+        var sz = new OpenLayers.Size(18,18);        
+        this.minimizeDiv = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_MinimizeDiv", 
+                                    null, 
+                                    sz, 
+                                    img, 
+                                    "absolute");
+        this.minimizeDiv.style.top = "5px";
+        this.minimizeDiv.style.right = "0px";
+        this.minimizeDiv.style.left = "";
+        this.minimizeDiv.style.display = "none";
+        OpenLayers.Event.observe(this.minimizeDiv, 
+                      "click", 
+                      this.minimizeControl.bindAsEventListener(this));
+
+        this.div.appendChild(this.minimizeDiv);
+    },
+    
+    /** 
+     * @private
+     *
+     * @param {Event} evt
+     */
+    ignoreEvent: function(evt) {
+        OpenLayers.Event.stop(evt);
+    },
+
+    /** Register a local 'mouseDown' flag so that we'll know whether or not
+     *   to ignore a mouseUp event
+     * 
+     * @private
+     *
+     * @param {Event} evt
+     */
+    mouseDown: function(evt) {
+        this.mouseDown = true;
+        this.ignoreEvent(evt);
+    },
+
+    /** If the 'mouseDown' flag has been set, that means that the drag was 
+     *   started from within the LayerSwitcher control, and thus we can 
+     *   ignore the mouseup. Otherwise, let the Event continue.
+     *  
+     * @private
+     *
+     * @param {Event} evt
+     */
+    mouseUp: function(evt) {
+        if (this.mouseDown) {
+            this.mouseDown = false;
+            this.ignoreEvent(evt);
+        }
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.LayerSwitcher"
+});

Added: sandbox/bertil/lib/OpenLayers/Control/MouseDefaults.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/MouseDefaults.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/MouseDefaults.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,281 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.MouseDefaults = OpenLayers.Class.create();
+OpenLayers.Control.MouseDefaults.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** @type Boolean */
+    performedDrag: false,
+    
+    image: "transform-move.gif",
+
+    size: new OpenLayers.Size(22,22),
+    
+    /** 
+     * @constructor
+     */
+    initialize: function() {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+    
+    turnOn: function() {
+        this.map.events.register( "click", this, this.defaultClick );
+        this.map.events.register( "dblclick", this, this.defaultDblClick );
+        this.map.events.register( "mousedown", this, this.defaultMouseDown );
+        this.map.events.register( "mouseup", this, this.defaultMouseUp );
+        this.map.events.register( "mousemove", this, this.defaultMouseMove );
+        this.map.events.register( "mouseout", this, this.defaultMouseOut );        
+
+        this.onWheelEvent = this.onWheelEvent.bindAsEventListener(this);
+        OpenLayers.Event.observe(window, "DOMMouseScroll",this.onWheelEvent , false);
+        OpenLayers.Event.observe(window, "mousewheel",this.onWheelEvent , false);
+        OpenLayers.Event.observe(document, "mousewheel",this.onWheelEvent , false);        
+    },
+    
+    turnOff: function() {
+        this.map.events.unregister( "click", this, this.defaultClick );
+        this.map.events.unregister( "dblclick", this, this.defaultDblClick );
+        this.map.events.unregister( "mousedown", this, this.defaultMouseDown );
+        this.map.events.unregister( "mouseup", this, this.defaultMouseUp );
+        this.map.events.unregister( "mousemove", this, this.defaultMouseMove );
+        this.map.events.unregister( "mouseout", this, this.defaultMouseOut );
+
+        OpenLayers.Event.stopObserving(window, "DOMMouseScroll",this.onWheelEvent , false);
+        OpenLayers.Event.stopObserving(window, "mousewheel",this.onWheelEvent , false);
+        OpenLayers.Event.stopObserving(document, "mousewheel",this.onWheelEvent , false);        
+    },    
+
+
+    /**
+     * 
+     */    
+    draw: function() {
+        this.map.events.register( "click", this, this.defaultClick );
+        this.map.events.register( "dblclick", this, this.defaultDblClick );
+        this.map.events.register( "mousedown", this, this.defaultMouseDown );
+        this.map.events.register( "mouseup", this, this.defaultMouseUp );
+        this.map.events.register( "mousemove", this, this.defaultMouseMove );
+        this.map.events.register( "mouseout", this, this.defaultMouseOut );
+    },
+
+    /**
+     * @param {Event} evt
+     * 
+     * @type Boolean
+     */
+    defaultClick: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        var notAfterDrag = !this.performedDrag;
+        this.performedDrag = false;
+        return notAfterDrag;
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultDblClick: function (evt) {
+        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
+        this.map.setCenter(newCenter, this.map.zoom + 1);
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.mouseDragStart = evt.xy.clone();
+        this.performedDrag  = false;
+        if (evt.shiftKey) {
+            this.map.div.style.cursor = "crosshair";
+            this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+                                                     this.mouseDragStart,
+                                                     null,
+                                                     null,
+                                                     "absolute",
+                                                     "2px solid red");
+            this.zoomBox.style.backgroundColor = "white";
+            this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+            this.zoomBox.style.opacity = "0.50";
+            this.zoomBox.style.fontSize = "1px";
+            this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+            this.map.viewPortDiv.appendChild(this.zoomBox);
+        }
+        document.onselectstart=function() { return false; }
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseMove: function (evt) {
+        if (this.mouseDragStart != null) {
+            if (this.zoomBox) {
+                var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+                var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+                this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+                this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+                if (evt.xy.x < this.mouseDragStart.x) {
+                    this.zoomBox.style.left = evt.xy.x+ "px";
+                }
+                if (evt.xy.y < this.mouseDragStart.y) {
+                    this.zoomBox.style.top = evt.xy.y+ "px";
+                }
+            } else {
+                var deltaX = this.mouseDragStart.x - evt.xy.x;
+                var deltaY = this.mouseDragStart.y - evt.xy.y;
+                var size = this.map.getSize();
+                var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+                                                 size.h / 2 + deltaY);
+                var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
+                this.map.setCenter(newCenter, null, true);
+                this.mouseDragStart = evt.xy.clone();
+                this.map.div.style.cursor = "move";
+            }
+            this.performedDrag = true;
+        }
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseUp: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        if (this.zoomBox) {
+            this.zoomBoxEnd(evt);    
+        } else {
+            if (this.performedDrag) {
+                this.map.setCenter(this.map.center);
+            }
+        }
+        document.onselectstart=null;
+        this.mouseDragStart = null;
+        this.map.div.style.cursor = "default";
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseOut: function (evt) {
+        if (this.mouseDragStart != null && 
+            OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            if (this.zoomBox) {
+                this.removeZoomBox();
+            }
+            this.mouseDragStart = null;
+        }
+    },
+
+
+    /** User spun scroll wheel up
+     * 
+     */
+    defaultWheelUp: function()  {
+        this.map.zoomIn();
+    },
+
+    /** User spun scroll wheel down
+     * 
+     */
+    defaultWheelDown: function()  {
+        this.map.zoomOut();
+    },
+
+    /** Zoombox function. 
+     *
+     */
+    zoomBoxEnd: function(evt) {
+        if (this.mouseDragStart != null) {
+            if (Math.abs(this.mouseDragStart.x - evt.xy.x) > 5 ||    
+                Math.abs(this.mouseDragStart.y - evt.xy.y) > 5) {   
+                var start = this.map.getLonLatFromViewPortPx( this.mouseDragStart ); 
+                var end = this.map.getLonLatFromViewPortPx( evt.xy );
+                var top = Math.max(start.lat, end.lat);
+                var bottom = Math.min(start.lat, end.lat);
+                var left = Math.min(start.lon, end.lon);
+                var right = Math.max(start.lon, end.lon);
+                var bounds = new OpenLayers.Bounds(left, bottom, right, top);
+                this.map.zoomToExtent(bounds);
+            } else {
+                var end = this.map.getLonLatFromViewPortPx( evt.xy );
+                this.map.setCenter(new OpenLayers.LonLat(
+                  (end.lon),
+                  (end.lat)
+                 ), this.map.getZoom() + 1);
+            }    
+            this.removeZoomBox();
+       }
+    },
+
+    /**
+     * Remove the zoombox from the screen and nullify our reference to it.
+     */
+    removeZoomBox: function() {
+        this.map.viewPortDiv.removeChild(this.zoomBox);
+        this.zoomBox = null;
+    },
+
+
+/**
+ *  Mouse ScrollWheel code thanks to http://adomas.org/javascript-mouse-wheel/
+ */
+
+
+    /** Catch the wheel event and handle it xbrowserly
+     * 
+     * @param {Event} e
+     */
+    onWheelEvent: function(e) {
+    
+        // first determine whether or not the wheeling was inside the map
+        var inMap = false;
+        var elem = OpenLayers.Event.element(e);
+        while(elem != null) {
+            if (this.map && elem == this.map.div) {
+                inMap = true;
+                break;
+            }
+            elem = elem.parentNode;
+        }
+        
+        if (inMap) {
+            
+            var delta = 0;
+            if (!e) {
+                e = window.event;
+            }
+            if (e.wheelDelta) {
+                delta = e.wheelDelta/120; 
+                if (window.opera) {
+                    delta = -delta;
+                }
+            } else if (e.detail) {
+                delta = -e.detail / 3;
+            }
+            if (delta) {
+                if (delta < 0) {
+                   this.defaultWheelDown();
+                } else {
+                   this.defaultWheelUp();
+                }
+            }
+            
+            //only wheel the map, not the window
+            OpenLayers.Event.stop(e);
+        }
+    },
+    
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.MouseDefaults"
+});
+

Added: sandbox/bertil/lib/OpenLayers/Control/MousePosition.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/MousePosition.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/MousePosition.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,107 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ */
+OpenLayers.Control.MousePosition = OpenLayers.Class.create();
+OpenLayers.Control.MousePosition.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+    
+    /** @type DOMElement */
+    element: null,
+    
+    /** @type String */
+    prefix: '',
+    
+    /** @type String */
+    separator: ', ',
+    
+    /** @type String */
+    suffix: '',
+    
+    /** @type int */
+    numdigits: 5,
+    
+    /** @type int */
+    granularity: 10,
+    
+    /** @type OpenLayers.LonLat */
+    lastXy: null,
+    
+    /**
+     * @constructor
+     * 
+     * @param {DOMElement} options Options for control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * @type DOMElement
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+        if (!this.element) {
+            this.div.left = "";
+            this.div.top = "";
+            this.div.className = "olControlMousePosition";
+            this.element = this.div;
+        }
+        
+        this.redraw();
+        return this.div;
+    },
+   
+    /**
+     * 
+     */
+    redraw: function(evt) {
+
+        var lonLat;
+
+        if (evt == null) {
+            lonLat = new OpenLayers.LonLat(0, 0);
+        } else {
+            if (this.lastXy == null ||
+                Math.abs(evt.xy.x - this.lastXy.x) > this.granularity ||
+                Math.abs(evt.xy.y - this.lastXy.y) > this.granularity)
+            {
+                this.lastXy = evt.xy;
+                return;
+            }
+
+            lonLat = this.map.getLonLatFromPixel(evt.xy);
+            this.lastXy = evt.xy;
+        }
+        
+        var digits = parseInt(this.numdigits);
+        var newHtml =
+            this.prefix +
+            lonLat.lon.toFixed(digits) +
+            this.separator + 
+            lonLat.lat.toFixed(digits) +
+            this.suffix;
+
+        if (newHtml != this.element.innerHTML) {
+            this.element.innerHTML = newHtml;
+        }
+    },
+
+    /** 
+     *
+     */
+    setMap: function() {
+        OpenLayers.Control.prototype.setMap.apply(this, arguments);
+        this.map.events.register( 'mousemove', this, this.redraw);
+    },     
+    
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.MousePosition"
+});

Added: sandbox/bertil/lib/OpenLayers/Control/MouseToolbar.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/MouseToolbar.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/MouseToolbar.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,310 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+
+/**
+ * @class
+ * 
+ * @requires OpenLayers/Control.js
+ * @requires OpenLayers/Control/MouseDefaults.js
+ */
+OpenLayers.Control.MouseToolbar = OpenLayers.Class.create();
+OpenLayers.Control.MouseToolbar.X = 6;
+OpenLayers.Control.MouseToolbar.Y = 300;
+OpenLayers.Control.MouseToolbar.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control.MouseDefaults, {
+    
+    mode: null,
+
+    buttons: null,
+
+    direction: "vertical",
+    
+    /** @type String */
+    buttonClicked: null,
+    
+    initialize: function(position, direction) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+        this.position = new OpenLayers.Pixel(OpenLayers.Control.MouseToolbar.X,
+                                             OpenLayers.Control.MouseToolbar.Y);
+        if (position) {
+            this.position = position;
+        }
+        if (direction) {
+            this.direction = direction; 
+        }
+        this.measureDivs = [];
+    },
+    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments); 
+        OpenLayers.Control.MouseDefaults.prototype.draw.apply(this, arguments);
+        this.buttons = new Object();
+        var sz = new OpenLayers.Size(28,28);
+        var centered = this.position;
+        this._addButton("zoombox", "drag-rectangle-off.png", "drag-rectangle-on.png", centered, sz, "Shift->Drag to zoom to area");
+        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+        this._addButton("pan", "panning-hand-off.png", "panning-hand-on.png", centered, sz, "Drag the map to pan.");
+        centered = centered.add((this.direction == "vertical" ? 0 : sz.w), (this.direction == "vertical" ? sz.h : 0));
+        this.switchModeTo("pan");
+        return this.div;
+        
+    },
+    
+    _addButton:function(id, img, activeImg, xy, sz, title) {
+        var imgLocation = OpenLayers.Util.getImagesLocation() + img;
+        var activeImgLocation = OpenLayers.Util.getImagesLocation() + activeImg;
+        // var btn = new ol.AlphaImage("_"+id, imgLocation, xy, sz);
+        var btn = OpenLayers.Util.createAlphaImageDiv(
+                                    "OpenLayers_Control_MouseToolbar_" + id, 
+                                    xy, sz, imgLocation, "absolute");
+
+        //we want to add the outer div
+        this.div.appendChild(btn);
+        btn.imgLocation = imgLocation;
+        btn.activeImgLocation = activeImgLocation;
+        
+        btn.events = new OpenLayers.Events(this, btn, null, true);
+        btn.events.register("mousedown", this, this.buttonDown); 
+        btn.events.register("mouseup", this, this.buttonUp); 
+        btn.events.register("dblclick", this, OpenLayers.Event.stop);
+        btn.action = id;
+        btn.title = title;
+        btn.alt = title;
+        btn.map = this.map;
+
+        //we want to remember/reference the outer div
+        this.buttons[id] = btn;
+        return btn;
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    buttonDown: function(evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.buttonClicked = evt.element.action;
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
+     * @param {Event} evt
+     */
+    buttonUp: function(evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        if (this.buttonClicked != null) {
+            if (this.buttonClicked == evt.element.action) {
+                this.switchModeTo(evt.element.action);
+            }
+            OpenLayers.Event.stop(evt);
+            this.buttonClicked = null;
+        }
+    },
+    
+    /**
+    * @param {Event} evt
+    */
+    defaultDblClick: function (evt) {
+        this.switchModeTo("pan");
+        this.performedDrag = false;
+        var newCenter = this.map.getLonLatFromViewPortPx( evt.xy ); 
+        this.map.setCenter(newCenter, this.map.zoom + 1);
+        OpenLayers.Event.stop(evt);
+        return false;
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseDown: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        this.mouseDragStart = evt.xy.clone();
+        this.performedDrag = false;
+        this.startViaKeyboard = false;
+        if (evt.shiftKey && this.mode !="zoombox") {
+            this.switchModeTo("zoombox");
+            this.startViaKeyboard = true;
+        } else if (evt.altKey && this.mode !="measure") {
+            this.switchModeTo("measure");
+        } else if (!this.mode) {
+            this.switchModeTo("pan");
+        }
+        
+        switch (this.mode) {
+            case "zoombox":
+                this.map.div.style.cursor = "crosshair";
+                this.zoomBox = OpenLayers.Util.createDiv('zoomBox',
+                                                         this.mouseDragStart,
+                                                         null,
+                                                         null,
+                                                         "absolute",
+                                                         "2px solid red");
+                this.zoomBox.style.backgroundColor = "white";
+                this.zoomBox.style.filter = "alpha(opacity=50)"; // IE
+                this.zoomBox.style.opacity = "0.50";
+                this.zoomBox.style.fontSize = "1px";
+                this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                this.map.viewPortDiv.appendChild(this.zoomBox);
+                this.performedDrag = true;
+                break;
+            case "measure":
+                var distance = "";
+                if (this.measureStart) {
+                    measureEnd = this.map.getLonLatFromViewPortPx(this.mouseDragStart);
+                    distance = OpenLayers.Util.distVincenty(this.measureStart, measureEnd);
+                    distance = Math.round(distance * 100) / 100;
+                    distance = distance + "km";
+                    this.measureStartBox = this.measureBox;
+                }    
+                this.measureStart = this.map.getLonLatFromViewPortPx(this.mouseDragStart);;
+                this.measureBox = OpenLayers.Util.createDiv(null,
+                                                         this.mouseDragStart.add(
+                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
+                                                           -2-parseInt(this.map.layerContainerDiv.style.top)),
+                                                         null,
+                                                         null,
+                                                         "absolute");
+                this.measureBox.style.width="4px";
+                this.measureBox.style.height="4px";
+                this.measureBox.style.fontSize = "1px";
+                this.measureBox.style.backgroundColor="red";
+                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                this.map.layerContainerDiv.appendChild(this.measureBox);
+                if (distance) {
+                    this.measureBoxDistance = OpenLayers.Util.createDiv(null,
+                                                         this.mouseDragStart.add(
+                                                           -2-parseInt(this.map.layerContainerDiv.style.left),
+                                                           2-parseInt(this.map.layerContainerDiv.style.top)),
+                                                         null,
+                                                         null,
+                                                         "absolute");
+                    
+                    this.measureBoxDistance.innerHTML = distance;
+                    this.measureBoxDistance.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                    this.map.layerContainerDiv.appendChild(this.measureBoxDistance);
+                    this.measureDivs.push(this.measureBoxDistance);
+                }
+                this.measureBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+                this.map.layerContainerDiv.appendChild(this.measureBox);
+                this.measureDivs.push(this.measureBox);
+                break;
+            default:
+                this.map.div.style.cursor = "move";
+                break;
+        }
+        document.onselectstart = function() { return false; } 
+        OpenLayers.Event.stop(evt);
+    },
+
+    switchModeTo: function(mode) {
+        if (mode != this.mode) {
+            
+
+            if (this.mode && this.buttons[this.mode]) {
+                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[this.mode], null, null, null, this.buttons[this.mode].imgLocation);
+            }
+            if (this.mode == "measure" && mode != "measure") {
+                for(var i = 0; i < this.measureDivs.length; i++) {
+                    if (this.measureDivs[i]) { 
+                        this.map.layerContainerDiv.removeChild(this.measureDivs[i]);
+                    }
+                }
+                this.measureDivs = [];
+                this.measureStart = null;
+            }
+            this.mode = mode;
+            if (this.buttons[mode]) {
+                OpenLayers.Util.modifyAlphaImageDiv(this.buttons[mode], null, null, null, this.buttons[mode].activeImgLocation);
+            }
+            switch (this.mode) {
+                case "zoombox":
+                    this.map.div.style.cursor = "crosshair";
+                    break;
+                default:
+                    this.map.div.style.cursor = "default";
+                    break;
+            }
+
+        } 
+    }, 
+
+    leaveMode: function() {
+        this.switchModeTo("pan");
+    },
+    
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseMove: function (evt) {
+        if (this.mouseDragStart != null) {
+            switch (this.mode) {
+                case "zoombox": 
+                    var deltaX = Math.abs(this.mouseDragStart.x - evt.xy.x);
+                    var deltaY = Math.abs(this.mouseDragStart.y - evt.xy.y);
+                    this.zoomBox.style.width = Math.max(1, deltaX) + "px";
+                    this.zoomBox.style.height = Math.max(1, deltaY) + "px";
+                    if (evt.xy.x < this.mouseDragStart.x) {
+                        this.zoomBox.style.left = evt.xy.x+"px";
+                    }
+                    if (evt.xy.y < this.mouseDragStart.y) {
+                        this.zoomBox.style.top = evt.xy.y+"px";
+                    }
+                    break;
+                default:
+                    var deltaX = this.mouseDragStart.x - evt.xy.x;
+                    var deltaY = this.mouseDragStart.y - evt.xy.y;
+                    var size = this.map.getSize();
+                    var newXY = new OpenLayers.Pixel(size.w / 2 + deltaX,
+                                                     size.h / 2 + deltaY);
+                    var newCenter = this.map.getLonLatFromViewPortPx( newXY ); 
+                    this.map.setCenter(newCenter, null, true);
+                    this.mouseDragStart = evt.xy.clone();
+            }
+            this.performedDrag = true;
+        }
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseUp: function (evt) {
+        if (!OpenLayers.Event.isLeftClick(evt)) return;
+        switch (this.mode) {
+            case "zoombox":
+                this.zoomBoxEnd(evt);
+                if (this.startViaKeyboard) this.leaveMode();
+                break;
+            case "pan":
+                if (this.performedDrag) {
+                    this.map.setCenter(this.map.center);
+                }        
+        }
+        document.onselectstart = null;
+        this.mouseDragStart = null;
+        this.map.div.style.cursor = "default";
+    },
+
+    /**
+    * @param {Event} evt
+    */
+    defaultMouseOut: function (evt) {
+        if (this.mouseDragStart != null
+            && OpenLayers.Util.mouseLeft(evt, this.map.div)) {
+            if (this.zoomBox) {
+                this.removeZoomBox();
+                if (this.startViaKeyboard) this.leaveMode();
+            }
+            this.mouseDragStart = null;
+            this.map.div.style.cursor = "default";
+        }
+    },
+
+    defaultClick: function (evt) {
+        if (this.performedDrag)  {
+            this.performedDrag = false;
+            return false;
+        }
+    }
+});
+

Added: sandbox/bertil/lib/OpenLayers/Control/OverviewMap.js
===================================================================
--- sandbox/bertil/lib/OpenLayers/Control/OverviewMap.js	                        (rev 0)
+++ sandbox/bertil/lib/OpenLayers/Control/OverviewMap.js	2006-11-06 13:32:47 UTC (rev 1764)
@@ -0,0 +1,514 @@
+/* Copyright (c) 2006 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+/**
+ * @fileoverview Locator Map Control
+ * @author Tim Schaub
+ */
+
+// @require: OpenLayers/Control.js
+
+/** 
+* @class
+*/
+OpenLayers.Control.OverviewMap = OpenLayers.Class.create();
+
+OpenLayers.Control.OverviewMap.prototype = 
+  OpenLayers.Class.inherit( OpenLayers.Control, {
+
+    /** For div.id
+     * @type String */
+    id:  "OverviewMap",
+
+    /** @type DOMElement */
+    element: null,
+    
+    /**
+     * The overvew map itself.
+     * @type OpenLayers.Map
+     */
+    ovmap: null,
+        
+    /**
+     * Ordered list of layers in the overview map.  If none are sent at
+     * construction, then the default below is used.
+     * 
+     * @type Array(OpenLayers.Layer)
+     */
+    layers: [],
+
+    /**
+     * The ratio of the overview map resolution to the main map resolution
+     * at which to zoom farther out on the overview map.
+     * @type Float
+     */
+    minRatio: 8,
+
+    /**
+     * The ratio of the overview map resolution to the main map resolution
+     * at which to zoom farther in on the overview map.
+     * @type Float
+     */
+    maxRatio: 32,
+
+    /**
+     * @constructor
+     * @param {Object} options Hashtable of options to set on the overview map
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, [options]);
+    },
+
+    /**
+     * @type DOMElement
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+
+        if(!(this.layers.length > 0)) {
+            if (this.map.baseLayer) {
+                var layer = this.map.baseLayer.clone();
+                this.layers = [layer];
+            } else {
+                this.map.events.register("changebaselayer", this, this.baseLayerDraw);
+                return this.div;
+            }
+        }
+
+        // create overview map DOM elements
+        this.element = document.createElement('div');
+        this.element.className = 'olControlOverviewMapElement';
+        this.element.style.display = 'none';
+
+        this.mapDiv = document.createElement('div');
+        this.mapDiv.style.width = '180px';
+        this.mapDiv.style.height = '90px';
+        this.mapDiv.style.position = 'relative';
+        this.mapDiv.style.overflow = 'hidden';
+        this.mapDiv.id = OpenLayers.Util.createUniqueID('overviewMap');
+        
+        this.extentRectangle = document.createElement('div');
+        this.extentRectangle.style.position = 'absolute';
+        this.extentRectangle.style.zIndex = 1000;  //HACK
+        this.extentRectangle.style.backgroundImage = 'url(' +
+                                        OpenLayers.Util.getImagesLocation() +
+                                        '/blank.png)';
+        this.extentRectangle.className = 'olControlOverviewMapExtentRectangle';
+        this.mapDiv.appendChild(this.extentRectangle);
+                
+        this.element.appendChild(this.mapDiv);  
+
+        this.div.appendChild(this.element);
+        this.di