[OpenLayers-Commits] r11765 - in sandbox/tschaub/canvas: . examples lib lib/OpenLayers lib/OpenLayers/BaseTypes lib/OpenLayers/Control lib/OpenLayers/Feature lib/OpenLayers/Filter lib/OpenLayers/Format lib/OpenLayers/Format/Filter lib/OpenLayers/Format/OWSContext lib/OpenLayers/Format/WFSCapabilities lib/OpenLayers/Geometry lib/OpenLayers/Handler lib/OpenLayers/Layer lib/OpenLayers/Layer/Google lib/OpenLayers/Protocol lib/OpenLayers/Protocol/WFS lib/OpenLayers/Renderer lib/OpenLayers/Strategy lib/OpenLayers/Tile lib/Rico tests tests/BaseTypes tests/Control tests/Format tests/Format/Filter tests/Format/OWSContext tests/Format/WFSCapabilities tests/Handler tests/Layer tests/Protocol tests/Renderer tests/Strategy tests/manual tests/speed theme/default theme/default/img tools

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Wed Mar 30 01:03:21 EDT 2011


Author: tschaub
Date: 2011-03-29 22:03:20 -0700 (Tue, 29 Mar 2011)
New Revision: 11765

Added:
   sandbox/tschaub/canvas/examples/arcgiscache_ags.html
   sandbox/tschaub/canvas/examples/arcgiscache_direct.html
   sandbox/tschaub/canvas/examples/arcgiscache_jsonp.html
   sandbox/tschaub/canvas/examples/cross-origin.html
   sandbox/tschaub/canvas/examples/cross-origin.js
   sandbox/tschaub/canvas/examples/kml-pointtrack.html
   sandbox/tschaub/canvas/examples/kml-pointtrack.js
   sandbox/tschaub/canvas/examples/mobile-drawing.html
   sandbox/tschaub/canvas/examples/mobile-drawing.js
   sandbox/tschaub/canvas/lib/OpenLayers/Filter/Function.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/QueryStringFilter.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGISCache.js
   sandbox/tschaub/canvas/lib/OpenLayers/Protocol/Script.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/NG.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js
   sandbox/tschaub/canvas/tests/Format/QueryStringFilter.html
   sandbox/tschaub/canvas/tests/Layer/ArcGISCache.html
   sandbox/tschaub/canvas/tests/Layer/ArcGISCache.json
   sandbox/tschaub/canvas/tests/Protocol/Script.html
   sandbox/tschaub/canvas/tests/Renderer/SVG2.html
   sandbox/tschaub/canvas/tests/manual/box-quirks.html
   sandbox/tschaub/canvas/tests/manual/box-strict.html
   sandbox/tschaub/canvas/tests/speed/vector-renderers.html
   sandbox/tschaub/canvas/tests/speed/vector-renderers.js
Modified:
   sandbox/tschaub/canvas/
   sandbox/tschaub/canvas/examples/bing.html
   sandbox/tschaub/canvas/examples/canvas.js
   sandbox/tschaub/canvas/examples/dynamic-text-layer.html
   sandbox/tschaub/canvas/examples/editingtoolbar-outside.html
   sandbox/tschaub/canvas/examples/example-list.html
   sandbox/tschaub/canvas/examples/fullScreen.js
   sandbox/tschaub/canvas/examples/geolocation.js
   sandbox/tschaub/canvas/examples/marker-shadow.html
   sandbox/tschaub/canvas/examples/mobile-jq.js
   sandbox/tschaub/canvas/examples/multitouch.html
   sandbox/tschaub/canvas/examples/navtoolbar-alwaysZoom.html
   sandbox/tschaub/canvas/examples/navtoolbar-outsidemap.html
   sandbox/tschaub/canvas/examples/ordering.html
   sandbox/tschaub/canvas/examples/restricted-extent.html
   sandbox/tschaub/canvas/examples/snap-split.html
   sandbox/tschaub/canvas/examples/snapping.html
   sandbox/tschaub/canvas/examples/spherical-mercator.html
   sandbox/tschaub/canvas/examples/split-feature.html
   sandbox/tschaub/canvas/examples/symbolizers-fill-stroke-graphic.html
   sandbox/tschaub/canvas/examples/vector-features.html
   sandbox/tschaub/canvas/examples/wfs-states.js
   sandbox/tschaub/canvas/lib/OpenLayers.js
   sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes.js
   sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Bounds.js
   sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Element.js
   sandbox/tschaub/canvas/lib/OpenLayers/Console.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/ArgParser.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/Attribution.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/KeyboardDefaults.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/MousePosition.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/Navigation.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoom.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoomBar.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/PinchZoom.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/SLDSelect.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/ScaleLine.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/SelectFeature.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/Snapping.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/Split.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/TouchNavigation.js
   sandbox/tschaub/canvas/lib/OpenLayers/Control/WMSGetFeatureInfo.js
   sandbox/tschaub/canvas/lib/OpenLayers/Events.js
   sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js
   sandbox/tschaub/canvas/lib/OpenLayers/Filter/FeatureId.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Atom.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Context.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_0_0.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_1_0.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/GML.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/GPX.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoJSON.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoRSS.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/JSON.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/KML.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/OSM.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext/v0_3_1.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/SLD.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSCapabilities.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetFeatureOfInterest.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetObservation.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSDescribeFeatureType.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WMC.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSCapabilities.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSDescribeLayer.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSGetFeatureInfo.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/WMTSCapabilities.js
   sandbox/tschaub/canvas/lib/OpenLayers/Geometry/Rectangle.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Box.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Click.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Drag.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Pinch.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Polygon.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGIS93Rest.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcIMS.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/EventPane.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/Google/v3.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/Grid.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/KaMap.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapGuide.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapServer.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/PointTrack.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/TMS.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/TileCache.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMS.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMTS.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/WorldWind.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/XYZ.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/Zoomify.js
   sandbox/tschaub/canvas/lib/OpenLayers/Map.js
   sandbox/tschaub/canvas/lib/OpenLayers/Popup.js
   sandbox/tschaub/canvas/lib/OpenLayers/Projection.js
   sandbox/tschaub/canvas/lib/OpenLayers/Protocol/HTTP.js
   sandbox/tschaub/canvas/lib/OpenLayers/Protocol/WFS/v1.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Elements.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/VML.js
   sandbox/tschaub/canvas/lib/OpenLayers/Request.js
   sandbox/tschaub/canvas/lib/OpenLayers/Strategy/BBOX.js
   sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Cluster.js
   sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Filter.js
   sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Fixed.js
   sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Paging.js
   sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Refresh.js
   sandbox/tschaub/canvas/lib/OpenLayers/Tile/Image.js
   sandbox/tschaub/canvas/lib/OpenLayers/Util.js
   sandbox/tschaub/canvas/lib/Rico/Color.js
   sandbox/tschaub/canvas/tests/BaseTypes/Bounds.html
   sandbox/tschaub/canvas/tests/Control.html
   sandbox/tschaub/canvas/tests/Control/DrawFeature.html
   sandbox/tschaub/canvas/tests/Control/KeyboardDefaults.html
   sandbox/tschaub/canvas/tests/Control/Measure.html
   sandbox/tschaub/canvas/tests/Control/PanZoomBar.html
   sandbox/tschaub/canvas/tests/Control/SLDSelect.html
   sandbox/tschaub/canvas/tests/Control/SelectFeature.html
   sandbox/tschaub/canvas/tests/Control/TouchNavigation.html
   sandbox/tschaub/canvas/tests/Control/WMSGetFeatureInfo.html
   sandbox/tschaub/canvas/tests/Events.html
   sandbox/tschaub/canvas/tests/Feature.html
   sandbox/tschaub/canvas/tests/Format/Filter/v1.html
   sandbox/tschaub/canvas/tests/Format/Filter/v1_0_0.html
   sandbox/tschaub/canvas/tests/Format/Filter/v1_1_0.html
   sandbox/tschaub/canvas/tests/Format/GeoJSON.html
   sandbox/tschaub/canvas/tests/Format/GeoRSS.html
   sandbox/tschaub/canvas/tests/Format/KML.html
   sandbox/tschaub/canvas/tests/Format/OSM.html
   sandbox/tschaub/canvas/tests/Format/OWSContext/v0_3_1.html
   sandbox/tschaub/canvas/tests/Format/WFSCapabilities/v1.html
   sandbox/tschaub/canvas/tests/Format/WMC.html
   sandbox/tschaub/canvas/tests/Format/WMSGetFeatureInfo.html
   sandbox/tschaub/canvas/tests/Handler/Box.html
   sandbox/tschaub/canvas/tests/Handler/Click.html
   sandbox/tschaub/canvas/tests/Handler/Drag.html
   sandbox/tschaub/canvas/tests/Handler/Path.html
   sandbox/tschaub/canvas/tests/Handler/Point.html
   sandbox/tschaub/canvas/tests/Handler/Polygon.html
   sandbox/tschaub/canvas/tests/Layer/GML.html
   sandbox/tschaub/canvas/tests/Layer/Vector.html
   sandbox/tschaub/canvas/tests/Map.html
   sandbox/tschaub/canvas/tests/Projection.html
   sandbox/tschaub/canvas/tests/Protocol/HTTP.html
   sandbox/tschaub/canvas/tests/Protocol/WFS.html
   sandbox/tschaub/canvas/tests/Strategy/BBOX.html
   sandbox/tschaub/canvas/tests/list-tests.html
   sandbox/tschaub/canvas/tests/manual/vector-features-performance.html
   sandbox/tschaub/canvas/theme/default/img/editing_tool_bar.png
   sandbox/tschaub/canvas/theme/default/style.css
   sandbox/tschaub/canvas/tools/mergejs.py
Log:
Merge r11553:11762 from trunk.


Property changes on: sandbox/tschaub/canvas
___________________________________________________________________
Modified: svn:mergeinfo
   - /sandbox/roberthl/openlayers:9745-9748
/trunk/openlayers:10737-11416,11422-11552
   + /sandbox/roberthl/openlayers:9745-9748
/trunk/openlayers:10737-11416,11422-11762

Copied: sandbox/tschaub/canvas/examples/arcgiscache_ags.html (from rev 11762, trunk/openlayers/examples/arcgiscache_ags.html)
===================================================================
--- sandbox/tschaub/canvas/examples/arcgiscache_ags.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/arcgiscache_ags.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,219 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>OpenLayers ArcGIS Cache Example (MapServer Access)</title>
+    <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+    <link rel="stylesheet" href="style.css" type="text/css" />
+    <script src='http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAjpkAC9ePGem0lIq5XcMiuhR_wWLPFku8Ix9i2SXYRVK3e45q1BQUd_beF8dtzKET_EteAjPdGDwqpQ'></script>
+    <script src="../lib/OpenLayers.js"></script>
+    <script src="../lib/OpenLayers/Layer/ArcGISCache.js" type="text/javascript"></script>
+    <script type="text/javascript">
+        var map, 
+            cacheLayer,
+            testLayer,
+            //This layer requires meta data about the ArcGIS service.  Typically you should use a 
+            //JSONP call to get this dynamically.  For this example, we are just going to hard-code
+            //an example that we got from here (yes, it's very big):
+            //    http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer?f=json&pretty=true
+            layerInfo = {
+                  "currentVersion" : 10.01, 
+                  "serviceDescription" : "This worldwide street map presents highway-level data for the world and street-level data for the United States, Canada, Japan, Southern Africa, and a number of countries in Europe and elsewhere. This comprehensive street map includes highways, major roads, minor roads, railways, water features, administrative boundaries, cities, parks, and landmarks, overlaid on shaded relief imagery for added context. The street map was developed by ESRI using ESRI basemap data, AND road data, USGS elevation data, and UNEP-WCMC parks and protected areas for the world, and Tele Atlas Dynamap® and Multinet® street data for North America and Europe. Coverage for street-level data in Europe includes Andorra, Austria, Belgium, Czech Republic, Denmark, France, Germany, Great Britain, Greece, Hungary, Ireland, Italy, Luxembourg, Netherlands, Northern Ireland (Belfast only), Norway, Poland, Portugal, San Marino, Slovakia, Spain, Sweden, and Switzerland. Co
 verage for street-level data elsewhere in the world includes China (Hong Kong only), Colombia, Egypt (Cairo only), Indonesia (Jakarta only), Japan, Mexico (Mexico City only), Russia (Moscow and St. Petersburg only), South Africa, Thailand, and Turkey (Istanbul and Ankara only). For more information on this map, visit us \u003ca href=\"http://goto.arcgisonline.com/maps/World_Street_Map \" target=\"_new\"\u003eonline\u003c/a\u003e.", 
+                  "mapName" : "Layers", 
+                  "description" : "This worldwide street map presents highway-level data for the world and street-level data for the United States, Canada, Japan, Southern Africa, most countries in Europe, and several other countries. This comprehensive street map includes highways, major roads, minor roads, one-way arrow indicators, railways, water features, administrative boundaries, cities, parks, and landmarks, overlaid on shaded relief imagery for added context. The map also includes building footprints for selected areas in the United States and Europe and parcel boundaries for much of the lower 48 states.\n\nThe street map was developed by ESRI using ESRI basemap data, DeLorme base map layers, AND road data, USGS elevation data, UNEP-WCMC parks and protected areas for the world, Tele Atlas Dynamap® and Multinet® street data for North America and Europe, and First American parcel data for the United States. Coverage for street-level data in Europe includes Andorra, Aus
 tria, Belgium, Czech Republic, Denmark, France, Germany, Great Britain, Greece, Hungary, Ireland, Italy, Luxembourg, Netherlands, Norway, Poland, Portugal, San Marino, Slovakia, Spain, Sweden, and Switzerland. Coverage for street-level data elsewhere in the world includes China (Hong Kong only), Colombia, Egypt (Cairo only), Indonesia (Jakarta only), Japan, Mexico, Russia, South Africa, Thailand, and Turkey (Istanbul and Ankara only). For more information on this map, visit us online at http://goto.arcgisonline.com/maps/World_Street_Map\n", 
+                  "copyrightText" : "Sources: ESRI, DeLorme, AND, Tele Atlas, First American, ESRI Japan, UNEP-WCMC, USGS, METI, ESRI Hong Kong, ESRI Thailand, Procalculo Prosis", 
+                  "layers" : [
+                    {
+                      "id" : 0, 
+                      "name" : "World Street Map", 
+                      "parentLayerId" : -1, 
+                      "defaultVisibility" : true, 
+                      "subLayerIds" : null, 
+                      "minScale" : 0, 
+                      "maxScale" : 0
+                    }
+                  ], 
+                  "tables" : [
+                    
+                  ], 
+                  "spatialReference" : {
+                    "wkid" : 102100
+                  }, 
+                  "singleFusedMapCache" : true, 
+                  "tileInfo" : {
+                    "rows" : 256, 
+                    "cols" : 256, 
+                    "dpi" : 96, 
+                    "format" : "JPEG", 
+                    "compressionQuality" : 90, 
+                    "origin" : {
+                      "x" : -20037508.342787, 
+                      "y" : 20037508.342787
+                    }, 
+                    "spatialReference" : {
+                      "wkid" : 102100
+                    }, 
+                    "lods" : [
+                      {"level" : 0, "resolution" : 156543.033928, "scale" : 591657527.591555}, 
+                      {"level" : 1, "resolution" : 78271.5169639999, "scale" : 295828763.795777}, 
+                      {"level" : 2, "resolution" : 39135.7584820001, "scale" : 147914381.897889}, 
+                      {"level" : 3, "resolution" : 19567.8792409999, "scale" : 73957190.948944}, 
+                      {"level" : 4, "resolution" : 9783.93962049996, "scale" : 36978595.474472}, 
+                      {"level" : 5, "resolution" : 4891.96981024998, "scale" : 18489297.737236}, 
+                      {"level" : 6, "resolution" : 2445.98490512499, "scale" : 9244648.868618}, 
+                      {"level" : 7, "resolution" : 1222.99245256249, "scale" : 4622324.434309}, 
+                      {"level" : 8, "resolution" : 611.49622628138, "scale" : 2311162.217155}, 
+                      {"level" : 9, "resolution" : 305.748113140558, "scale" : 1155581.108577}, 
+                      {"level" : 10, "resolution" : 152.874056570411, "scale" : 577790.554289}, 
+                      {"level" : 11, "resolution" : 76.4370282850732, "scale" : 288895.277144}, 
+                      {"level" : 12, "resolution" : 38.2185141425366, "scale" : 144447.638572}, 
+                      {"level" : 13, "resolution" : 19.1092570712683, "scale" : 72223.819286}, 
+                      {"level" : 14, "resolution" : 9.55462853563415, "scale" : 36111.909643}, 
+                      {"level" : 15, "resolution" : 4.77731426794937, "scale" : 18055.954822}, 
+                      {"level" : 16, "resolution" : 2.38865713397468, "scale" : 9027.977411}, 
+                      {"level" : 17, "resolution" : 1.19432856685505, "scale" : 4513.988705}
+                    ]
+                  }, 
+                  "initialExtent" : {
+                    "xmin" : -20037507.0671618, 
+                    "ymin" : -20037507.0671618, 
+                    "xmax" : 20037507.0671618, 
+                    "ymax" : 20037507.0671619, 
+                    "spatialReference" : {
+                      "wkid" : 102100
+                    }
+                  }, 
+                  "fullExtent" : {
+                    "xmin" : -20037507.0671618, 
+                    "ymin" : -19971868.8804086, 
+                    "xmax" : 20037507.0671618, 
+                    "ymax" : 19971868.8804086, 
+                    "spatialReference" : {
+                      "wkid" : 102100
+                    }
+                  }, 
+                  "units" : "esriMeters", 
+                  "supportedImageFormatTypes" : "PNG24,PNG,JPG,DIB,TIFF,EMF,PS,PDF,GIF,SVG,SVGZ,AI,BMP", 
+                  "documentInfo" : {
+                    "Title" : "World Street Map", 
+                    "Author" : "ESRI", 
+                    "Comments" : "", 
+                    "Subject" : "streets, highways, major roads, railways, water features, administrative boundaries, cities, parks, protected areas, landmarks ", 
+                    "Category" : "transportation(Transportation Networks) ", 
+                    "Keywords" : "World, Global, 2009, Japan, UNEP-WCMC", 
+                    "Credits" : ""
+                  }, 
+                  "capabilities" : "Map"
+                };
+
+        function init(){
+            //The max extent for spherical mercator
+            var maxExtent = new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34);
+            
+            //Max extent from layerInfo above            
+            var layerMaxExtent = new OpenLayers.Bounds(
+                layerInfo.fullExtent.xmin, 
+                layerInfo.fullExtent.ymin, 
+                layerInfo.fullExtent.xmax, 
+                layerInfo.fullExtent.ymax  
+            );
+            
+            var resolutions = [];
+            for (var i=0; i<layerInfo.tileInfo.lods.length; i++) {
+                resolutions.push(layerInfo.tileInfo.lods[i].resolution);
+            }
+            
+            map = new OpenLayers.Map('map', {
+                maxExtent: maxExtent,
+                StartBounds: layerMaxExtent,
+                units: (layerInfo.units == "esriFeet") ? 'ft' : 'm',
+                resolutions: resolutions,
+                tileSize: new OpenLayers.Size(layerInfo.tileInfo.width, layerInfo.tileInfo.height),                
+                projection: 'EPSG:' + layerInfo.spatialReference.wkid
+            });
+            
+            
+            
+            cacheLayer = new OpenLayers.Layer.ArcGISCache( "AGSCache",
+                    "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer", {
+                        isBaseLayer: true,
+
+                        //From layerInfo above                        
+                        resolutions: resolutions,                        
+                        tileSize: new OpenLayers.Size(layerInfo.tileInfo.cols, layerInfo.tileInfo.rows),                        
+                        tileOrigin: new OpenLayers.LonLat(layerInfo.tileInfo.origin.x , layerInfo.tileInfo.origin.y),                        
+                        maxExtent: layerMaxExtent,                        
+                        projection: 'EPSG:' + layerInfo.spatialReference.wkid
+                    });
+
+            
+            // create Google Mercator layers
+            testLayer = new OpenLayers.Layer.Google(
+                "Google Streets",
+                {'sphericalMercator': true}
+            );
+            
+            map.addLayers([testLayer, cacheLayer]);
+            
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.addControl( new OpenLayers.Control.MousePosition() );
+            
+            map.zoomToExtent(new OpenLayers.Bounds(-8341644, 4711236, -8339198, 4712459));
+        }
+    </script>
+  </head>
+  <body onload="init()">
+      <h1 id="title">OpenLayers ArcGIS Cache Example (MapServer Access)</h1>
+
+    <div id="tags">
+        arcgis, arcgiscache, cache, tms
+    </div>
+
+    <p id="shortdesc">
+        Demonstrates the basic initialization of the ArcGIS Cache layer using a prebuilt configuration, and standard tile access.
+    </p>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        <p>This example demonstrates using the ArcGISCache layer for 
+        accessing ESRI's ArcGIS Server (AGS) Map Cache tiles through 
+        an AGS MapServer.  Toggle the visibility of the AGS layer to
+        demonstrate how the two maps are lined up correctly.</p>
+        
+         <h2>Notes on this layer</h2>
+        <p>A few attempts have been made at this kind of layer before. See 
+        <a href="http://trac.osgeo.org/openlayers/ticket/1967">here</a> and 
+        <a href="http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js">here</a>.
+        A problem the users encounter is that the tiles seem to "jump around".
+        This is due to the fact that the max extent for the cached layer actually
+        changes at each zoom level due to the way these caches are constructed.
+        We have attempted to use the resolutions, tile size, and tile origin
+        from the cache meta data to make the appropriate changes to the max extent
+        of the tile to compensate for this behavior.</p>
+        You will need to know:
+        <ul>
+            <li>Max Extent: The max extent of the layer</li>
+            <li>Resolutions: An array of resolutions, one for each zoom level</li>
+            <li>Tile Origin: The location of the tile origin for the cache in the upper left.</li>
+            <li>Tile Size: The size of each tile in the cache. Commonly 256 x 256</li>
+        </ul>
+        <p>It's important that you set the correct values in your layer, and these
+        values will differ from layer to layer. You can find these values for your 
+        layer in a metadata page in ArcGIS Server. 
+        (ie. <a href="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer">http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer</a>)</p>
+        <ul>
+            <li>Max Extent: Full Extent</li>
+            <li>Resolutions: Tile Info -> Levels of Detail -> Resolution</li>
+            <li>Tile Origin: Origin -> X,Y</li>
+            <li>Tile Size: Tile Info -> Height,Width</li>
+        </ul>
+        
+        <h2> Other Examples </h2>
+        <p>This is one of three examples for this layer.  You can also configure this
+        layer to use <a href="arcgiscache_direct.html">prebuilt tiles in a file store
+         (not a live server).</a> It is also  possible to let this
+          <a href="arcgiscache_jsonp.html">layer 'auto-configure' itself using the
+          capabilities json object from the server itself when using a live ArcGIS server.</a>
+        </p>
+    </div>
+  </body>
+</html>

Copied: sandbox/tschaub/canvas/examples/arcgiscache_direct.html (from rev 11762, trunk/openlayers/examples/arcgiscache_direct.html)
===================================================================
--- sandbox/tschaub/canvas/examples/arcgiscache_direct.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/arcgiscache_direct.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,106 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+    <head>
+        <title>ArcGIS Server Map Cache Example (Direct Access)</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+        <link rel="stylesheet" href="style.css" type="text/css" />
+        <script src="../lib/OpenLayers.js" type="text/javascript"></script>
+        <script src="../lib/OpenLayers/Layer/ArcGISCache.js" type="text/javascript"></script>
+        <script type="text/javascript">
+        /* First 4 variables extracted from conf.xml file */
+        
+            /* Tile layers & map MUST have same projection */
+            var proj='EPSG:26915';
+        
+        
+            /* Layer can also accept serverResolutions array
+             * to deal with situation in which layer resolution array & map resolution
+             * array are out of sync*/
+            var mapResolutions = [33.0729828126323,16.9333672000677,8.46668360003387,4.23334180001693,2.11667090000847,1.05833545000423];
+
+            /* For this example this next line is not really needed, 256x256 is default.
+             * However, you would need to change this if your layer had different tile sizes */
+            var tileSize = new OpenLayers.Size(256,256);
+            
+            /* Tile Origin is required unless it is the same as the implicit map origin
+             * which can be affected by several variables including maxExtent for map or base layer */
+            var agsTileOrigin = new OpenLayers.LonLat(-5120900,9998100);
+            
+            /* This can really be any valid bounds that the map would reasonably be within */
+            /*  var mapExtent = new OpenLayers.Bounds(293449.454286,4307691.661132,314827.830376,4323381.484178); */
+            var mapExtent = new OpenLayers.Bounds(289310.8204,4300021.937,314710.8712,4325421.988);
+            
+            var aerialsUrl = 'http://serverx.esri.com/arcgiscache/dgaerials/Layers/_alllayers';
+            var roadsUrl = 'http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/_alllayers';
+            
+            var map;
+            function init(){
+                map = new OpenLayers.Map('map', {
+                    maxExtent:mapExtent,
+                    controls: [
+                        new OpenLayers.Control.Navigation(),
+                        new OpenLayers.Control.LayerSwitcher(), 
+                        new OpenLayers.Control.PanZoomBar(),
+                        new OpenLayers.Control.MousePosition()]
+                });
+                
+                var baseLayer = new OpenLayers.Layer.ArcGISCache('Aerials', aerialsUrl, {
+                    tileOrigin: agsTileOrigin,
+                    resolutions: mapResolutions,
+                    sphericalMercator: true,
+                    maxExtent: mapExtent,
+                    useArcGISServer: false,
+                    isBaseLayer: true,
+                    type: 'jpg',
+                    projection: proj
+                });
+                var overlayLayer = new OpenLayers.Layer.ArcGISCache('Roads', roadsUrl, {
+                    tileOrigin: agsTileOrigin,
+                    resolutions: mapResolutions,
+                    sphericalMercator: true,
+                    maxExtent: mapExtent,
+                    useArcGISServer: false,
+                    isBaseLayer: false,
+                    projection: proj
+                });
+                map.addLayers([baseLayer, overlayLayer]);
+                
+                //map.zoomToExtent(new OpenLayers.Bounds(295892.34, 4308521.69, 312825.71, 4316988.37));
+                map.zoomToExtent(new OpenLayers.Bounds(-8341644, 4711236, -8339198, 4712459));
+            }
+        </script>
+    </head>
+    <body onload="init()">
+        <h1 id="title">ArcGIS Server Map Cache Example (Direct Access)</h1>
+
+        <div id="tags">
+        </div>
+
+        <p id="shortdesc">
+            Demonstrates the basic initialization of the ArcGIS Cache layer using a prebuilt configuration, and direct tile access from a file store.
+        </p>
+
+        <div id="map" class="smallmap"></div>
+        
+        <div id="docs">
+            <p>This example demonstrates using the ArcGISCache layer for 
+            accessing ESRI's ArcGIS Server (AGS) Map Cache tiles directly 
+            via the folder structure and HTTP.  Toggle the visibility of the AGS layer to
+            demonstrate how the two maps are lined up correctly.</p>
+
+            <h2>Notes on this Layer</h2>
+            <p>It's important that you set the correct values in your layer, and these
+            values will differ between tile sets. You can find these values for your 
+            layer in conf.xml at the root of your cache. 
+            (ie. <a href="http://serverx.esri.com/arcgiscache/dgaerials/Layers/conf.xml">http://serverx.esri.com/arcgiscache/dgaerials/Layers/conf.xml</a>)</p>
+
+            <p>For fused map caches this is often http:<i>ServerName</i>/arcgiscache/<i>MapServiceName</i>/Layers <br />
+            For individual layer caches this is often  http:<i>ServerName</i>/arcgiscache/<i>LayerName</i>/Layers </p>
+            
+            <h2> Other Examples </h2>
+            <p>This is one of three examples for this layer.  You can also configure this
+            layer to use <a href="arcgiscache_ags.html">prebuilt tiles from a live server.</a> It is also
+            possible to let this <a href="arcgiscache_jsonp.html">layer 'auto-configure' itself using the capabilities json object from the server itself when using a live ArcGIS server.</a>
+            </p>
+        </div>
+    </body>
+</html>

Copied: sandbox/tschaub/canvas/examples/arcgiscache_jsonp.html (from rev 11762, trunk/openlayers/examples/arcgiscache_jsonp.html)
===================================================================
--- sandbox/tschaub/canvas/examples/arcgiscache_jsonp.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/arcgiscache_jsonp.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,108 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>OpenLayers ArcGIS Cache Example (Autoconfigure with JSONP)</title>
+    <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
+    <link rel="stylesheet" href="style.css" type="text/css" />
+
+    <script src="../lib/OpenLayers.js"></script>
+    <script src="../lib/OpenLayers/Layer/ArcGISCache.js" type="text/javascript"></script>
+    
+    <!-- This is to simplify making the JSONP request for this example -->
+    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
+    
+    <script type="text/javascript">
+        var map,
+            layerURL = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
+        
+        function init() {
+            var jsonp_url = layerURL + '?f=json&pretty=true&callback=?';
+            $.getJSON(jsonp_url, function(data) {                
+                initMap(data);
+            });
+        }
+
+        function initMap(layerInfo){
+            /*
+             * The initialize function in this layer has the ability to automatically configure
+             * itself if given the JSON capabilities object from the ArcGIS map server.
+             * This hugely simplifies setting up a new layer, and switching basemaps when using this technique.
+             *
+             * see the 'initialize' function in ArcGISCache.js, or 
+             * see the other two ArcGISCache.js examples for direct manual configuration options
+             *
+             */
+            var baseLayer = new OpenLayers.Layer.ArcGISCache("AGSCache", layerURL, {
+                layerInfo: layerInfo
+            });
+            
+            /*
+             * Make sure our baselayer and our map are synced up
+             */
+            map = new OpenLayers.Map('map', { 
+                maxExtent: baseLayer.maxExtent,
+                units: baseLayer.units,
+                resolutions: baseLayer.resolutions,
+                numZoomLevels: baseLayer.numZoomLevels,
+                tileSize: baseLayer.tileSize,
+                displayProjection: baseLayer.displayProjection,
+                StartBounds: baseLayer.initialExtent                
+            });
+            map.addLayers([baseLayer]);
+            
+            
+            //overlay test layer
+            //http://openlayers.org/dev/examples/web-mercator.html
+            var wms = new OpenLayers.Layer.WMS("Highways",
+                "http://sampleserver1.arcgisonline.com/arcgis/services/Specialty/ESRI_StateCityHighway_USA/MapServer/WMSServer", 
+                {layers: "2", format: "image/gif", transparent: "true"}, 
+                { isBaseLayer: false, wrapDateLine: false } 
+            );
+            map.addLayers([wms]);
+
+            
+            
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.addControl(new OpenLayers.Control.MousePosition() );            
+            //map.zoomToExtent(new OpenLayers.Bounds(-8341644, 4711236, -8339198, 4712459));
+            map.zoomToExtent(new OpenLayers.Bounds(-8725663.6225564, 4683718.6735907, -8099491.4868444, 4996804.7414467));
+        }
+    </script>
+  </head>
+  <body onload="init()">
+      <h1 id="title">OpenLayers ArcGIS Cache Example (Autoconfigure with JSONP)</h1>
+
+    <div id="tags">
+        arcgis, arcgiscache, cache, tms, jsonp
+    </div>
+
+    <p id="shortdesc">
+        Demonstrates the basic initialization of the ArcGIS Cache layer by using the server capabilities object.
+    </p>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        <p>This example demonstrates using the ArcGISCache layer for 
+        accessing ESRI's ArcGIS Server (AGS) Map Cache tiles normally through 
+        a live AGS MapServer.  Toggle the visibility of the overlay to
+        demonstrate how the two layers are lined up correctly.</p>
+        
+        <h2>Notes on this Layer</h2>
+        <p>
+        This method automatically configures the layer using the capabilities object 
+        generated by the server itself.  This page shows how to construct the url for the server capabilities object,
+        retrieve it using JSONP (and jQuery), and pass it in during construction.  Note that in this case, 
+        the layer is constructed before the map.  This approach greatly simplifies the
+        configuration of your map, and works best when all your tiles / overlays are similarly laid out.
+        If you are using a live AGS map server for your layer, it can be helpful to check your
+        server configuration using this technique before trying one of the other examples for this layer.  
+        </p>
+        
+        <h2> Other Examples </h2>
+        <p>This is one of three examples for this layer.  You can also configure this
+        layer to use <a href="arcgiscache_direct.html">prebuilt tiles in a file store (not a live server).</a>  
+        As well a retrieve <a href="arcgiscache_ags.html">tiles from a live server.</a> 
+        </p>
+    </div>
+  </body>
+</html>

Modified: sandbox/tschaub/canvas/examples/bing.html
===================================================================
--- sandbox/tschaub/canvas/examples/bing.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/bing.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -17,12 +17,10 @@
             // setting restrictedExtent so that we can use the 
             // VirtualEarth-layers, see e.g. 
             // http://dev.openlayers.org/apidocs/files/OpenLayers/Layer/VirtualEarth-js.html
-            var restrictedExtent = new OpenLayers.Bounds(-20037508, -20037508, 
-                20037508, 20037508);
+            var restrictedExtent = new OpenLayers.Bounds(-180, -90, 
+                180, 90);
             
-            map = new OpenLayers.Map("map", {
-                restrictedExtent: restrictedExtent
-            });
+            map = new OpenLayers.Map("map");
             
             map.addControl(new OpenLayers.Control.LayerSwitcher());
 

Modified: sandbox/tschaub/canvas/examples/canvas.js
===================================================================
--- sandbox/tschaub/canvas/examples/canvas.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/canvas.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -43,9 +43,9 @@
         protocol: new OpenLayers.Protocol.WFS({
             version: "1.1.0",
             srsName: "EPSG:900913",
-            url:  "http://demo.opengeo.org/geoserver/wfs",
+            url:  "http://v2.suite.opengeo.org/geoserver/wfs",
             featureType: "states",
-            featureNS: "http://www.openplans.org/topp"
+            featureNS: "http://usa.opengeo.org"
         }),
         styleMap: styleMap,
         renderers: ["Canvas", "SVG", "VML"]

Copied: sandbox/tschaub/canvas/examples/cross-origin.html (from rev 11762, trunk/openlayers/examples/cross-origin.html)
===================================================================
--- sandbox/tschaub/canvas/examples/cross-origin.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/cross-origin.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>OpenLayers Script Protocol Example</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css">
+        <link rel="stylesheet" href="style.css" type="text/css">
+        <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
+        <meta name="apple-mobile-web-app-capable" content="yes" />
+        <script src="../lib/OpenLayers.js"></script>
+    </head>
+    <body>
+        <h1 id="title">Script Protocol</h1>
+        <div id="tags">
+            protocol, script, cross origin, advanced
+        </div>
+        <p id="shortdesc">
+            Demonstrates the use of a script protocol for making feature requests 
+            cross origin.
+        </p>
+        <div id="map" class="smallmap"></div>
+        <div id="docs">
+            <p>
+                In cases where a service returns serialized features and accepts
+                a named callback (e.g. http://example.com/features.json?callback=foo),
+                the script protocol can be used to read features without being
+                restricted by the same origin policy.
+            </p>
+            <p>
+                View the <a href="cross-origin.js" target="_blank">cross-origin.js</a>
+                source to see how this is done
+            </p>
+        </div>
+        <script src="cross-origin.js"></script>
+    </body>
+</html>

Copied: sandbox/tschaub/canvas/examples/cross-origin.js (from rev 11762, trunk/openlayers/examples/cross-origin.js)
===================================================================
--- sandbox/tschaub/canvas/examples/cross-origin.js	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/cross-origin.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,39 @@
+var map = new OpenLayers.Map({
+    div: "map",
+    layers: [
+        new OpenLayers.Layer.WMS(
+            "World Map",
+            "http://maps.opengeo.org/geowebcache/service/wms",
+            {layers: "bluemarble"}
+        ),
+        new OpenLayers.Layer.Vector("States", {
+            strategies: [new OpenLayers.Strategy.BBOX()],
+            protocol: new OpenLayers.Protocol.Script({
+                url: "http://suite.opengeo.org/geoserver/wfs",
+                callbackKey: "format_options",
+                callbackPrefix: "callback:",
+                params: {
+                    service: "WFS",
+                    version: "1.1.0",
+                    srsName: "EPSG:4326",
+                    request: "GetFeature",
+                    typeName: "world:cities",
+                    outputFormat: "json"
+                },
+                filterToParams: function(filter, params) {
+                    // example to demonstrate BBOX serialization
+                    if (filter.type === OpenLayers.Filter.Spatial.BBOX) {
+                        params.bbox = filter.value.toArray();
+                        if (filter.projection) {
+                            params.bbox.push(filter.projection.getCode());
+                        }
+                    }
+                    return params;
+                }
+            })
+        })
+    ],
+    center: new OpenLayers.LonLat(0, 0),
+    zoom: 1
+});
+

Modified: sandbox/tschaub/canvas/examples/dynamic-text-layer.html
===================================================================
--- sandbox/tschaub/canvas/examples/dynamic-text-layer.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/dynamic-text-layer.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -41,7 +41,13 @@
             // Needed only for interaction, not for the display.
             function onPopupClose(evt) {
                 // 'this' is the popup.
-                selectControl.unselect(this.feature);
+                var feature = this.feature;
+                if (feature.layer) { // The feature is not destroyed
+                    selectControl.unselect(feature);
+                } else { // After "moveend" or "refresh" events on POIs layer all 
+                         //     features have been destroyed by the Strategy.BBOX
+                    this.destroy();
+                }
             }
             function onFeatureSelect(evt) {
                 feature = evt.feature;
@@ -53,7 +59,7 @@
                                          null, true, onPopupClose);
                 feature.popup = popup;
                 popup.feature = feature;
-                map.addPopup(popup);
+                map.addPopup(popup, true);
             }
             function onFeatureUnselect(evt) {
                 feature = evt.feature;

Modified: sandbox/tschaub/canvas/examples/editingtoolbar-outside.html
===================================================================
--- sandbox/tschaub/canvas/examples/editingtoolbar-outside.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/editingtoolbar-outside.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -9,11 +9,16 @@
     <style type="text/css">
         .olControlEditingToolbar  {
             float:left;
-            right: 0px;
-            height: 30px; 
-            width: 150px;
+            width: auto;
         }
     </style>
+    <!--[if lte IE 6]>
+        <style type="text/css">
+            .olControlEditingToolbar {
+                width: 150px;
+            }
+        </style>
+    <![endif]-->
     <script src="../lib/Firebug/firebug.js"></script>
     <script src="../lib/OpenLayers.js"></script>
     <script type="text/javascript">

Modified: sandbox/tschaub/canvas/examples/example-list.html
===================================================================
--- sandbox/tschaub/canvas/examples/example-list.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/example-list.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -101,7 +101,7 @@
             }
             #examples .mainlink {
                 height: 8em;
-                overflow: hidden;
+                overflow: auto;
             }
             #exwin {
                 position: absolute;

Modified: sandbox/tschaub/canvas/examples/fullScreen.js
===================================================================
--- sandbox/tschaub/canvas/examples/fullScreen.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/fullScreen.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -13,18 +13,3 @@
     map.addControl(new OpenLayers.Control.LayerSwitcher());
     map.setCenter(new OpenLayers.LonLat(0, 0), 6);
 }
-var map;
-function init(){
-    map = new OpenLayers.Map('map');
-
-    var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
-        "http://vmap0.tiles.osgeo.org/wms/vmap0",
-        {layers: 'basic'} );
-        var ol_wms_nobuffer = new OpenLayers.Layer.WMS( "OpenLayers WMS (no tile buffer)",
-        "http://vmap0.tiles.osgeo.org/wms/vmap0",
-        {layers: 'basic'}, {buffer: 0});
-
-    map.addLayers([ol_wms, ol_wms_nobuffer]);
-    map.addControl(new OpenLayers.Control.LayerSwitcher());
-    map.setCenter(new OpenLayers.LonLat(0, 0), 6);
-}

Modified: sandbox/tschaub/canvas/examples/geolocation.js
===================================================================
--- sandbox/tschaub/canvas/examples/geolocation.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/geolocation.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -2,7 +2,7 @@
     fillColor: '#000',
     fillOpacity: 0.1,
     strokeWidth: 0
-}
+};
 
 var map = new OpenLayers.Map('map');
 var layer = new OpenLayers.Layer.OSM( "Simple OSM Map");
@@ -24,7 +24,9 @@
         grow = 'up';
 
     var resize = function(){
-        if (count>16) clearInterval(window.resizeInterval);
+        if (count>16) {
+            clearInterval(window.resizeInterval);
+        }
         var interval = radius * 0.03;
         var ratio = interval/radius;
         switch(count) {
@@ -40,7 +42,7 @@
         feature.geometry.resize(1+ratio, point);
         vector.drawFeature(feature);
         count++;
-    }
+    };
     window.resizeInterval = window.setInterval(resize, 50, point, radius);
 };
 

Copied: sandbox/tschaub/canvas/examples/kml-pointtrack.html (from rev 11762, trunk/openlayers/examples/kml-pointtrack.html)
===================================================================
--- sandbox/tschaub/canvas/examples/kml-pointtrack.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/kml-pointtrack.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
+        <meta name="apple-mobile-web-app-capable" content="yes" />
+        <title>OpenLayers KML Track in a PointTrack Layer Example</title>
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css">
+        <link rel="stylesheet" href="../theme/default/google.css" type="text/css">
+        <link rel="stylesheet" href="style.css" type="text/css">
+        <style>
+            .olControlAttribution {
+                bottom: 2px;
+            }
+        </style>
+        <script src="../lib/OpenLayers.js"></script>
+        <script src="kml-pointtrack.js"></script>
+    </head>
+    <body onload="init()">
+        <h1 id="title">Parsing gx:Track in KML</h1>
+        <p id="shortdesc">
+            Demonstrates populating a PointTrack layer with gx:Track elements from KML.
+        </p>
+        <div id="map" class="smallmap"></div>
+        <div id="docs">
+            <p>
+                If a KML document contains <code>&lt;gx:Track&gt;</code> 
+                elements and the extractTracks property is set true on the 
+                parser, features will be created that represent track points.
+                These track points can easily be visualized as track lines with
+                a <code>PointTrack</code> layer, preserving the KML's original
+                styles.
+            </p>
+            <p>
+                View the <a href="kml-pointtrack.js" target="_blank">kml-pointtrack.js</a>
+                source to see how this is done.
+        </div>
+    </body>
+</html>

Copied: sandbox/tschaub/canvas/examples/kml-pointtrack.js (from rev 11762, trunk/openlayers/examples/kml-pointtrack.js)
===================================================================
--- sandbox/tschaub/canvas/examples/kml-pointtrack.js	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/kml-pointtrack.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,51 @@
+var map;
+
+function init() {
+
+    var mercator = new OpenLayers.Projection("EPSG:900913");
+    var geographic = new OpenLayers.Projection("EPSG:4326");
+
+    map = new OpenLayers.Map({
+        div: "map",
+        projection: mercator,
+        layers: [
+            new OpenLayers.Layer.OSM(),
+            new OpenLayers.Layer.PointTrack("Aircraft Tracks", {
+                projection: geographic,
+                strategies: [new OpenLayers.Strategy.Fixed()],
+                protocol: new OpenLayers.Protocol.HTTP({
+                    url: "kml-track.kml",
+                    format: new OpenLayers.Format.KML({
+                        extractTracks: true,
+                        extractStyles: true
+                    })
+                }),
+                dataFrom: OpenLayers.Layer.PointTrack.TARGET_NODE,
+                styleFrom: OpenLayers.Layer.PointTrack.TARGET_NODE,
+                eventListeners: {
+                    "beforefeaturesadded": function(e) {
+                        // group the tracks by fid and create one track for
+                        // every fid
+                        var fid, points = [], feature;
+                        for (var i=0, len=e.features.length; i<len; i++) {
+                            feature = e.features[i];
+                            if (feature.fid !== fid || i === len-1) {
+                                fid = feature.fid;
+                                this.addNodes(points, {silent: true});
+                                points = [];
+                            }
+                            points.push(feature);
+                        }
+                        return false;
+                    }
+                }
+            })
+        ],
+        center: new OpenLayers.LonLat(-93.2735, 44.8349).transform(geographic, mercator),
+        zoom: 8
+    });
+
+    map.addControl(new OpenLayers.Control.LayerSwitcher());
+    
+};
+

Modified: sandbox/tschaub/canvas/examples/marker-shadow.html
===================================================================
--- sandbox/tschaub/canvas/examples/marker-shadow.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/marker-shadow.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -33,6 +33,10 @@
         function init() {
             map = new OpenLayers.Map("map");
             
+            // allow testing of specific renderers via "?renderer=Canvas", etc
+            var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+            renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
             layer = new OpenLayers.Layer.Vector(
                 "Marker Drop Shadows",
                 {
@@ -55,7 +59,8 @@
                         pointRadius: 10
                     }),
                     isBaseLayer: true,
-                    rendererOptions: {yOrdering: true}
+                    rendererOptions: {yOrdering: true},
+                    renderers: renderer
                 }
             );
             

Copied: sandbox/tschaub/canvas/examples/mobile-drawing.html (from rev 11762, trunk/openlayers/examples/mobile-drawing.html)
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-drawing.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/mobile-drawing.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>OpenLayers Mobile Drawing</title>
+        <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0;">
+        <meta name="apple-mobile-web-app-capable" content="yes">
+        <link rel="stylesheet" href="style.mobile.css" type="text/css">
+        <link rel="stylesheet" href="../theme/default/style.css" type="text/css">
+        <script src="../lib/OpenLayers.js"></script>
+        <script src="mobile-drawing.js"></script>
+        <style>
+            html, body {
+                margin: 0;
+                padding: 0;
+                height: 100%;
+            }
+            #map {
+                position: relative;
+                width: 100%;
+                height: 100%;
+            }
+            .olControlAttribution {
+                font-size: 10px;
+                bottom: 5px;
+                right: 5px;
+            }
+            #title, #tags, #shortdesc {
+                display: none;
+            }
+        </style>
+    </head>
+    <body>
+        <h1 id="title">Mobile Drawing Example</h1>
+        <div id="tags">
+            mobile, drawing
+        </div>
+        <p id="shortdesc">
+            A full-screen map with drawing tools for mobile devices.
+        </p>
+        <div id="map"></div>
+        <script>
+            init();
+        </script>
+    </body>
+</html>

Copied: sandbox/tschaub/canvas/examples/mobile-drawing.js (from rev 11762, trunk/openlayers/examples/mobile-drawing.js)
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-drawing.js	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/mobile-drawing.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,55 @@
+function init() {
+
+    // create a vector layer for drawing
+    var vector = new OpenLayers.Layer.Vector();
+
+    // OpenLayers' EditingToolbar internally creates a Navigation control, we
+    // want a TouchNavigation control here so we create our own editing toolbar
+    var toolbar = new OpenLayers.Control.Panel({
+        displayClass: 'olControlEditingToolbar'
+    });
+    toolbar.addControls([
+        // this control is just there to be able to deactivate the drawing
+        // tools
+        new OpenLayers.Control({
+            displayClass: 'olControlNavigation'
+        }),
+        new OpenLayers.Control.DrawFeature(vector, OpenLayers.Handler.Point, {
+            displayClass: 'olControlDrawFeaturePoint'
+        }),
+        new OpenLayers.Control.DrawFeature(vector, OpenLayers.Handler.Path, {
+            displayClass: 'olControlDrawFeaturePath'
+        }),
+        new OpenLayers.Control.DrawFeature(vector, OpenLayers.Handler.Polygon, {
+            displayClass: 'olControlDrawFeaturePolygon'
+        })
+    ]);
+
+    map = new OpenLayers.Map({
+        div: 'map',
+        projection: 'EPSG:900913',
+        units: 'm',
+        numZoomLevels: 18,
+        maxResolution: 156543.0339,
+        maxExtent: new OpenLayers.Bounds(
+            -20037508.34, -20037508.34, 20037508.34, 20037508.34
+        ),
+        controls: [
+            new OpenLayers.Control.TouchNavigation({
+                dragPanOptions: {
+                    interval: 100
+                }
+            }),
+            new OpenLayers.Control.ZoomPanel(),
+            toolbar
+        ],
+        layers: [new OpenLayers.Layer.OSM(), vector],
+        center: new OpenLayers.LonLat(0, 0),
+        zoom: 1,
+        theme: null
+    });
+
+    // activate the first control to render the "navigation icon"
+    // as active
+    toolbar.controls[0].activate();
+};

Modified: sandbox/tschaub/canvas/examples/mobile-jq.js
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-jq.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/mobile-jq.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -47,10 +47,11 @@
 
     var sprintersLayer = new OpenLayers.Layer.Vector("Sprinters", {
         styleMap: new OpenLayers.StyleMap({
-            externalGraphic : "img/mobile-loc.png",
-            graphicOpacity : 1.0,
-            graphicWith:16,
-            graphicHeight:26
+            externalGraphic: "img/mobile-loc.png",
+            graphicOpacity: 1.0,
+            graphicWith: 16,
+            graphicHeight: 26,
+            graphicYOffset: -26
         })
     });
     

Modified: sandbox/tschaub/canvas/examples/multitouch.html
===================================================================
--- sandbox/tschaub/canvas/examples/multitouch.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/multitouch.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -14,6 +14,10 @@
         box.innerHTML = evt.touches.length;
         evt.preventDefault();
     });    
+    box.addEventListener("touchmove", function(evt) {
+        box.innerHTML = evt.touches.length;
+        evt.preventDefault();
+    });    
     if (!(typeof box.ontouchstart != 'undefined')) { 
         box.style.backgroundColor = "red";
     }    

Modified: sandbox/tschaub/canvas/examples/navtoolbar-alwaysZoom.html
===================================================================
--- sandbox/tschaub/canvas/examples/navtoolbar-alwaysZoom.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/navtoolbar-alwaysZoom.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -7,7 +7,7 @@
 	    <link rel="stylesheet" href="style.css" type="text/css" />
 		<!-- Override the position of the toolbar to make it fit in a small map -->
 		<style type='text/css'>     
-			.olControlNavToolbar div { 
+			.olControlNavToolbar {
 			  top: 150px;
 			}
     	</style>  
@@ -49,7 +49,7 @@
 				        var div = OpenLayers.Control.Panel.prototype.draw.apply(this, arguments);
 				        this.activateControl(this.controls[0]);
 				        return div;
-				    },
+				    }
 				});
 				
 				var map;

Modified: sandbox/tschaub/canvas/examples/navtoolbar-outsidemap.html
===================================================================
--- sandbox/tschaub/canvas/examples/navtoolbar-outsidemap.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/navtoolbar-outsidemap.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -5,15 +5,6 @@
     <title>OpenLayers: Custom Navigation Toolbar</title>
     <link rel="stylesheet" href="../theme/default/style.css" type="text/css" />
     <link rel="stylesheet" href="style.css" type="text/css" />
-    <style type="text/css">
-        #paneldiv {
-            height: 80px;
-        }
-        #paneldiv div { 
-            top: 10px; 
-        }
-        
-    </style>
     <script src="../lib/OpenLayers.js"></script>
     <script type="text/javascript">
         var lon = 5;

Modified: sandbox/tschaub/canvas/examples/ordering.html
===================================================================
--- sandbox/tschaub/canvas/examples/ordering.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/ordering.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -32,6 +32,10 @@
         function initYOrderMap() {
             var map = new OpenLayers.Map("yorder");
             
+            // allow testing of specific renderers via "?renderer=Canvas", etc
+            var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+            renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
             var layer = new OpenLayers.Layer.Vector(
                 "Y-Order",
                 {
@@ -41,7 +45,8 @@
                         graphicZIndex: GOLD_Z_INDEX
                     }),
                     isBaseLayer: true,
-                    rendererOptions: {yOrdering: true}
+                    rendererOptions: {yOrdering: true},
+                    renderers: renderer
                 }
             );
             

Modified: sandbox/tschaub/canvas/examples/restricted-extent.html
===================================================================
--- sandbox/tschaub/canvas/examples/restricted-extent.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/restricted-extent.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -9,7 +9,7 @@
     <script src="../lib/OpenLayers.js"></script>
     <script type="text/javascript">
         var map = null;
-        var extent = new OpenLayers.Bounds(-180, -90, 180, 90);
+        var extent = new OpenLayers.Bounds(8, 44.5, 19, 50);
 
         function init() {
             var options = {
@@ -24,7 +24,7 @@
             ); 
 
             map.addLayers([wms]);
-            map.setCenter(extent, 1);
+            map.zoomToExtent(extent);
             document.getElementById("toggle").checked = true;
         }
         
@@ -68,8 +68,8 @@
         <input type="checkbox" id="toggle" checked="checked"
                onclick="toggleRestrictedExtent();" />
         <label for="toggle">
-            Toggle restricted extent (to [-180, -90, 180, 90]).
+            Toggle restricted extent (to [8, 44.5, 19, 50]).
         </label>
     
   </body>
-</html>
+</html>
\ No newline at end of file

Modified: sandbox/tschaub/canvas/examples/snap-split.html
===================================================================
--- sandbox/tschaub/canvas/examples/snap-split.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/snap-split.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -7,10 +7,10 @@
     <link rel="stylesheet" href="style.css" type="text/css" />
     <style type="text/css">
         .olControlEditingToolbar .olControlModifyFeatureItemInactive { 
-            background-position: -1px 0px ;                                                                   
+            background-position: -1px -1px;
         }
         .olControlEditingToolbar .olControlModifyFeatureItemActive { 
-            background-position: -1px -23px ;                                                                   
+            background-position: -1px -24px;
         }
         label.head {
             font-weight: bold;
@@ -92,6 +92,10 @@
                 })
             });
 
+            // allow testing of specific renderers via "?renderer=Canvas", etc
+            var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+            renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
             // create three vector layers
             vectors = new OpenLayers.Layer.Vector("Lines", {
                 isBaseLayer: true,
@@ -103,7 +107,8 @@
                 styleMap: styles,
                 maxExtent: new OpenLayers.Bounds(
                     1549471.9221, 6403610.94, 1550001.32545, 6404015.8
-                )
+                ),
+                renderers: renderer
             });
             map.addLayer(vectors);
             

Modified: sandbox/tschaub/canvas/examples/snapping.html
===================================================================
--- sandbox/tschaub/canvas/examples/snapping.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/snapping.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -7,10 +7,10 @@
     <link rel="stylesheet" href="style.css" type="text/css" />
     <style type="text/css">
         .olControlEditingToolbar .olControlModifyFeatureItemInactive { 
-            background-position: -1px 0px ;                                                                   
+            background-position: -1px -1px;
         }
         .olControlEditingToolbar .olControlModifyFeatureItemActive { 
-            background-position: -1px -23px ;                                                                   
+            background-position: -1px -24px;
         }
         table {
             padding: 1em 0 1em;

Modified: sandbox/tschaub/canvas/examples/spherical-mercator.html
===================================================================
--- sandbox/tschaub/canvas/examples/spherical-mercator.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/spherical-mercator.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -32,7 +32,7 @@
 
 function init(){
     var maxExtent = new OpenLayers.Bounds(-20037508, -20037508, 20037508, 20037508),
-        restrictedExtent = maxExtent.clone(-20037508, -20037508, 20037508, 20037508),
+        restrictedExtent = maxExtent.clone(),
         maxResolution = 156543.0339;
     
     var options = {

Modified: sandbox/tschaub/canvas/examples/split-feature.html
===================================================================
--- sandbox/tschaub/canvas/examples/split-feature.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/split-feature.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -68,9 +68,10 @@
                     }
                 });
                 map.addControl(split);
-                split.activate();
+
                 map.zoomToMaxExtent();
 
+                split.activate();
             }
             
             function flashFeatures(features, index) {

Modified: sandbox/tschaub/canvas/examples/symbolizers-fill-stroke-graphic.html
===================================================================
--- sandbox/tschaub/canvas/examples/symbolizers-fill-stroke-graphic.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/symbolizers-fill-stroke-graphic.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -28,7 +28,8 @@
                                 symbolizer: {
                                     graphic: false,
                                     label: "Label for invisible point",
-                                    labelSelect: true
+                                    labelSelect: true,
+                                    fontStyle: "italic"
                                 },
                                 filter: new OpenLayers.Filter.Comparison({
                                     type: "==",

Modified: sandbox/tschaub/canvas/examples/vector-features.html
===================================================================
--- sandbox/tschaub/canvas/examples/vector-features.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/vector-features.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -16,6 +16,10 @@
                     "http://vmap0.tiles.osgeo.org/wms/vmap0", {layers: 'basic'} );
             map.addLayer(layer);
 
+            // allow testing of specific renderers via "?renderer=Canvas", etc
+            var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+            renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
             /*
              * Layer style
              */
@@ -68,7 +72,10 @@
             // graphicTitle only works in Firefox and Internet Explorer
             style_mark.graphicTitle = "this is a test tooltip";
 
-            var vectorLayer = new OpenLayers.Layer.Vector("Simple Geometry", {style: layer_style});
+            var vectorLayer = new OpenLayers.Layer.Vector("Simple Geometry", {
+                style: layer_style,
+                renderers: renderer
+            });
 
             // create a point feature
             var point = new OpenLayers.Geometry.Point(-111.04, 45.68);

Modified: sandbox/tschaub/canvas/examples/wfs-states.js
===================================================================
--- sandbox/tschaub/canvas/examples/wfs-states.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/examples/wfs-states.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -10,13 +10,18 @@
     );
     map.addLayer(base);
 
+    // allow testing of specific renderers via "?renderer=Canvas", etc
+    var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+    renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
     var wfs = new OpenLayers.Layer.Vector("States", {
         strategies: [new OpenLayers.Strategy.BBOX()],
         protocol: new OpenLayers.Protocol.WFS({
             url: "http://demo.opengeo.org/geoserver/wfs",
             featureType: "states",
             featureNS: "http://www.openplans.org/topp"
-        })
+        }),
+        renderers: renderer
     });
     map.addLayer(wfs);
 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Bounds.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Bounds.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Bounds.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -121,11 +121,9 @@
      * 
      * Returns:
      * {String} String representation of bounds object. 
-     *          (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
      */
     toString:function() {
-        return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
-                 + " right-top=(" + this.right + "," + this.top + ")" );
+        return [this.left, this.bottom, this.right, this.top].join(",");
     },
 
     /**
@@ -260,7 +258,7 @@
      *          Default is center.
      *
      * Returns:
-     * {<OpenLayers.Bound>} A new bounds that is scaled by ratio
+     * {<OpenLayers.Bounds>} A new bounds that is scaled by ratio
      *                      from origin.
      */
 
@@ -560,6 +558,8 @@
      * Parameters:
      * maxExtent - {<OpenLayers.Bounds>}
      * options - {Object} Some possible options are:
+     *
+     * Allowed Options:
      *                    leftTolerance - {float} Allow for a margin of error 
      *                                            with the 'left' value of this 
      *                                            bound.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Element.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Element.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes/Element.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -45,12 +45,16 @@
 
     /**
      * APIFunction: hide
-     * Hide element(s) passed in
+     * *Deprecated*. Hide element(s) passed in
      * 
      * Parameters:
      * element - {DOMElement} Actually user can pass any number of elements
      */
     hide: function() {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
+            newMethod: "element.style.display = 'none';"
+        }));
+
         for (var i=0, len=arguments.length; i<len; i++) {
             var element = OpenLayers.Util.getElement(arguments[i]);
             if (element) {
@@ -61,12 +65,16 @@
 
     /**
      * APIFunction: show
-     * Show element(s) passed in
+     * *Deprecated*. Show element(s) passed in
      * 
      * Parameters:
      * element - {DOMElement} Actually user can pass any number of elements
      */
     show: function() {
+        OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated", {
+            newMethod: "element.style.display = '';"
+        }));
+
         for (var i=0, len=arguments.length; i<len; i++) {
             var element = OpenLayers.Util.getElement(arguments[i]);
             if (element) {

Modified: sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/BaseTypes.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -25,7 +25,7 @@
      * 
      * Parameters:
      * str - {String} The string to test.
-     * sub - {Sring} The substring to look for.
+     * sub - {String} The substring to look for.
      *  
      * Returns:
      * {Boolean} The first string starts with the second.
@@ -204,7 +204,7 @@
      * *Deprecated*. Whether or not a string starts with another string. 
      * 
      * Parameters:
-     * sStart - {Sring} The string we're testing for.
+     * sStart - {String} The string we're testing for.
      *  
      * Returns:
      * {Boolean} Whether or not this string starts with the string passed in.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Console.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Console.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Console.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -101,7 +101,7 @@
      * Expects a single error message
      * 
      * Parameters:
-     * object - {Object}
+     * error - {Object}
      */
     userError: function(error) {
         alert(error);

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/ArgParser.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/ArgParser.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/ArgParser.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -60,10 +60,10 @@
      * Parameters:
      * options - {Object}
      */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
 
+    /**
+     * Method: getParameters
+     */    
     getParameters: function(url) {
         url = url || window.location.href;
         var parameters = OpenLayers.Util.getParameters(url);

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/Attribution.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/Attribution.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/Attribution.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -30,9 +30,6 @@
      * Parameters:
      * options - {Object} Options for control.
      */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
 
     /** 
      * Method: destroy

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/KeyboardDefaults.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/KeyboardDefaults.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/KeyboardDefaults.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -37,22 +37,7 @@
     /**
      * Constructor: OpenLayers.Control.KeyboardDefaults
      */
-    initialize: function() {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
-    
-    /**
-     * APIMethod: destroy
-     */
-    destroy: function() {
-        if (this.handler) {
-            this.handler.destroy();
-        }        
-        this.handler = null;
         
-        OpenLayers.Control.prototype.destroy.apply(this, arguments);
-    },
-    
     /**
      * Method: draw
      * Create handler.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/MousePosition.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/MousePosition.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/MousePosition.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -87,9 +87,6 @@
      * Parameters:
      * options - {Object} Options for control.
      */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, arguments);
-    },
 
     /**
      * Method: destroy

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/Navigation.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/Navigation.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/Navigation.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -32,7 +32,7 @@
     dragPan: null,
 
     /**
-     * APIProprety: dragPanOptions
+     * APIProperty: dragPanOptions
      * {Object} Options passed to the DragPan control.
      */
     dragPanOptions: null,

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoom.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoom.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoom.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -64,10 +64,10 @@
      * APIMethod: destroy
      */
     destroy: function() {
-        OpenLayers.Control.prototype.destroy.apply(this, arguments);
         this.removeButtons();
         this.buttons = null;
         this.position = null;
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);
     },
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoomBar.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoomBar.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/PanZoomBar.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -60,6 +60,14 @@
     zoomWorldIcon: false,
 
     /**
+     * APIProperty: panIcons
+     * {Boolean} Set this property to false not to display the pan icons. If
+     * false the zoom world icon is placed under the zoom bar. Defaults to
+     * true.
+     */
+    panIcons: true,
+
+    /**
      * APIProperty: forceFixedZoomLevel
      * {Boolean} Force a fixed zoom level even though the map has 
      *     fractionalZoom
@@ -87,9 +95,6 @@
     /**
      * Constructor: OpenLayers.Control.PanZoomBar
      */ 
-    initialize: function() {
-        OpenLayers.Control.PanZoom.prototype.initialize.apply(this, arguments);
-    },
 
     /**
      * APIMethod: destroy
@@ -147,26 +152,37 @@
         this.buttons = [];
 
         var sz = new OpenLayers.Size(18,18);
-        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
-        var wposition = sz.w;
+        if (this.panIcons) {
+            var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);
+            var wposition = sz.w;
 
-        if (this.zoomWorldIcon) {
-            centered = new OpenLayers.Pixel(px.x+sz.w, px.y);
-        }
+            if (this.zoomWorldIcon) {
+                centered = new OpenLayers.Pixel(px.x+sz.w, px.y);
+            }
 
-        this._addButton("panup", "north-mini.png", centered, sz);
-        px.y = centered.y+sz.h;
-        this._addButton("panleft", "west-mini.png", px, sz);
-        if (this.zoomWorldIcon) {
-            this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
-            
-            wposition *= 2;
+            this._addButton("panup", "north-mini.png", centered, sz);
+            px.y = centered.y+sz.h;
+            this._addButton("panleft", "west-mini.png", px, sz);
+            if (this.zoomWorldIcon) {
+                this._addButton("zoomworld", "zoom-world-mini.png", px.add(sz.w, 0), sz);
+
+                wposition *= 2;
+            }
+            this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
+            this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
+            this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
+            centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
+            this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
         }
-        this._addButton("panright", "east-mini.png", px.add(wposition, 0), sz);
-        this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
-        this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3+5), sz);
-        centered = this._addZoomBar(centered.add(0, sz.h*4 + 5));
-        this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
+        else {
+            this._addButton("zoomin", "zoom-plus-mini.png", px, sz);
+            centered = this._addZoomBar(px.add(0, sz.h));
+            this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
+            if (this.zoomWorldIcon) {
+                centered = centered.add(0, sz.h+3);
+                this._addButton("zoomworld", "zoom-world-mini.png", centered, sz);
+            }
+        }
         return this.div;
     },
 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/PinchZoom.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/PinchZoom.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/PinchZoom.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  * full list of contributors). Published under the Clear BSD license.
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -124,6 +124,7 @@
      */
     pinchStart: function(evt, pinchData) {
         this.pinchOrigin = evt.xy;
+        this.currentCenter = evt.xy;
     },
     
     /**
@@ -170,21 +171,20 @@
      *     of the pinch gesture. This give us the final scale of the pinch.
      */
     pinchDone: function(evt, start, last) {
+        this.applyTransform("");
         var zoom = this.map.getZoomForResolution(this.map.getResolution() / last.scale, true);
-        var resolution = this.map.getResolutionForZoom(zoom);
+        if (zoom !== this.map.getZoom() || !this.currentCenter.equals(this.pinchOrigin)) {
+            var resolution = this.map.getResolutionForZoom(zoom);
 
-        var location = this.map.getLonLatFromPixel(this.pinchOrigin);
-        var zoomPixel = this.currentCenter;        
-        var size = this.map.getSize();
-        
-        location.lon += resolution * ((size.w / 2) - zoomPixel.x);
-        location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
+            var location = this.map.getLonLatFromPixel(this.pinchOrigin);
+            var zoomPixel = this.currentCenter;        
+            var size = this.map.getSize();
 
-        this.map.setCenter(location, zoom);
+            location.lon += resolution * ((size.w / 2) - zoomPixel.x);
+            location.lat -= resolution * ((size.h / 2) - zoomPixel.y);
 
-        var style = this.map.layerContainerDiv.style;
-        style['-webkit-transform'] = "";
-        style['-moz-transform'] = "";
+            this.map.setCenter(location, zoom);
+        }
     },
 
     CLASS_NAME: "OpenLayers.Control.PinchZoom"

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/SLDSelect.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/SLDSelect.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/SLDSelect.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -11,6 +11,7 @@
  * @requires OpenLayers/Handler/Path.js
  * @requires OpenLayers/Handler/Click.js
  * @requires OpenLayers/Filter/Spatial.js
+ * @requires OpenLayers/Format/SLD/v1_0_0.js
  */
 
 /**
@@ -272,7 +273,7 @@
                     maxScaleDenominator: layer.options.minScale})
             ]});
         }
-        return new OpenLayers.Format.SLD().write(sld);
+        return new OpenLayers.Format.SLD({srsName: this.map.getProjection()}).write(sld);
     },
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/ScaleLine.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/ScaleLine.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/ScaleLine.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -80,9 +80,6 @@
      * options - {Object} An optional object whose properties will be used
      *     to extend the control.
      */
-    initialize: function(options) {
-        OpenLayers.Control.prototype.initialize.apply(this, [options]);     
-    },
 
     /**
      * Method: draw

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/SelectFeature.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/SelectFeature.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/SelectFeature.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -132,7 +132,7 @@
     
     /**
      * Property: layers
-     * {Array(<OpenLayers.Layer.Vector>} The layers this control will work on,
+     * {Array(<OpenLayers.Layer.Vector>)} The layers this control will work on,
      * or null if the control was configured with a single layer
      */
     layers: null,
@@ -463,8 +463,23 @@
      */
     unhighlight: function(feature) {
         var layer = feature.layer;
-        feature._lastHighlighter = feature._prevHighlighter;
-        delete feature._prevHighlighter;
+        // three cases:
+        // 1. there's no other highlighter, in that case _prev is undefined,
+        //    and we just need to undef _last
+        // 2. another control highlighted the feature after we did it, in
+        //    that case _last references this other control, and we just
+        //    need to undef _prev
+        // 3. another control highlighted the feature before we did it, in
+        //    that case _prev references this other control, and we need to
+        //    set _last to _prev and undef _prev
+        if(feature._prevHighlighter == undefined) {
+            delete feature._lastHighlighter;
+        } else if(feature._prevHighlighter == this.id) {
+            delete feature._prevHighlighter;
+        } else {
+            feature._lastHighlighter = feature._prevHighlighter;
+            delete feature._prevHighlighter;
+        }
         layer.drawFeature(feature, feature.style || feature.layer.style ||
             "default");
         this.events.triggerEvent("featureunhighlighted", {feature : feature});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/Snapping.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/Snapping.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/Snapping.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -372,7 +372,7 @@
      * Method: considerSnapping
      *
      * Parameters:
-     * point - {<OpenLayers.Geometry.Point}} The vertex to be snapped (or
+     * point - {<OpenLayers.Geometry.Point>} The vertex to be snapped (or
      *     unsnapped).
      * loc - {<OpenLayers.Geometry.Point>} The location of the mouse in map
      *     coords.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/Split.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/Split.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/Split.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -297,7 +297,7 @@
      * Remove a feature from a list based on the given geometry.
      *
      * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>} A list of features.
+     * features - {Array(<OpenLayers.Feature.Vector>)} A list of features.
      * geometry - {<OpenLayers.Geometry>} A geometry.
      */
     removeByGeometry: function(features, geometry) {
@@ -340,7 +340,7 @@
      *     will be split if eligible.
      *
      * Parameters:
-     * feature - {<OpenLayers.Feature.Vector}} The newly created or modified
+     * feature - {<OpenLayers.Feature.Vector>} The newly created or modified
      *     feature.
      *
      * Returns:

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/TouchNavigation.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/TouchNavigation.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/TouchNavigation.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for
  * full list of contributors). Published under the Clear BSD license.
  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
  * full text of the license. */
@@ -27,7 +27,7 @@
     dragPan: null,
 
     /**
-     * APIProprety: dragPanOptions
+     * APIProperty: dragPanOptions
      * {Object} Options passed to the DragPan control.
      */
     dragPanOptions: null,
@@ -39,12 +39,18 @@
     pinchZoom: null,
 
     /**
-     * APIProprety: pinchZoomOptions
+     * APIProperty: pinchZoomOptions
      * {Object} Options passed to the PinchZoom control.
      */
     pinchZoomOptions: null,
 
     /**
+     * APIProperty: clickHandlerOptions
+     * {Object} Options passed to the Click handler.
+     */
+    clickHandlerOptions: null,
+
+    /**
      * APIProperty: documentDrag
      * {Boolean} Allow panning of the map by dragging outside map viewport.
      *     Default is false.
@@ -121,13 +127,14 @@
      */
     draw: function() {
         var clickCallbacks = {
-            'click': this.defaultClick,
-            'dblclick': this.defaultDblClick
+            click: this.defaultClick,
+            dblclick: this.defaultDblClick
         };
-        var clickOptions = {
-            'double': true,
-            'stopDouble': true
-        };
+        var clickOptions = OpenLayers.Util.extend({
+            "double": true,
+            stopDouble: true,
+            pixelTolerance: 2
+        }, this.clickHandlerOptions);
         this.handlers.click = new OpenLayers.Handler.Click(
             this, clickCallbacks, clickOptions
         );

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/WMSGetFeatureInfo.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/WMSGetFeatureInfo.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/WMSGetFeatureInfo.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -88,7 +88,10 @@
 
     /**
      * Property: infoFormat
-     * {String} The mimetype to request from the server
+     * {String} The mimetype to request from the server. If you are using 
+     * drillDown mode and have multiple servers that do not share a common 
+     * infoFormat, you can override the control's infoFormat by providing an 
+     * INFO_FORMAT parameter in your <OpenLayers.Layer.WMS> instance(s).
      */
     infoFormat: 'text/html',
     
@@ -366,7 +369,7 @@
             height: this.map.getSize().h,
             width: this.map.getSize().w,
             format: format,
-            info_format: this.infoFormat
+            info_format: firstLayer.params.INFO_FORMAT || this.infoFormat
         }, (parseFloat(firstLayer.params.VERSION) >= 1.3) ?
             {
                 crs: projection,

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -71,13 +71,14 @@
     map: null,
 
     /** 
-     * Property: div 
-     * {DOMElement} 
+     * APIProperty: div 
+     * {DOMElement} The element that contains the control, if not present the 
+     *     control is placed inside the map.
      */
     div: null,
 
     /** 
-     * Property: type 
+     * APIProperty: type 
      * {Number} Controls can have a 'type'. The type determines the type of
      * interactions which are possible with them when they are placed in an
      * <OpenLayers.Control.Panel>. 
@@ -101,7 +102,7 @@
     displayClass: "",
     
     /**
-    * Property: title  
+    * APIProperty: title  
     * {string}  This property is used for showing a tooltip over the  
     * Control.  
     */ 
@@ -136,9 +137,9 @@
     eventListeners: null,
 
     /** 
-     * Property: events
-     * {<OpenLayers.Events>} Events instance for triggering control specific
-     *     events.
+     * APIProperty: events
+     * {<OpenLayers.Events>} Events instance for listeners and triggering
+     *     control specific events.
      */
     events: null,
 
@@ -228,6 +229,7 @@
             this.map.removeControl(this);
             this.map = null;
         }
+        this.div = null;
     },
 
     /** 
@@ -296,7 +298,7 @@
     },
 
     /**
-     * Method: activate
+     * APIMethod: activate
      * Explicitly activates a control and it's associated
      * handler if one has been set.  Controls can be
      * deactivated by calling the deactivate() method.
@@ -324,7 +326,7 @@
     },
     
     /**
-     * Method: deactivate
+     * APIMethod: deactivate
      * Deactivates a control and it's associated handler if any.  The exact
      * effect of this depends on the control itself.
      * 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Events.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Events.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Events.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -604,6 +604,9 @@
      * events.register("loadstart", object, loadStartListener);
      * events.register("loadstart", object, loadEndListener);
      * (end)
+     *
+     * Parameters:
+     *  object - {Object}     
      */
     on: function(object) {
         for(var type in object) {
@@ -851,7 +854,17 @@
     clearMouseCache: function() { 
         this.element.scrolls = null;
         this.element.lefttop = null;
-        this.element.offsets = null;
+        // OpenLayers.Util.pagePosition needs to use
+        // element.getBoundingClientRect to correctly calculate the offsets
+        // for the iPhone, but once the page is scrolled, getBoundingClientRect
+        // returns incorrect offsets. So our best bet is to not invalidate the
+        // offsets once we have them, and hope that the page was not scrolled
+        // when we did the initial calculation.
+        var body = document.body;
+        if (body && !((body.scrollTop != 0 || body.scrollLeft != 0) &&
+                                    navigator.userAgent.match(/iPhone/i))) {
+            this.element.offsets = null;
+        }
     },      
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -387,6 +387,7 @@
  * fontOpacity - {Number} Opacity (0-1) for the label
  * fontFamily - {String} The font family for the label, to be provided like in CSS.
  * fontSize - {String} The font size for the label, to be provided like in CSS.
+ * fontStyle - {String} The font style for the label, to be provided like in CSS.
  * fontWeight - {String} The font weight for the label, to be provided like in CSS.
  * display - {String} Symbolizers will have no effect if display is set to "none".  All other values have no effect.
  */ 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Filter/FeatureId.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Filter/FeatureId.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Filter/FeatureId.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -26,6 +26,12 @@
     fids: null,
     
     /** 
+     * Property: type
+     * {String} Type to identify this filter.
+     */
+    type: "FID",
+    
+    /** 
      * Constructor: OpenLayers.Filter.FeatureId
      * Creates an ogc:FeatureId rule.
      *

Copied: sandbox/tschaub/canvas/lib/OpenLayers/Filter/Function.js (from rev 11762, trunk/openlayers/lib/OpenLayers/Filter/Function.js)
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Filter/Function.js	                        (rev 0)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Filter/Function.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,52 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Filter.js
+ */
+
+/**
+ * Class: OpenLayers.Filter.Function
+ * This class represents a filter function.
+ * We are using this class for creation of complex 
+ * filters that can contain filter functions as values.
+ * Nesting function as other functions parameter is supported.
+ * 
+ * Inherits from
+ * - <OpenLayers.Filter>
+ */
+OpenLayers.Filter.Function = OpenLayers.Class(OpenLayers.Filter, {
+
+    /**
+     * APIProperty: name
+     * {String} Name of the function.
+     */
+    name: null,
+    
+    /**
+     * APIProperty: params
+     * {Array(<OpenLayers.Filter.Function> || String || Number)} Function parameters
+     * For now support only other Functions, String or Number
+     */
+    params: null,  
+    
+    /** 
+     * Constructor: OpenLayers.Filter.Function
+     * Creates a filter function.
+     *
+     * Parameters:
+     * options - {Object} An optional object with properties to set on the
+     *     function.
+     * 
+     * Returns:
+     * {<OpenLayers.Filter.Function>}
+     */
+    initialize: function(options) {
+        OpenLayers.Filter.prototype.initialize.apply(this, [options]);
+    },
+
+    CLASS_NAME: "OpenLayers.Filter.Function"
+});
+

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Atom.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Atom.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Atom.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -67,9 +67,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
     
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Context.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Context.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Context.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -269,9 +269,14 @@
      */
     contextToMap: function(context, options) {
         options = OpenLayers.Util.applyDefaults({
-            maxExtent: context.maxExtent,
-            projection: context.projection
+            maxExtent:  context.maxExtent,
+            projection: context.projection,
+            units:      context.units
         }, options);
+        if (options.maxExtent) {
+            options.maxResolution = 
+                options.maxExtent.getWidth() / OpenLayers.Map.TILE_WIDTH;
+        }
         var map = new OpenLayers.Map(options);
         map.addLayers(this.getLayersFromContext(context.layersContext));
         map.setCenter(

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -186,6 +186,10 @@
             "Distance": function(node, obj) {
                 obj.distance = parseInt(this.getChildValue(node));
                 obj.distanceUnits = node.getAttribute("units");
+            },
+            "Function": function(node, obj) {
+                //TODO write decoder for it
+                return;
             }
         }
     },
@@ -234,6 +238,28 @@
     },
 
     /**
+     * Method: writeOgcExpression
+     * Limited support for writing OGC expressions. Currently it supports
+     * (<OpenLayers.Filter.Function> || String || Number)
+     *
+     * Parameters:
+     * value - (<OpenLayers.Filter.Function> || String || Number)
+     * node - {DOMElement} A parent DOM element 
+     *
+     * Returns:
+     * {DOMElement} Updated node element.
+     */
+    writeOgcExpression: function(value, node) {
+        if(value instanceof OpenLayers.Filter.Function){
+            var child = this.writeNode("Function", value, node);
+            node.appendChild(child);
+        } else {
+            this.writeNode("Literal", value, node);
+        }
+        return node;
+    },    
+    
+    /**
      * Method: write
      *
      * Parameters:
@@ -247,6 +273,19 @@
     },
     
     /**
+     * Method: writeFeatureIdNodes
+     * 
+     * Parameters:
+     * filter - {<OpenLayers.Filter.FeatureId}
+     * node - {DOMElement}
+     */
+    writeFeatureIdNodes: function(filter, node) {
+        for (var i=0, ii=filter.fids.length; i<ii; ++i) {
+            this.writeNode("FeatureId", filter.fids[i], node);
+        }
+    },
+    
+    /**
      * Property: writers
      * As a compliment to the readers property, this structure contains public
      *     writing functions grouped by namespace alias and named like the
@@ -256,11 +295,8 @@
         "ogc": {
             "Filter": function(filter) {
                 var node = this.createElementNSPlus("ogc:Filter");
-                var sub = filter.CLASS_NAME.split(".").pop();
-                if(sub == "FeatureId") {
-                    for(var i=0; i<filter.fids.length; ++i) {
-                        this.writeNode("FeatureId", filter.fids[i], node);
-                    }
+                if (filter.type === "FID") {
+                    this.writeFeatureIdNodes(filter, node);
                 } else {
                     this.writeNode(this.getFilterType(filter), filter, node);
                 }
@@ -274,64 +310,80 @@
             "And": function(filter) {
                 var node = this.createElementNSPlus("ogc:And");
                 var childFilter;
-                for(var i=0; i<filter.filters.length; ++i) {
+                for (var i=0, ii=filter.filters.length; i<ii; ++i) {
                     childFilter = filter.filters[i];
+                    if (childFilter.type === "FID") {
+                        this.writeFeatureIdNodes(childFilter, node);
+                    } else {
                     this.writeNode(
                         this.getFilterType(childFilter), childFilter, node
                     );
                 }
+                }
                 return node;
             },
             "Or": function(filter) {
                 var node = this.createElementNSPlus("ogc:Or");
                 var childFilter;
-                for(var i=0; i<filter.filters.length; ++i) {
+                for (var i=0, ii=filter.filters.length; i<ii; ++i) {
                     childFilter = filter.filters[i];
+                    if (childFilter.type === "FID") {
+                        this.writeFeatureIdNodes(childFilter, node);
+                    } else {
                     this.writeNode(
                         this.getFilterType(childFilter), childFilter, node
                     );
                 }
+                }
                 return node;
             },
             "Not": function(filter) {
                 var node = this.createElementNSPlus("ogc:Not");
                 var childFilter = filter.filters[0];
+                if (childFilter.type === "FID") {
+                    this.writeFeatureIdNodes(childFilter, node);
+                } else {
                 this.writeNode(
                     this.getFilterType(childFilter), childFilter, node
                 );
+                }
                 return node;
             },
             "PropertyIsLessThan": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsLessThan");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);                
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsGreaterThan": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThan");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsLessThanOrEqualTo": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsLessThanOrEqualTo");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsGreaterThanOrEqualTo": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsGreaterThanOrEqualTo");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsBetween": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsBetween");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
                 this.writeNode("LowerBoundary", filter, node);
                 this.writeNode("UpperBoundary", filter, node);
@@ -350,13 +402,13 @@
                 });
             },
             "LowerBoundary": function(filter) {
-                // no ogc:expression handling for now
+                // handle Literals or Functions for now
                 var node = this.createElementNSPlus("ogc:LowerBoundary");
-                this.writeNode("Literal", filter.lowerBoundary, node);
+                this.writeOgcExpression(filter.lowerBoundary, node);
                 return node;
             },
             "UpperBoundary": function(filter) {
-                // no ogc:expression handling for now
+                // handle Literals or Functions for now
                 var node = this.createElementNSPlus("ogc:UpperBoundary");
                 this.writeNode("Literal", filter.upperBoundary, node);
                 return node;
@@ -382,6 +434,18 @@
                     },
                     value: filter.distance
                 });
+            },
+            "Function": function(filter) {
+                var node = this.createElementNSPlus("ogc:Function", {
+                    attributes: {
+                        name: filter.name
+                    }
+                });
+                var params = filter.params;
+                for(var i=0, len=params.length; i<len; i++){
+                    this.writeOgcExpression(params[i], node);
+                }
+                return node;
             }
         }
     },
@@ -418,7 +482,8 @@
         "DWITHIN": "DWITHIN",
         "WITHIN": "WITHIN",
         "CONTAINS": "CONTAINS",
-        "INTERSECTS": "INTERSECTS"
+        "INTERSECTS": "INTERSECTS",
+        "FID": "FeatureId"
     },
 
     CLASS_NAME: "OpenLayers.Format.Filter.v1" 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_0_0.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_0_0.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_0_0.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -96,16 +96,18 @@
         "ogc": OpenLayers.Util.applyDefaults({
             "PropertyIsEqualTo": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsNotEqualTo": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo");
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsLike": function(filter) {
@@ -150,6 +152,9 @@
     writeSpatial: function(filter, name) {
         var node = this.createElementNSPlus("ogc:"+name);
         this.writeNode("PropertyName", filter, node);
+        if(filter.value instanceof OpenLayers.Filter.Function) {
+            this.writeNode("Function", filter.value, node);
+        } else {
         var child;
         if(filter.value instanceof OpenLayers.Geometry) {
             child = this.writeNode("feature:_geometry", filter.value).firstChild;
@@ -160,6 +165,7 @@
             child.setAttribute("srsName", filter.projection);
         }
         node.appendChild(child);
+        }
         return node;
     },
 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_1_0.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_1_0.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1_1_0.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -108,18 +108,20 @@
                 var node = this.createElementNSPlus("ogc:PropertyIsEqualTo", {
                     attributes: {matchCase: filter.matchCase}
                 });
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsNotEqualTo": function(filter) {
                 var node = this.createElementNSPlus("ogc:PropertyIsNotEqualTo", {
                     attributes: {matchCase: filter.matchCase}
                 });
-                // no ogc:expression handling for now
+                // no ogc:expression handling for PropertyName for now
                 this.writeNode("PropertyName", filter, node);
-                this.writeNode("Literal", filter.value, node);
+                // handle Literals or Functions for now
+                this.writeOgcExpression(filter.value, node);
                 return node;
             },
             "PropertyIsLike": function(filter) {
@@ -164,6 +166,9 @@
     writeSpatial: function(filter, name) {
         var node = this.createElementNSPlus("ogc:"+name);
         this.writeNode("PropertyName", filter, node);
+        if(filter.value instanceof OpenLayers.Filter.Function) {
+            this.writeNode("Function", filter.value, node);
+        } else {
         var child;
         if(filter.value instanceof OpenLayers.Geometry) {
             child = this.writeNode("feature:_geometry", filter.value).firstChild;
@@ -174,6 +179,7 @@
             child.setAttribute("srsName", filter.projection);
         }
         node.appendChild(child);
+        }
         return node;
     },
 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -47,9 +47,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * APIMethod: write

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/GML.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/GML.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/GML.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -582,7 +582,7 @@
      * Method: parseAttributes
      *
      * Parameters:
-     * node - {<DOMElement>}
+     * node - {DOMElement}
      *
      * Returns:
      * {Object} An attributes object.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/GPX.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/GPX.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/GPX.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -134,7 +134,7 @@
     * Method: extractSegment
     *
     * Parameters:
-    * segment - {<DOMElement>} a trkseg or rte node to parse
+    * segment - {DOMElement} a trkseg or rte node to parse
     * segmentType - {String} nodeName of waypoints that form the line
     *
     * Returns:

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoJSON.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoJSON.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoJSON.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -40,9 +40,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.JSON.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoRSS.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoRSS.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/GeoRSS.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -83,9 +83,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
     
     /**
      * Method: createGeometryFromItem
@@ -253,7 +250,7 @@
         var eles = this.getElementsByTagNameNS(node, nsuri, name);
         if(eles && eles[0] && eles[0].firstChild
             && eles[0].firstChild.nodeValue) {
-            value = eles[0].firstChild.nodeValue;
+            value = OpenLayers.Format.XML.prototype.getChildValue(eles[0]);
         } else {
             value = (def == undefined) ? "" : def;
         }
@@ -263,12 +260,12 @@
     /**
      * APIMethod: read
      * Return a list of features from a GeoRSS doc
-     
+     *
      * Parameters:
-     * data - {Element} 
+     * doc - {Element} 
      *
      * Returns:
-     * An Array of <OpenLayers.Feature.Vector>s
+     * {Array(<OpenLayers.Feature.Vector>)}
      */
     read: function(doc) {
         if (typeof doc == "string") { 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/JSON.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/JSON.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/JSON.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -75,9 +75,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/KML.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/KML.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/KML.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -1125,7 +1125,7 @@
      * Accept Feature Collection, and return a string. 
      * 
      * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>} An array of features.
+     * features - {Array(<OpenLayers.Feature.Vector>)} An array of features.
      *
      * Returns:
      * {String} A KML string.
@@ -1222,7 +1222,8 @@
      * {DOMElement}
      */
     buildGeometryNode: function(geometry) {
-        if (this.internalProjection && this.externalProjection) {
+        if (this.internalProjection && this.externalProjection && 
+            !(geometry instanceof OpenLayers.Geometry.Collection)) {
             geometry = geometry.clone();
             geometry.transform(this.internalProjection, 
                                this.externalProjection);

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/OSM.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/OSM.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/OSM.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -347,6 +347,13 @@
         'point': function(point) {
             var id = null;
             var geometry = point.geometry ? point.geometry : point;
+            
+            if (this.internalProjection && this.externalProjection) {
+                geometry = geometry.clone();
+                geometry.transform(this.internalProjection, 
+                                   this.externalProjection);
+            }                       
+            
             var already_exists = false; // We don't return anything if the node
                                         // has already been created
             if (point.osm_id) {

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext/v0_3_1.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext/v0_3_1.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext/v0_3_1.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -179,9 +179,10 @@
     decomposeNestingPath: function(nPath){
         var a = [];
         if (nPath instanceof Array) {
-            while (nPath.length > 0) {
-                a.push(nPath.slice());
-                nPath.pop();
+            var path = nPath.slice();
+            while (path.length > 0) {
+                a.push(path.slice());
+                path.pop();
             }
             a.reverse();
         }
@@ -440,7 +441,9 @@
                 var node = this.createElementNSPlus("Style");
                 this.writeNode("Name", style, node);
                 this.writeNode("Title", style, node);
-                this.writeNode("LegendURL", style, node);
+                if (style.legend) {
+                    this.writeNode("LegendURL", style, node);
+                }
                 return node;
             },
             "Name": function(obj) {

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/OWSContext.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -23,6 +23,15 @@
      * {String} Version number to assume if none found.  Default is "0.3.1".
      */
     defaultVersion: "0.3.1",
+
+    /**
+     * Constructor: OpenLayers.Format.OWSContext
+     * Create a new parser for OWS Context documents.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
     
     /**
      * Method: getParser

Copied: sandbox/tschaub/canvas/lib/OpenLayers/Format/QueryStringFilter.js (from rev 11762, trunk/openlayers/lib/OpenLayers/Format/QueryStringFilter.js)
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/QueryStringFilter.js	                        (rev 0)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/QueryStringFilter.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,180 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Format.js
+ * @requires OpenLayers/Filter/Comparison.js
+ */
+
+/**
+ * Class: OpenLayers.Format.QueryStringFilter
+ * Parser for reading a query string and creating a simple filter.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Format>
+ */
+OpenLayers.Format.QueryStringFilter = (function() {
+
+    /** 
+     * Map the OpenLayers.Filter.Comparison types to the operation strings of 
+     * the protocol.
+     */
+    var cmpToStr = {};
+    cmpToStr[OpenLayers.Filter.Comparison.EQUAL_TO] = "eq";
+    cmpToStr[OpenLayers.Filter.Comparison.NOT_EQUAL_TO] = "ne";
+    cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN] = "lt";
+    cmpToStr[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO] = "lte";
+    cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN] = "gt";
+    cmpToStr[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
+    cmpToStr[OpenLayers.Filter.Comparison.LIKE] = "ilike";
+
+    /**
+     * Function: regex2value
+     * Convert the value from a regular expression string to a LIKE/ILIKE
+     * string known to the web service.
+     *
+     * Parameters:
+     * value - {String} The regex string.
+     *
+     * Returns:
+     * {String} The converted string.
+     */
+    function regex2value(value) {
+
+        // highly sensitive!! Do not change this without running the
+        // Protocol/HTTP.html unit tests
+
+        // convert % to \%
+        value = value.replace(/%/g, "\\%");
+
+        // convert \\. to \\_ (\\.* occurences converted later)
+        value = value.replace(/\\\\\.(\*)?/g, function($0, $1) {
+            return $1 ? $0 : "\\\\_";
+        });
+
+        // convert \\.* to \\%
+        value = value.replace(/\\\\\.\*/g, "\\\\%");
+
+        // convert . to _ (\. and .* occurences converted later)
+        value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) {
+            return $1 || $2 ? $0 : "_";
+        });
+
+        // convert .* to % (\.* occurnces converted later)
+        value = value.replace(/(\\)?\.\*/g, function($0, $1) {
+            return $1 ? $0 : "%";
+        });
+
+        // convert \. to .
+        value = value.replace(/\\\./g, ".");
+
+        // replace \* with * (watching out for \\*)
+        value = value.replace(/(\\)?\\\*/g, function($0, $1) {
+            return $1 ? $0 : "*";
+        });
+
+        return value;
+    }
+    
+    return OpenLayers.Class(OpenLayers.Format, {
+        
+        /**
+         * Property: wildcarded.
+         * {Boolean} If true percent signs are added around values
+         *     read from LIKE filters, for example if the protocol
+         *     read method is passed a LIKE filter whose property
+         *     is "foo" and whose value is "bar" the string
+         *     "foo__ilike=%bar%" will be sent in the query string;
+         *     defaults to false.
+         */
+        wildcarded: false,
+
+        /**
+         * APIProperty: srsInBBOX
+         * {Boolean} Include the SRS identifier in BBOX query string parameter.  
+         *     Default is false.  If true and the layer has a projection object set,
+         *     any BBOX filter will be serialized with a fifth item identifying the
+         *     projection.  E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
+         */
+        srsInBBOX: false,
+
+        /**
+         * APIMethod: write
+         * Serialize an <OpenLayers.Filter> objects using the "simple" filter syntax for 
+         *     query string parameters.  This function must be called as a method of
+         *     a protocol instance.
+         *
+         * Parameters:
+         * filter - {<OpenLayers.Filter>} filter to convert.
+         * params - {Object} The parameters object.
+         *
+         * Returns:
+         * {Object} The resulting parameters object.
+         */
+        write: function(filter, params) {
+            params = params || {};
+            var className = filter.CLASS_NAME;
+            var filterType = className.substring(className.lastIndexOf(".") + 1);
+            switch (filterType) {
+                case "Spatial":
+                    switch (filter.type) {
+                        case OpenLayers.Filter.Spatial.BBOX:
+                            params.bbox = filter.value.toArray();
+                            if (this.srsInBBOX && filter.projection) {
+                                params.bbox.push(filter.projection.getCode());
+                            }
+                            break;
+                        case OpenLayers.Filter.Spatial.DWITHIN:
+                            params.tolerance = filter.distance;
+                            // no break here
+                        case OpenLayers.Filter.Spatial.WITHIN:
+                            params.lon = filter.value.x;
+                            params.lat = filter.value.y;
+                            break;
+                        default:
+                            OpenLayers.Console.warn(
+                                "Unknown spatial filter type " + filter.type);
+                    }
+                    break;
+                case "Comparison":
+                    var op = cmpToStr[filter.type];
+                    if (op !== undefined) {
+                        var value = filter.value;
+                        if (filter.type == OpenLayers.Filter.Comparison.LIKE) {
+                            value = regex2value(value);
+                            if (this.wildcarded) {
+                                value = "%" + value + "%";
+                            }
+                        }
+                        params[filter.property + "__" + op] = value;
+                        params.queryable = params.queryable || [];
+                        params.queryable.push(filter.property);
+                    } else {
+                        OpenLayers.Console.warn(
+                            "Unknown comparison filter type " + filter.type);
+                    }
+                    break;
+                case "Logical":
+                    if (filter.type === OpenLayers.Filter.Logical.AND) {
+                        for (var i=0,len=filter.filters.length; i<len; i++) {
+                            params = this.write(filter.filters[i], params);
+                        }
+                    } else {
+                        OpenLayers.Console.warn(
+                            "Unsupported logical filter type " + filter.type);
+                    }
+                    break;
+                default:
+                    OpenLayers.Console.warn("Unknown filter type " + filterType);
+            }
+            return params;
+        },
+        
+        CLASS_NAME: "OpenLayers.Format.QueryStringFilter"
+        
+    });
+
+
+})();
\ No newline at end of file

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/SLD.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/SLD.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/SLD.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -58,9 +58,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * APIMethod: write

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSCapabilities.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSCapabilities.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSCapabilities.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -36,16 +36,12 @@
 
     /**
      * Constructor: OpenLayers.Format.SOSCapabilities
-     * Create a new parser for SOS capabilities.
+     * Create a new parser for SOS Capabilities.
      *
      * Parameters:
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-        this.options = options;
-    },
 
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetFeatureOfInterest.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetFeatureOfInterest.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetFeatureOfInterest.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -66,9 +66,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetObservation.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetObservation.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/SOSGetObservation.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -68,9 +68,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * Method: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -151,7 +151,17 @@
     read_cap_Post: function(obj, node) {
         obj.post = node.getAttribute("onlineResource");
     },
+
+    /**
+     * Method: read_cap_SRS
+     */
+    read_cap_SRS: function(obj, node) {
+        var srs = this.getChildValue(node);
+        if (srs) {
+            obj.srs = srs;
+        }
+    },
     
     CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_0_0" 
 
-});
\ No newline at end of file
+});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -31,6 +31,16 @@
         );
     },
 
+    /**
+     * Method: read_cap_DefaultSRS
+     */
+    read_cap_DefaultSRS: function(obj, node) {
+        var defaultSRS = this.getChildValue(node);
+        if (defaultSRS) {
+            obj.srs = defaultSRS;
+        }
+    },
+
     CLASS_NAME: "OpenLayers.Format.WFSCapabilities.v1_1_0" 
 
-});
\ No newline at end of file
+});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSCapabilities.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -36,10 +36,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-        this.options = options;
-    },
 
     /**
      * APIMethod: read
@@ -77,4 +73,4 @@
     
     CLASS_NAME: "OpenLayers.Format.WFSCapabilities" 
 
-});
\ No newline at end of file
+});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSDescribeFeatureType.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSDescribeFeatureType.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WFSDescribeFeatureType.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -31,9 +31,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-    },
     
     /**
      * Property: readers
@@ -195,4 +192,4 @@
     
     CLASS_NAME: "OpenLayers.Format.WFSDescribeFeatureType" 
 
-});
\ No newline at end of file
+});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WMC.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WMC.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WMC.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -22,6 +22,15 @@
      * {String} Version number to assume if none found.  Default is "1.1.0".
      */
     defaultVersion: "1.1.0",
+
+    /**
+     * Constructor: OpenLayers.Format.WMC
+     * Create a new parser for Web Map Context documents.
+     *
+     * Parameters:
+     * options - {Object} An optional object whose properties will be set on
+     *     this instance.
+     */
     
     /**
      * Method: getParser

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSCapabilities.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSCapabilities.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSCapabilities.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -51,10 +51,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-        this.options = options;
-    },
 
     /**
      * APIMethod: read
@@ -90,4 +86,4 @@
     
     CLASS_NAME: "OpenLayers.Format.WMSCapabilities" 
 
-});
\ No newline at end of file
+});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSDescribeLayer.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSDescribeLayer.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSDescribeLayer.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -37,10 +37,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-        this.options = options;
-    },
 
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSGetFeatureInfo.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSGetFeatureInfo.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WMSGetFeatureInfo.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -56,11 +56,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, arguments);
-        OpenLayers.Util.extend(this, options);
-        this.options = options;
-    },
 
     /**
      * APIMethod: read
@@ -155,10 +150,25 @@
             var featureNode = featureNodes[i];
             var geom = null;
 
+            // attributes can be actual attributes on the FIELDS tag, 
+            // or FIELD children
             var attributes = {};
-            for(var j=0, jlen=featureNode.attributes.length; j<jlen; j++) {
-                var attribute = featureNode.attributes[j];
-                attributes[attribute.nodeName] = attribute.nodeValue;
+            var j;
+            var jlen = featureNode.attributes.length;
+            if (jlen > 0) {
+                for(j=0; j<jlen; j++) {
+                    var attribute = featureNode.attributes[j];
+                    attributes[attribute.nodeName] = attribute.nodeValue;
+                }
+            } else {
+                var nodes = featureNode.childNodes;
+                for (j=0, jlen=nodes.length; j<jlen; ++j) {
+                    var node = nodes[j];
+                    if (node.nodeType != 3) {
+                        attributes[node.getAttribute("name")] = 
+                            node.getAttribute("value");
+                    }
+                }
             }
 
             response.push(
@@ -235,12 +245,14 @@
                 var child = children[i];
                 if (child.nodeType == 1) {
                     var grandchildren = child.childNodes;
-                    if (grandchildren.length == 1) {
+                    var name = (child.prefix) ?
+                        child.nodeName.split(":")[1] : child.nodeName;
+                    if (grandchildren.length == 0) {
+                        attributes[name] = null
+                    } else if (grandchildren.length == 1) {
                         var grandchild = grandchildren[0];
                         if (grandchild.nodeType == 3 ||
                             grandchild.nodeType == 4) {
-                            var name = (child.prefix) ? 
-                                child.nodeName.split(":")[1] : child.nodeName;
                             var value = grandchild.nodeValue.replace(
                                 this.regExes.trimSpace, "");
                             attributes[name] = value;

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/WMTSCapabilities.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/WMTSCapabilities.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/WMTSCapabilities.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -55,10 +55,6 @@
      * options - {Object} An optional object whose properties will be set on
      *     this instance.
      */
-    initialize: function(options) {
-        OpenLayers.Format.XML.prototype.initialize.apply(this, [options]);
-        this.options = options;
-    },
 
     /**
      * APIMethod: read

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Geometry/Rectangle.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Geometry/Rectangle.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Geometry/Rectangle.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -50,7 +50,7 @@
      * Constructor: OpenLayers.Geometry.Rectangle
      * 
      * Parameters:
-     * points - {Array(<OpenLayers.Geometry.Point>}
+     * points - {Array(<OpenLayers.Geometry.Point>)}
      */
     initialize: function(x, y, width, height) {
         OpenLayers.Geometry.prototype.initialize.apply(this, arguments);

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Box.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Box.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Box.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -32,11 +32,11 @@
     boxDivClassName: 'olHandlerBoxZoomBox',
     
     /**
-     * Property: boxCharacteristics
-     * {Object} Caches some box characteristics from css. This is used
-     *     by the getBoxCharacteristics method.
+     * Property: boxOffsets
+     * {Object} Caches box offsets from css. This is used by the getBoxOffsets
+     * method.
      */
-    boxCharacteristics: null,
+    boxOffsets: null,
 
     /**
      * Constructor: OpenLayers.Handler.Box
@@ -96,7 +96,13 @@
              new OpenLayers.Pixel(-9999, -9999));
         this.zoomBox.className = this.boxDivClassName;                                         
         this.zoomBox.style.zIndex = this.map.Z_INDEX_BASE["Popup"] - 1;
+        
         this.map.eventsDiv.appendChild(this.zoomBox);
+        
+        var offset = this.getBoxOffsets();
+        var pos = this.dragHandler.start;
+        this.zoomBox.style.left = (pos.x - offset.left) + "px";
+        this.zoomBox.style.top = (pos.y - offset.top) + "px";
 
         OpenLayers.Element.addClass(
             this.map.eventsDiv, "olDrawBox"
@@ -111,24 +117,14 @@
         var startY = this.dragHandler.start.y;
         var deltaX = Math.abs(startX - xy.x);
         var deltaY = Math.abs(startY - xy.y);
-        this.zoomBox.style.width = Math.max(1, deltaX) + "px";
-        this.zoomBox.style.height = Math.max(1, deltaY) + "px";
-        this.zoomBox.style.left = xy.x < startX ? xy.x+"px" : startX+"px";
-        this.zoomBox.style.top = xy.y < startY ? xy.y+"px" : startY+"px";
 
-        // depending on the box model, modify width and height to take borders
-        // of the box into account
-        var box = this.getBoxCharacteristics();
-        if (box.newBoxModel) {
-            if (xy.x > startX) {
-                this.zoomBox.style.width =
-                    Math.max(1, deltaX - box.xOffset) + "px";
-            }
-            if (xy.y > startY) {
-                this.zoomBox.style.height =
-                    Math.max(1, deltaY - box.yOffset) + "px";
-            }
-        }
+        var offset = this.getBoxOffsets();
+        this.zoomBox.style.width = (deltaX + offset.width + 1) + "px";
+        this.zoomBox.style.height = (deltaY + offset.height + 1) + "px";
+        this.zoomBox.style.left = (xy.x < startX ?
+            startX - deltaX - offset.left : startX - offset.left) + "px";
+        this.zoomBox.style.top = (xy.y < startY ?
+            startY - deltaY - offset.top : startY - offset.top) + "px";
     },
 
     /**
@@ -159,7 +155,7 @@
     removeBox: function() {
         this.map.eventsDiv.removeChild(this.zoomBox);
         this.zoomBox = null;
-        this.boxCharacteristics = null;
+        this.boxOffsets = null;
         OpenLayers.Element.removeClass(
             this.map.eventsDiv, "olDrawBox"
         );
@@ -183,7 +179,11 @@
      */
     deactivate: function () {
         if (OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
-            this.dragHandler.deactivate();
+            if (this.dragHandler.deactivate()) {
+                if (this.zoomBox) {
+                    this.removeBox();
+                }
+            }
             return true;
         } else {
             return false;
@@ -191,34 +191,53 @@
     },
     
     /**
-     * Method: getCharacteristics
-     * Determines offset and box model for a box.
+     * Method: getBoxOffsets
+     * Determines border offsets for a box, according to the box model.
      * 
      * Returns:
-     * {Object} a hash with the following properties:
-     *     - xOffset - Corner offset in x-direction
-     *     - yOffset - Corner offset in y-direction
-     *     - newBoxModel - true for all browsers except IE in quirks mode
+     * {Object} an object with the following offsets:
+     *     - left
+     *     - right
+     *     - top
+     *     - bottom
+     *     - width
+     *     - height
      */
-    getBoxCharacteristics: function() {
-        if (!this.boxCharacteristics) {
-            var xOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
-                "border-left-width")) + parseInt(OpenLayers.Element.getStyle(
-                this.zoomBox, "border-right-width")) + 1;
-            var yOffset = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
-                "border-top-width")) + parseInt(OpenLayers.Element.getStyle(
-                this.zoomBox, "border-bottom-width")) + 1;
-            // all browsers use the new box model, except IE in quirks mode
-            var newBoxModel = OpenLayers.BROWSER_NAME == "msie" ?
-                document.compatMode != "BackCompat" : true;
-            this.boxCharacteristics = {
-                xOffset: xOffset,
-                yOffset: yOffset,
-                newBoxModel: newBoxModel
+    getBoxOffsets: function() {
+        if (!this.boxOffsets) {
+            // Determine the box model. If the testDiv's clientWidth is 3, then
+            // the borders are outside and we are dealing with the w3c box
+            // model. Otherwise, the browser uses the traditional box model and
+            // the borders are inside the box bounds, leaving us with a
+            // clientWidth of 1.
+            var testDiv = document.createElement("div");
+            //testDiv.style.visibility = "hidden";
+            testDiv.style.position = "absolute";
+            testDiv.style.border = "1px solid black";
+            testDiv.style.width = "3px";
+            document.body.appendChild(testDiv);
+            var w3cBoxModel = testDiv.clientWidth == 3;
+            document.body.removeChild(testDiv);
+            
+            var left = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+                "border-left-width"));
+            var right = parseInt(OpenLayers.Element.getStyle(
+                this.zoomBox, "border-right-width"));
+            var top = parseInt(OpenLayers.Element.getStyle(this.zoomBox,
+                "border-top-width"));
+            var bottom = parseInt(OpenLayers.Element.getStyle(
+                this.zoomBox, "border-bottom-width"));
+            this.boxOffsets = {
+                left: left,
+                right: right,
+                top: top,
+                bottom: bottom,
+                width: w3cBoxModel === false ? left + right : 0,
+                height: w3cBoxModel === false ? top + bottom : 0
             };
         }
-        return this.boxCharacteristics;
+        return this.boxOffsets;
     },
-
+  
     CLASS_NAME: "OpenLayers.Handler.Box"
 });

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Click.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Click.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Click.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -51,13 +51,21 @@
      *     constructed.
      */
     pixelTolerance: 0,
-    
+        
     /**
+     * APIProperty: dblclickTolerance
+     * {Number} Maximum distance in pixels between clicks for a sequence of 
+     *     events to be considered a double click.  Default is 13.  If the
+     *     distance between two clicks is greater than this value, a double-
+     *     click will not be fired.
+     */
+    dblclickTolerance: 13,
+        
+    /**
      * APIProperty: stopSingle
      * {Boolean} Stop other listeners from being notified of clicks.  Default
-     *     is false.  If true, any click listeners registered before this one
-     *     will not be notified of *any* click event (associated with double
-     *     or single clicks).
+     *     is false.  If true, any listeners registered before this one for 
+     *     click or rightclick events will not be notified.
      */
     stopSingle: false,
     
@@ -82,6 +90,13 @@
      * {Number} The id of the timeout waiting to clear the <delayedCall>.
      */
     timerId: null,
+
+    /**
+     * Property: touch
+     * {Boolean} When a touchstart event is fired, touch will be true and all
+     *     mouse related listeners will do nothing.
+     */
+    touch: false,
     
     /**
      * Property: down
@@ -92,15 +107,22 @@
      */
     down: null,
 
-    /** 
+    /**
      * Property: last
      * {Object} Object that store relevant information about the last
-     *     touchmove. Its 'xy' OpenLayers.Pixel property gives
+     *     mousemove or touchmove. Its 'xy' OpenLayers.Pixel property gives
      *     the average location of the mouse/touch event. Its 'touches'
      *     property records clientX/clientY of each touches.
      */
     last: null,
 
+    /** 
+     * Property: first
+     * {Object} When waiting for double clicks, this object will store 
+     *     information about the first click in a two click sequence.
+     */
+    first: null,
+
     /**
      * Property: rightclickTimerId
      * {Number} The id of the right mouse timeout waiting to clear the 
@@ -126,35 +148,77 @@
      */
     initialize: function(control, callbacks, options) {
         OpenLayers.Handler.prototype.initialize.apply(this, arguments);
-        // optionally register for mouseup and mousedown
-        if(this.pixelTolerance != null) {
-            this.mousedown = function(evt) {
-                this.down = this.getEventInfo(evt);
-                return true;
-            };
+    },
+    
+    /**
+     * Method: touchstart
+     * Handle touchstart.
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    touchstart: function(evt) {
+        if (!this.touch) {
+            this.unregisterMouseListeners();
+            this.touch = true;
         }
+        this.down = this.getEventInfo(evt);
+        this.last = this.getEventInfo(evt);
+        return true;
     },
     
     /**
-     * Method: mousedown
-     * Handle mousedown.  Only registered as a listener if pixelTolerance is
-     *     a non-zero value at construction.
+     * Method: touchmove
+     *    Store position of last move, because touchend event can have
+     *    an empty "touches" property.
      *
      * Returns:
      * {Boolean} Continue propagating this event.
      */
-    mousedown: null,
+    touchmove: function(evt) {
+        this.last = this.getEventInfo(evt);
+        return true;
+    },
 
     /**
-     * Method: touchstart
-     * Handle touchstart.
+     * Method: touchend
+     *   Correctly set event xy property, and add lastTouches to have
+     *   touches property from last touchstart or touchmove
+     */
+    touchend: function(evt) {
+        // touchstart may not have been allowed to propagate
+        if (this.down) {
+            evt.xy = this.last.xy;
+            evt.lastTouches = this.last.touches;
+            this.handleSingle(evt);
+        }
+        return true;
+    },
+    
+    /**
+     * Method: unregisterMouseListeners
+     * In a touch environment, we don't want to handle mouse events.
+     */
+    unregisterMouseListeners: function() {
+        this.map.events.un({
+            mousedown: this.mousedown,
+            mouseup: this.mouseup,
+            click: this.click,
+            dblclick: this.dblclick,
+            scope: this
+        });
+    },
+
+    /**
+     * Method: mousedown
+     * Handle mousedown.
      *
      * Returns:
      * {Boolean} Continue propagating this event.
      */
-    touchstart: function(evt) {
+    mousedown: function(evt) {
         this.down = this.getEventInfo(evt);
-        this.last = null;
+        this.last = this.getEventInfo(evt);
         return true;
     },
 
@@ -171,8 +235,7 @@
         // Collect right mouse clicks from the mouseup
         //  IE - ignores the second right click in mousedown so using
         //  mouseup instead
-        if(this.checkModifiers(evt) &&
-           this.control.handleRightClicks &&
+        if (this.checkModifiers(evt) && this.control.handleRightClicks &&
            OpenLayers.Event.isRightClick(evt)) {
             propagate = this.rightclick(evt);
         }
@@ -194,7 +257,7 @@
            if(this.rightclickTimerId != null) {
                 //Second click received before timeout this must be 
                 // a double click
-                this.clearTimer();      
+                this.clearTimer();
                 this.callback('dblrightclick', [evt]);
                 return !this.stopDouble;
             } else { 
@@ -227,93 +290,93 @@
         if (evt) {
            this.callback('rightclick', [evt]);
         }
-        return !this.stopSingle;
     },
     
     /**
+     * Method: click
+     * Handle click events from the browser.  This is registered as a listener
+     *     for click events and should not be called from other events in this
+     *     handler.
+     *
+     * Returns:
+     * {Boolean} Continue propagating this event.
+     */
+    click: function(evt) {
+        if (!this.last) {
+            this.last = this.getEventInfo(evt);
+        }
+        this.handleSingle(evt);
+        return !this.stopSingle;
+    },
+
+    /**
      * Method: dblclick
      * Handle dblclick.  For a dblclick, we get two clicks in some browsers
      *     (FF) and one in others (IE).  So we need to always register for
-     *     dblclick to properly handle single clicks.
+     *     dblclick to properly handle single clicks.  This method is registered
+     *     as a listener for the dblclick browser event.  It should *not* be
+     *     called by other methods in this handler.
      *     
      * Returns:
      * {Boolean} Continue propagating this event.
      */
     dblclick: function(evt) {
-        // for touch devices trigger dblclick only for
-        // "one finger" touch
-        var last = this.down || this.last;
-        if (this.passesTolerance(evt) &&
-            (!last || !last.touches || last.touches.length == 1)) {
-            if(this["double"]) {
-                this.callback('dblclick', [evt]);
-            }
-            this.clearTimer();
-        }
+        this.handleDouble(evt);
         return !this.stopDouble;
     },
     
-    /**
-     * Method: touchmove
-     *    Store position of last move, because touchend event can have
-     *    an empty "touches" property.
+    /** 
+     * Method: handleDouble
+     * Handle double-click sequence.
      */
-    touchmove: function(evt) {
-        this.last = this.getEventInfo(evt);
-    },
-
-    /**
-     * Method: touchend
-     *   Correctly set event xy property, and add lastTouches to have
-     *   touches property from last touchstart or touchmove
-     */
-    touchend: function(evt) {
-        var last = this.last || this.down;
-        if (!evt || !last) {
-            return false;
+    handleDouble: function(evt) {
+        if (this["double"] && this.passesDblclickTolerance(evt)) {
+            this.callback("dblclick", [evt]);
         }
-        evt.xy = last.xy;
-        evt.lastTouches = last.touches;
-        return evt.xy ? this.click(evt) : false;
     },
-
-    /**
-     * Method: click
-     * Handle click.
-     *
-     * Returns:
-     * {Boolean} Continue propagating this event.
+    
+    /** 
+     * Method: handleSingle
+     * Handle single click sequence.
      */
-    click: function(evt) {
-        // Sencha Touch emulates click events, see ticket 3079 for more info
-        if (this.down && this.down.touches && evt.type === "click") {
-            return !this.stopSingle;
-        }
-        if(this.passesTolerance(evt)) {
-            if(this.timerId != null) {
+    handleSingle: function(evt) {
+        if (this.passesTolerance(evt)) {
+            if (this.timerId != null) {
                 // already received a click
-                var last = this.down || this.last;
-                if (last && last.touches && last.touches.length > 0) {
-                    // touch device - we may trigger dblclick
-                    this.dblclick(evt);
-                } else {
+                if (this.last.touches && this.last.touches.length === 1) {
+                    // touch device, no dblclick event - this may be a double
+                    this.handleDouble(evt);
+                }
+                // if we're not in a touch environment we clear the click timer
+                // if we've got a second touch, we'll get two touchend events
+                if (!this.last.touches || this.last.touches.length !== 2) {
                     this.clearTimer();
                 }
             } else {
+                // remember the first click info so we can compare to the second
+                this.first = this.getEventInfo(evt);
                 // set the timer, send evt only if single is true
                 //use a clone of the event object because it will no longer 
                 //be a valid event object in IE in the timer callback
                 var clickEvent = this.single ?
                     OpenLayers.Util.extend({}, evt) : null;
-                this.timerId = window.setTimeout(
-                    OpenLayers.Function.bind(this.delayedCall, this, clickEvent),
-                    this.delay
-               );
+                this.queuePotentialClick(clickEvent);
             }
         }
-        return !this.stopSingle;
     },
     
+    /** 
+     * Method: queuePotentialClick
+     * This method is separated out largely to make testing easier (so we
+     *     don't have to override window.setTimeout)
+     */
+    queuePotentialClick: function(evt) {
+        this.timerId = window.setTimeout(
+            OpenLayers.Function.bind(this.delayedCall, this, evt),
+            this.delay
+        );
+    },
+
     /**
      * Method: passesTolerance
      * Determine whether the event is within the optional pixel tolerance.  Note
@@ -327,28 +390,67 @@
      */
     passesTolerance: function(evt) {
         var passes = true;
-        if(this.pixelTolerance != null && this.down && this.down.xy) {
-            var dpx = Math.sqrt(
-                Math.pow(this.down.xy.x - evt.xy.x, 2) +
-                Math.pow(this.down.xy.y - evt.xy.y, 2)
-            );
-            if(dpx > this.pixelTolerance) {
-                passes = false;
+        if (this.pixelTolerance != null && this.down && this.down.xy) {
+            passes = this.pixelTolerance >= this.down.xy.distanceTo(evt.xy);
+            // for touch environments, we also enforce that all touches
+            // start and end within the given tolerance to be considered a click
+            if (passes && this.touch && 
+                this.down.touches.length === this.last.touches.length) {
+                // the touchend event doesn't come with touches, so we check
+                // down and last
+                for (var i=0, ii=this.down.touches.length; i<ii; ++i) {
+                    if (this.getTouchDistance(
+                            this.down.touches[i], 
+                            this.last.touches[i]
+                        ) > this.pixelTolerance) {
+                        passes = false;
+                        break;
+                    }
+                }
             }
         }
         return passes;
     },
+    
+    /** 
+     * Method: getTouchDistance
+     *
+     * Returns:
+     * {Boolean} The pixel displacement between two touches.
+     */
+    getTouchDistance: function(from, to) {
+        return Math.sqrt(
+            Math.pow(from.clientX - to.clientX, 2) +
+            Math.pow(from.clientY - to.clientY, 2)
+        );
+    },
+    
+    /**
+     * Method: passesDblclickTolerance
+     * Determine whether the event is within the optional double-cick pixel 
+     *     tolerance.
+     *
+     * Returns:
+     * {Boolean} The click is within the double-click pixel tolerance.
+     */
+    passesDblclickTolerance: function(evt) {
+        var passes = true;
+        if (this.down && this.first) {
+            passes = this.down.xy.distanceTo(this.first.xy) <= this.dblclickTolerance;
+        }
+        return passes;
+    },
 
     /**
      * Method: clearTimer
      * Clear the timer and set <timerId> to null.
      */
     clearTimer: function() {
-        if(this.timerId != null) {
+        if (this.timerId != null) {
             window.clearTimeout(this.timerId);
             this.timerId = null;
         }
-        if(this.rightclickTimerId != null) {
+        if (this.rightclickTimerId != null) {
             window.clearTimeout(this.rightclickTimerId);
             this.rightclickTimerId = null;
         }
@@ -361,8 +463,8 @@
      */
     delayedCall: function(evt) {
         this.timerId = null;
-        if(evt) {
-            this.callback('click', [evt]);
+        if (evt) {
+            this.callback("click", [evt]);
         }
     },
 
@@ -407,7 +509,7 @@
         if(OpenLayers.Handler.prototype.deactivate.apply(this, arguments)) {
             this.clearTimer();
             this.down = null;
-            this.last = null;
+            this.first = null;
             deactivated = true;
         }
         return deactivated;

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Drag.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Drag.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Drag.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -63,6 +63,14 @@
     start: null,
 
     /**
+     * Property: lastMoveEvt
+     * {Object} The last mousemove event that occurred. Used to
+     *     position the map correctly when our "delay drag"
+     *     timeout expired.
+     */
+    lastMoveEvt: null,
+
+    /**
      * Property: oldOnselectstart
      * {Function}
      */
@@ -184,6 +192,7 @@
      * {Boolean} Let the event propagate.
      */
     dragmove: function (evt) {
+        this.lastMoveEvt = evt;
         if (this.started && !this.timeoutId && (evt.xy.x != this.last.x ||
                                                 evt.xy.y != this.last.y)) {
             if(this.documentDrag === true && this.documentEvents) {
@@ -209,7 +218,7 @@
                 this.oldOnselectstart = document.onselectstart;
                 document.onselectstart = OpenLayers.Function.False;
             }
-            this.last = this.evt.xy;
+            this.last = evt.xy;
         }
         return true;
     },
@@ -364,6 +373,12 @@
      */
     removeTimeout: function() {
         this.timeoutId = null;
+        // if timeout expires while we're still dragging (mouseup
+        // hasn't occurred) then call mousemove to move to the
+        // correct position
+        if(this.dragging) {
+            this.mousemove(this.lastMoveEvt);
+        }
     },
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -79,13 +79,14 @@
      *     feature.
      */
     createFeature: function(pixel) {
-        if(!pixel) {
-            pixel = new OpenLayers.Pixel(-50, -50);
+        var geometry;
+        if(pixel) {
+            var lonlat = this.map.getLonLatFromPixel(pixel);
+            geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+        } else {
+            geometry = new OpenLayers.Geometry.Point();
         }
-        var lonlat = this.control.map.getLonLatFromPixel(pixel);
-        this.point = new OpenLayers.Feature.Vector(
-            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
-        );
+        this.point = new OpenLayers.Feature.Vector(geometry);
         this.line = new OpenLayers.Feature.Vector(
             new OpenLayers.Geometry.LineString([this.point.geometry])
         );
@@ -223,12 +224,12 @@
      * Returns: 
      * {Boolean} Allow event propagation
      */
-    mousedown: function(evt) {
+    down: function(evt) {
         var stopDown = this.stopDown;
         if(this.freehandMode(evt)) {
             stopDown = true;
         }
-        if(!this.lastDown || !this.lastDown.equals(evt.xy)) {
+        if (!this.touch && (!this.lastDown || !this.passesTolerance(this.lastDown, evt.xy, this.pixelTolerance))) {
             this.modifyFeature(evt.xy, !!this.lastUp);
         }
         this.mouseDown = true;
@@ -248,7 +249,7 @@
      * Returns: 
      * {Boolean} Allow event propagation
      */
-    mousemove: function (evt) {
+    move: function (evt) {
         if(this.stoppedDown && this.freehandMode(evt)) {
             if(this.persist) {
                 this.destroyPersistedFeature();
@@ -256,7 +257,7 @@
             this.addPoint(evt.xy);
             return false;
         }
-        if(!this.mouseDown || this.stoppedDown) {
+        if (!this.touch && (!this.mouseDown || this.stoppedDown)) {
             this.modifyFeature(evt.xy, !!this.lastUp);
         }
         return true;
@@ -273,13 +274,17 @@
      * Returns: 
      * {Boolean} Allow event propagation
      */
-    mouseup: function (evt) {
-        if(this.mouseDown && (!this.lastUp || !this.lastUp.equals(evt.xy))) {
+    up: function (evt) {
+        if (this.mouseDown && (!this.lastUp || !this.passesTolerance(
+                this.lastUp, evt.xy, this.dblclickTolerance))) {
             if(this.stoppedDown && this.freehandMode(evt)) {
                 this.removePoint();
                 this.finalize();
             } else {
-                if(this.lastDown.equals(evt.xy)) {
+                if (this.passesTolerance(this.lastDown, evt.xy, this.pixelTolerance)) {
+                    if (this.touch) {
+                        this.modifyFeature(evt.xy);
+                    }
                     if(this.lastUp == null && this.persist) {
                         this.destroyPersistedFeature();
                     }
@@ -290,10 +295,18 @@
         }
         this.stoppedDown = this.stopDown;
         this.mouseDown = false;
-        return !this.stopUp;
+        return !this.stopUp && !this.isDblclick;
     },
 
     /**
+     * Method: finishTouchGeometry
+     * Finish the geometry and send it back to the control.
+     */
+    finishTouchGeometry: function() {
+        this.finishGeometry();
+    },
+
+    /**
      * APIMethod: finishGeometry
      * Finish the geometry and send it back to the control.
      */

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Pinch.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Pinch.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Pinch.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -37,9 +37,9 @@
     /**
      * Property: stopDown
      * {Boolean} Stop propagation of touchstart events from getting to
-     *     listeners on the same element. Default is true.
+     *     listeners on the same element. Default is false.
      */
-    stopDown: true,
+    stopDown: false,
 
     /**
      * Property: pinching
@@ -105,6 +105,7 @@
             this.start = null;
             this.last = null;
         }
+        // prevent document dragging
         OpenLayers.Event.stop(evt);
         return propagate;
     },
@@ -120,15 +121,15 @@
      * {Boolean} Let the event propagate.
      */
     touchmove: function(evt) {
-        var propagate = true;
         if (this.started && OpenLayers.Event.isMultiTouch(evt)) {
             this.pinching = true;
             var current = this.getPinchData(evt);
             this.callback("move", [evt, current]);
             this.last = current;
-            propagate = false;
+            // prevent document dragging
+            OpenLayers.Event.stop(evt);
         }
-        return propagate;
+        return true;
     },
 
     /**
@@ -142,16 +143,14 @@
      * {Boolean} Let the event propagate.
      */
     touchend: function(evt) {
-        var propagate = true;
         if (this.started) {
             this.started = false;
             this.pinching = false;
             this.callback("done", [evt, this.start, this.last]);
             this.start = null;
             this.last = null;
-            propagate = false;
         }
-        return propagate;
+        return true;
     },
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -95,8 +95,51 @@
      * {Object} Any optional properties to be set on the sketch layer.
      */
     layerOptions: null,
+    
+    /**
+     * APIProperty: pixelTolerance
+     * {Number} Maximum number of pixels between down and up (mousedown
+     *     and mouseup, or touchstart and touchend) for the handler to
+     *     add a new point. If set to an integer value, if the
+     *     displacement between down and up is great to this value
+     *     no point will be added. Default value is 5.
+     */
+    pixelTolerance: 5,
 
     /**
+     * APIProperty: dblclickTolerance
+     * {Number} Maximum number of pixels between two touchend for an
+     *     event to be considered a dblclick.  Default is 20.
+     */
+    dblclickTolerance: 20,
+
+    /**
+     * Property: touch
+     * {Boolean} Indcates the support of touch events.
+     */
+    touch: false,
+
+    /**
+     * Property: timerId
+     * {Integer} The timer used to test the double touch.
+     */
+    timerId: null,
+
+    /**
+     * Property: last
+     * {<OpenLayers.Pixel>} The last pixel used to know the distance between
+     * two touches (for double touch).
+     */
+    last: null,
+
+    /**
+     * Property: dblclick
+     * {Boolean} The current event is a dblclick.
+     */
+    isDblclick: false,
+    
+    
+    /**
      * Constructor: OpenLayers.Handler.Point
      * Create a new point handler.
      *
@@ -157,13 +200,14 @@
      * pixel - {<OpenLayers.Pixel>} A pixel location on the map.
      */
     createFeature: function(pixel) {
-        if(!pixel) {
-            pixel = new OpenLayers.Pixel(-50, -50);
+        var geometry;
+        if(pixel) {
+            var lonlat = this.map.getLonLatFromPixel(pixel);
+            geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+        } else {
+            geometry = new OpenLayers.Geometry.Point();
         }
-        var lonlat = this.map.getLonLatFromPixel(pixel);
-        this.point = new OpenLayers.Feature.Vector(
-            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
-        );
+        this.point = new OpenLayers.Feature.Vector(geometry);
         this.callback("create", [this.point.geometry, this.point]);
         this.point.geometry.clearBounds();
         this.layer.addFeatures([this.point], {silent: true});
@@ -214,6 +258,14 @@
     },
 
     /**
+     * Method: finishTouchGeometry
+     * Finish the geometry and send it back to the control.
+     */
+    finishTouchGeometry: function() {
+        this.finalize();
+    },
+    
+    /**
      * Method: finalize
      * Finish the geometry and call the "done" callback.
      *
@@ -233,7 +285,7 @@
         if(cancel || !this.persist) {
             this.destroyFeature();
         }
-        if(!noNew) {
+        if(!noNew && this.active) {
             this.createFeature();
         }
     },
@@ -333,6 +385,127 @@
         var geom = this.getGeometry();
         return geom && geom.clone();
     },
+
+    /**
+     * Method: mousedown
+     * Handle mousedown.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousedown: function(evt) {
+        if (this.touch) {
+            return;
+        }
+        return this.down(evt);
+    },
+
+    /**
+     * Method: touchstart
+     * Handle touchstart.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    touchstart: function(evt) {
+        this.touch = true;
+
+        var last = this.last;
+        this.last = evt.xy;
+
+        if (this.timerId &&
+            this.passesTolerance(last, evt.xy, this.dblclickTolerance)) {
+            this.isDblclick = true;
+            // a valid touch immediately adds a component and leaves us with a
+            // complete geometry
+            this.finishTouchGeometry();
+            window.clearTimeout(this.timerId);
+            this.timerId = null;
+            return false;
+        }
+        else {
+            if (this.timerId) {
+                window.clearTimeout(this.timerId);
+                this.timerId = null;
+            }
+            this.isDblclick = false;
+            this.timerId = window.setTimeout(
+                OpenLayers.Function.bind(function() {
+                    this.timerId = null;
+                }, this), 300);
+            return this.down(evt);
+        }
+    },
+
+    /**
+     * Method: mousemove
+     * Handle mousemove.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mousemove: function(evt) {
+        if (this.touch) {
+            return;
+        }
+        return this.move(evt);
+    },
+
+    /**
+     * Method: touchmove
+     * Handle touchmove.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    touchmove: function(evt) {
+        this.last = evt.xy;
+        return this.move(evt);
+    },
+
+    /**
+     * Method: mouseup
+     * Handle mouseup.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    mouseup: function(evt) {
+        if (this.touch) {
+            return;
+        }
+        return this.up(evt);
+    },
+
+    /**
+     * Method: touchend
+     * Handle touchend.
+     * 
+     * Parameters:
+     * evt - {Event} The browser event
+     *
+     * Returns: 
+     * {Boolean} Allow event propagation
+     */
+    touchend: function(evt) {
+        evt.xy = this.last;
+        return this.up(evt);
+    },
   
     /**
      * Method: mousedown
@@ -345,10 +518,12 @@
      * Returns: 
      * {Boolean} Allow event propagation
      */
-    mousedown: function(evt) {
+    down: function(evt) {
         this.mouseDown = true;
         this.lastDown = evt.xy;
-        this.modifyFeature(evt.xy);
+        if(!this.touch) { // no point displayed until up on touch devices
+            this.modifyFeature(evt.xy);
+        }
         this.stoppedDown = this.stopDown;
         return !this.stopDown;
     },
@@ -364,8 +539,9 @@
      * Returns: 
      * {Boolean} Allow event propagation
      */
-    mousemove: function (evt) {
-        if(!this.mouseDown || this.stoppedDown) {
+    move: function (evt) {
+        if(!this.touch // no point displayed until up on touch devices
+           && (!this.mouseDown || this.stoppedDown)) {
             this.modifyFeature(evt.xy);
         }
         return true;
@@ -382,18 +558,24 @@
      * Returns: 
      * {Boolean} Allow event propagation
      */
-    mouseup: function (evt) {
+    up: function (evt) {
         this.mouseDown = false;
         this.stoppedDown = this.stopDown;
+
         // check keyboard modifiers
         if(!this.checkModifiers(evt)) {
             return true;
         }
         // ignore double-clicks
-        if(this.lastUp && this.lastUp.equals(evt.xy)) {
+        if (this.lastUp && this.passesTolerance(this.lastUp, evt.xy,
+                                                this.dblclickTolerance)) {
             return true;
         }
-        if(this.lastDown && this.lastDown.equals(evt.xy)) {
+        if (this.lastDown && this.passesTolerance(this.lastDown, evt.xy,
+                                                  this.pixelTolerance)) {
+            if (this.touch) {
+                this.modifyFeature(evt.xy);
+            }
             if(this.persist) {
                 this.destroyPersistedFeature();
             }
@@ -420,5 +602,28 @@
         }
     },
 
+    /**
+     * Method: passesTolerance
+     * Determine whether the event is within the optional pixel tolerance.
+     * Note that the pixel tolerance check only works if mousedown events get
+     * to the listeners registered here.  If they are stopped by other
+     * elements, <pixelTolerance> and <dblclickTolerance> will have no effect
+     * here (this method will always return true).
+     *
+     * Returns:
+     * {Boolean} The click is within the pixel tolerance (if specified).
+     */
+    passesTolerance: function(pixel1, pixel2, tolerance) {
+        var passes = true;
+
+        if (tolerance != null && pixel1 && pixel2) {
+            var dist = pixel1.distanceTo(pixel2);
+            if (dist > tolerance) {
+                passes = false;
+            }
+        }
+        return passes;
+    },
+    
     CLASS_NAME: "OpenLayers.Handler.Point"
 });

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Polygon.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Polygon.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Polygon.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -75,13 +75,14 @@
      *     feature.
      */
     createFeature: function(pixel) {
-        if(!pixel) {
-            pixel = new OpenLayers.Pixel(-50, -50);
+        var geometry;
+        if(pixel) {
+            var lonlat = this.map.getLonLatFromPixel(pixel);
+            geometry = new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat);
+        } else {
+            geometry = new OpenLayers.Geometry.Point();
         }
-        var lonlat = this.control.map.getLonLatFromPixel(pixel);
-        this.point = new OpenLayers.Feature.Vector(
-            new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat)
-        );
+        this.point = new OpenLayers.Feature.Vector(geometry);
         this.line = new OpenLayers.Feature.Vector(
             new OpenLayers.Geometry.LinearRing([this.point.geometry])
         );
@@ -148,8 +149,19 @@
             point.y = last.y;
         }
     },
-    
+
     /**
+     * Method: finishTouchGeometry
+     * Finish the geometry and send it back to the control.
+     */
+    finishTouchGeometry: function() {
+        var index = this.line.geometry.components.length - 2;
+        this.line.geometry.removeComponent(this.line.geometry.components[index]);
+        this.removePoint();
+        this.finalize();
+    },
+
+    /**
      * Method: finalizeInteriorRing
      * Enforces that new ring has some area and doesn't contain vertices of any
      *     other rings.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGIS93Rest.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGIS93Rest.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGIS93Rest.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -232,22 +232,5 @@
                                                              newArguments);
     },
 
-    /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-
-    
     CLASS_NAME: "OpenLayers.Layer.ArcGIS93Rest"
 });

Copied: sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGISCache.js (from rev 11762, trunk/openlayers/lib/OpenLayers/Layer/ArcGISCache.js)
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGISCache.js	                        (rev 0)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcGISCache.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,475 @@
+/** 
+ * @requires OpenLayers/Layer/XYZ.js 
+ */ 
+
+/** 
+ * Class: OpenLayers.Layer.ArcGISCache   
+ * Layer for accessing cached map tiles from an ArcGIS Server style mapcache. 
+ * Tile must already be cached for this layer to access it. This does not require 
+ * ArcGIS Server itself.
+ * 
+ * A few attempts have been made at this kind of layer before. See 
+ * http://trac.osgeo.org/openlayers/ticket/1967 
+ * and 
+ * http://trac.osgeo.org/openlayers/browser/sandbox/tschaub/arcgiscache/lib/OpenLayers/Layer/ArcGISCache.js
+ *
+ * Typically the problem encountered is that the tiles seem to "jump around".
+ * This is due to the fact that the actual max extent for the tiles on AGS layers
+ * changes at each zoom level due to the way these caches are constructed.
+ * We have attempted to use the resolutions, tile size, and tile origin
+ * from the cache meta data to make the appropriate changes to the max extent
+ * of the tile to compensate for this behavior.  This must be done as zoom levels change
+ * and before tiles are requested, which is why methods from base classes are overridden.
+ *
+ * For reference, you can access mapcache meta data in two ways. For accessing a 
+ * mapcache through ArcGIS Server, you can simply go to the landing page for the
+ * layer. (ie. http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer)
+ * For accessing it directly through HTTP, there should always be a conf.xml file
+ * in the root directory. 
+ * (ie. http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/conf.xml)
+ *  
+ *Inherits from: 
+ *  - <OpenLayers.Layer.XYZ>             
+ */    
+OpenLayers.Layer.ArcGISCache = OpenLayers.Class(OpenLayers.Layer.XYZ, {  
+
+    /**
+     * APIProperty: url
+     * {String | Array} The base URL for the layer cache.  You can also
+     *     provide a list of URL strings for the layer if your cache is
+     *     available from multiple origins.  This must be set before the layer
+     *     is drawn.
+     */
+    url: null,
+    
+   /**
+    * APIProperty: tileOrigin
+    * {<OpenLayers.LonLat>} The location of the tile origin for the cache.
+    *     An ArcGIS cache has it's origin at the upper-left (lowest x value
+    *     and highest y value of the coordinate system).  The units for the
+    *     tile origin should be the same as the units for the cached data.
+    */
+    tileOrigin: null, 
+   
+   /**
+    * APIProperty: tileSize
+    * {<OpenLayers.Size>} This size of each tile. Defaults to 256 by 256 pixels.
+    */
+    tileSize: new OpenLayers.Size(256, 256),
+    
+   /**
+    * APIProperty: useAGS
+    * {Boolean} Indicates if we are going to be accessing the ArcGIS Server (AGS)
+    *     cache via an AGS MapServer or directly through HTTP. When accessing via
+    *     AGS the path structure uses a standard z/y/x structure. But AGS actually
+    *     stores the tile images on disk using a hex based folder structure that looks
+    *     like "http://example.com/mylayer/L00/R00000000/C00000000.png".  Learn more
+    *     about this here:
+    *     http://blogs.esri.com/Support/blogs/mappingcenter/archive/2010/08/20/Checking-Your-Local-Cache-Folders.aspx
+    *     Defaults to true;
+    */    
+    useArcGISServer: true,
+
+   /**
+    * APIProperty: type
+    * {String} Image type for the layer.  This becomes the filename extension
+    *     in tile requests.  Default is "png" (generating a url like
+    *     "http://example.com/mylayer/L00/R00000000/C00000000.png").
+    */
+    type: 'png',
+    
+    /**
+    * APIProperty: useScales
+    * {Boolean} Optional override to indicate that the layer should use 'scale' information
+    *     returned from the server capabilities object instead of 'resolution' information.
+    *     This can be important if your tile server uses an unusual DPI for the tiles.
+    */
+    useScales: false,
+    
+   /**
+    * APIProperty: overrideDPI
+    * {Boolean} Optional override to change the OpenLayers.DOTS_PER_INCH setting based 
+    *     on the tile information in the server capabilities object.  This can be useful 
+    *     if your server has a non-standard DPI setting on its tiles, and you're only using 
+    *     tiles with that DPI.  This value is used while OpenLayers is calculating resolution
+    *     using scales, and is not necessary if you have resolution information. (This is
+    *     typically the case)  Regardless, this setting can be useful, but is dangerous
+    *     because it will impact other layers while calculating resolution.  Only use this
+    *     if you know what you are doing.  (See OpenLayers.Util.getResolutionFromScale)
+    */
+    overrideDPI: false,
+    
+   /**
+    * Constructor: OpenLayers.Layer.ArcGISCache 
+    * Creates a new instance of this class 
+    * 
+    * Parameters: 
+    * name - {String} 
+    * url - {String} 
+    * options - {Object} extra layer options
+    */ 
+    initialize: function(name, url, options) { 
+        OpenLayers.Layer.XYZ.prototype.initialize.apply(this, arguments);
+
+        if (this.resolutions) {        
+            this.serverResolutions = this.resolutions;
+            this.maxExtent = this.getMaxExtentForResolution(this.resolutions[0]);
+        }
+
+        // this block steps through translating the values from the server layer JSON 
+        // capabilities object into values that we can use.  This is also a helpful
+        // reference when configuring this layer directly.
+        if (this.layerInfo) {
+            // alias the object
+            var info = this.layerInfo;
+            
+            // build our extents
+            var startingTileExtent = new OpenLayers.Bounds(
+                info.fullExtent.xmin, 
+                info.fullExtent.ymin, 
+                info.fullExtent.xmax, 
+                info.fullExtent.ymax  
+            );
+
+            // set our projection based on the given spatial reference.
+            // esri uses slightly different IDs, so this may not be comprehensive
+            this.projection = 'EPSG:' + info.spatialReference.wkid;
+            this.sphericalMercator = (info.spatialReference.wkid == 102100);
+            
+            // convert esri units into openlayers units (basic feet or meters only)
+            this.units = (info.units == "esriFeet") ? 'ft' : 'm';
+
+            // optional extended section based on whether or not the server returned
+            // specific tile information
+            if (!!info.tileInfo) {            
+                // either set the tiles based on rows/columns, or specific width/height
+                this.tileSize = new OpenLayers.Size(
+                    info.tileInfo.width || info.tileInfo.cols, 
+                    info.tileInfo.height || info.tileInfo.rows
+                );
+                
+                // this must be set when manually configuring this layer
+                this.tileOrigin = new OpenLayers.LonLat(
+                    info.tileInfo.origin.x, 
+                    info.tileInfo.origin.y
+                );
+
+                var upperLeft = new OpenLayers.Geometry.Point(
+                    startingTileExtent.left, 
+                    startingTileExtent.top
+                );
+                
+                var bottomRight = new OpenLayers.Geometry.Point(
+                    startingTileExtent.right, 
+                    startingTileExtent.bottom
+                );            
+                
+                if (this.useScales) {
+                    this.scales = [];
+                } else {
+                    this.resolutions = [];
+                }
+                
+                this.lods = [];
+                for(var key in info.tileInfo.lods) {
+                    var lod = info.tileInfo.lods[key];
+                    if (this.useScales) {
+                        this.scales.push(lod.scale);
+                    } else {
+                        this.resolutions.push(lod.resolution);
+                    }
+                    
+                    var start = this.getContainingTileCoords(upperLeft, lod.resolution);
+                    lod.startTileCol = start.x;
+                    lod.startTileRow = start.y;
+                    
+                    var end = this.getContainingTileCoords(bottomRight, lod.resolution);
+                    lod.endTileCol = end.x;
+                    lod.endTileRow = end.y;    
+                    this.lods.push(lod);
+                }
+
+                this.maxExtent = this.calculateMaxExtentWithLOD(this.lods[0]);
+                this.serverResolutions = this.resolutions;
+                if (this.overrideDPI && info.tileInfo.dpi) {
+                    // see comment above for 'overrideDPI'
+                    OpenLayers.DOTS_PER_INCH = info.tileInfo.dpi;
+                }
+            } 
+       }
+    }, 
+
+   /** 
+    * Method: getContainingTileCoords
+    * Calculates the x/y pixel corresponding to the position of the tile
+    *     that contains the given point and for the for the given resolution.
+    * 
+    * Parameters:
+    * point - {<OpenLayers.Geometry.Point>} 
+    * res - {Float} The resolution for which to compute the extent.
+    * 
+    * Returns: 
+    * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position 
+    * of the upper left tile for the given resolution.
+    */
+    getContainingTileCoords: function(point, res) {
+        return new OpenLayers.Pixel(
+           Math.max(Math.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)),0),
+           Math.max(Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res)),0)
+        );
+    },
+    
+   /** 
+    * Method: calculateMaxExtentWithLOD
+    * Given a Level of Detail object from the server, this function
+    *     calculates the actual max extent
+    * 
+    * Parameters: 
+    * lod - {Object} a Level of Detail Object from the server capabilities object 
+            representing a particular zoom level
+    * 
+    * Returns: 
+    * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level
+    */
+   calculateMaxExtentWithLOD: function(lod) {
+        // the max extent we're provided with just overlaps some tiles
+        // our real extent is the bounds of all the tiles we touch
+
+        var numTileCols = (lod.endTileCol - lod.startTileCol) + 1;
+        var numTileRows = (lod.endTileRow - lod.startTileRow) + 1;        
+
+        var minX = this.tileOrigin.lon + (lod.startTileCol * this.tileSize.w * lod.resolution);
+        var maxX = minX + (numTileCols * this.tileSize.w * lod.resolution);
+
+        var maxY = this.tileOrigin.lat - (lod.startTileRow * this.tileSize.h * lod.resolution);
+        var minY = maxY - (numTileRows * this.tileSize.h * lod.resolution);
+        return new OpenLayers.Bounds(minX, minY, maxX, maxY);
+   },
+    
+   /** 
+    * Method: calculateMaxExtentWithExtent
+    * Given a 'suggested' max extent from the server, this function uses
+    *     information about the actual tile sizes to determine the actual
+    *     extent of the layer.
+    * 
+    * Parameters: 
+    * extent - {<OpenLayers.Bounds>} The 'suggested' extent for the layer
+    * res - {Float} The resolution for which to compute the extent.
+    * 
+    * Returns: 
+    * {<OpenLayers.Bounds>} The actual extent of the tiles for the given zoom level
+    */
+   calculateMaxExtentWithExtent: function(extent, res) {
+        var upperLeft = new OpenLayers.Geometry.Point(extent.left, extent.top);
+        var bottomRight = new OpenLayers.Geometry.Point(extent.right, extent.bottom);
+        var start = this.getContainingTileCoords(upperLeft, res);
+        var end = this.getContainingTileCoords(bottomRight, res);
+        var lod = {
+            resolution: res,
+            startTileCol: start.x,
+            startTileRow: start.y,
+            endTileCol: end.x,
+            endTileRow: end.y
+        };
+        return this.calculateMaxExtentWithLOD(lod);
+   },
+    
+    /** 
+    * Method: getUpperLeftTileCoord
+    * Calculates the x/y pixel corresponding to the position 
+    *     of the upper left tile for the given resolution.
+    * 
+    * Parameters: 
+    * res - {Float} The resolution for which to compute the extent.
+    * 
+    * Returns: 
+    * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position 
+    * of the upper left tile for the given resolution.
+    */
+    getUpperLeftTileCoord: function(res) {
+        var upperLeft = new OpenLayers.Geometry.Point(
+            this.maxExtent.left,
+            this.maxExtent.top);
+        return this.getContainingTileCoords(upperLeft, res);
+    },
+
+    /** 
+    * Method: getLowerRightTileCoord
+    * Calculates the x/y pixel corresponding to the position 
+    *     of the lower right tile for the given resolution.
+    *  
+    * Parameters: 
+    * res - {Float} The resolution for which to compute the extent.
+    * 
+    * Returns: 
+    * {<OpenLayers.Pixel>} The x/y pixel corresponding to the position
+    * of the lower right tile for the given resolution.
+    */
+    getLowerRightTileCoord: function(res) {
+        var bottomRight = new OpenLayers.Geometry.Point(
+            this.maxExtent.right,
+            this.maxExtent.bottom);
+        return this.getContainingTileCoords(bottomRight, res);
+    },
+    
+   /** 
+    * Method: getMaxExtentForResolution
+    * Since the max extent of a set of tiles can change from zoom level
+    *     to zoom level, we need to be able to calculate that max extent 
+    *     for a given resolution.
+    *
+    * Parameters: 
+    * res - {Float} The resolution for which to compute the extent.
+    * 
+    * Returns: 
+    * {<OpenLayers.Bounds>} The extent for this resolution
+    */ 
+    getMaxExtentForResolution: function(res) {
+        var start = this.getUpperLeftTileCoord(res);
+        var end = this.getLowerRightTileCoord(res);
+
+        var numTileCols = (end.x - start.x) + 1;
+        var numTileRows = (end.y - start.y) + 1;
+
+        var minX = this.tileOrigin.lon + (start.x * this.tileSize.w * res);
+        var maxX = minX + (numTileCols * this.tileSize.w * res);
+        
+        var maxY = this.tileOrigin.lat - (start.y * this.tileSize.h * res);
+        var minY = maxY - (numTileRows * this.tileSize.h * res);
+        return new OpenLayers.Bounds(minX, minY, maxX, maxY);
+    },
+    
+   /** 
+    * APIMethod: clone 
+    * Returns an exact clone of this OpenLayers.Layer.ArcGISCache
+    * 
+    * Parameters: 
+    * [obj] - {Object} optional object to assign the cloned instance to.
+    *  
+    * Returns: 
+    * {<OpenLayers.Layer.ArcGISCache>} clone of this instance 
+    */ 
+    clone: function (obj) { 
+        if (obj == null) { 
+            obj = new OpenLayers.Layer.ArcGISCache(this.name, this.url, this.options);
+        }
+        return OpenLayers.Layer.XYZ.prototype.clone.apply(this, [obj]);
+    },
+
+    /**
+     * Method: getMaxExtent
+     * Get this layer's maximum extent.
+     *
+     * Returns:
+     * {OpenLayers.Bounds}
+     */
+    getMaxExtent: function() {
+        var resolution = this.map.getResolution();
+        return this.maxExtent = this.getMaxExtentForResolution(resolution);
+    },
+
+    /**
+     * Method: getTileOrigin
+     * Determine the origin for aligning the grid of tiles.  
+     *     The origin will be derived from the layer's <maxExtent> property. 
+     *
+     * Returns:
+     * {<OpenLayers.LonLat>} The tile origin.
+     */
+    getTileOrigin: function() {
+        var extent = this.getMaxExtent();
+        return new OpenLayers.LonLat(extent.left, extent.bottom);
+    },
+
+   /**
+    * Method: getURL
+    * Determine the URL for a tile given the tile bounds.  This is should support
+    *     urls that access tiles through an ArcGIS Server MapServer or directly through
+    *     the hex folder structure using HTTP.  Just be sure to set the useArcGISServer
+    *     property appropriately!  This is basically the same as 
+    *     'OpenLayers.Layer.TMS.getURL',  but with the addition of hex addressing,
+    *     and tile rounding.
+    *
+    * Parameters:
+    * bounds - {<OpenLayers.Bounds>}
+    *
+    * Returns:
+    * {String} The URL for a tile based on given bounds.
+    */
+    getURL: function (bounds) {
+        var res = this.getResolution(); 
+
+        // tile center
+        var originTileX = (this.tileOrigin.lon + (res * this.tileSize.w/2)); 
+        var originTileY = (this.tileOrigin.lat - (res * this.tileSize.h/2));
+
+        var center = bounds.getCenterLonLat();
+        var point = { x: center.lon, y: center.lat };
+        var x = (Math.round(Math.abs((center.lon - originTileX) / (res * this.tileSize.w)))); 
+        var y = (Math.round(Math.abs((originTileY - center.lat) / (res * this.tileSize.h)))); 
+        var z = this.map.getZoom();
+
+        // this prevents us from getting pink tiles (non-existant tiles)
+        if (this.lods) {        
+            var lod = this.lods[this.map.getZoom()];
+            if ((x < lod.startTileCol || x > lod.endTileCol) 
+                || (y < lod.startTileRow || y > lod.endTileRow)) {
+                    return null;
+            }
+        }
+        else {
+            var start = this.getUpperLeftTileCoord(res);
+            var end = this.getLowerRightTileCoord(res);
+            if ((x < start.x || x >= end.x)
+                || (y < start.y || y >= end.y)) {
+                    return null;
+            }        
+        }
+
+        // Construct the url string
+        var url = this.url;
+        var s = '' + x + y + z;
+
+        if (url instanceof Array) {
+            url = this.selectUrl(s, url);
+        }
+
+        // Accessing tiles through ArcGIS Server uses a different path
+        // structure than direct access via the folder structure.
+        if (this.useArcGISServer) {
+            // AGS MapServers have pretty url access to tiles
+            url = url + '/tile/${z}/${y}/${x}';
+        } else {
+            // The tile images are stored using hex values on disk.
+            x = 'C' + this.zeroPad(x, 8, 16);
+            y = 'R' + this.zeroPad(y, 8, 16);
+            z = 'L' + this.zeroPad(z, 2, 16);
+            url = url + '/${z}/${y}/${x}.' + this.type;
+        }
+
+        // Write the values into our formatted url
+        url = OpenLayers.String.format(url, {'x': x, 'y': y, 'z': z});
+
+        return url;
+    },
+
+    /**
+     * Method: zeroPad
+     * Create a zero padded string optionally with a radix for casting numbers.
+     *
+     * Parameters:
+     * num - {Number} The number to be zero padded.
+     * len - {Number} The length of the string to be returned.
+     * radix - {Number} An integer between 2 and 36 specifying the base to use
+     *     for representing numeric values.
+     */
+    zeroPad: function(num, len, radix) {
+        var str = num.toString(radix || 10);
+        while (str.length < len) {
+            str = "0" + str;
+        }
+        return str;
+    },
+
+    CLASS_NAME: 'OpenLayers.Layer.ArcGISCache' 
+}); 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcIMS.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcIMS.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/ArcIMS.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -445,22 +445,5 @@
         return obj;
     },
     
-    /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added image tile.
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(
-            this, position, bounds, null, this.tileSize
-        );
-    },
-    
     CLASS_NAME: "OpenLayers.Layer.ArcIMS"
 });

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/EventPane.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/EventPane.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/EventPane.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -225,6 +225,8 @@
         
         if (this.dragPanMapObject) {
             this.dragPanMapObject(dx, -dy);
+        } else {
+            this.moveTo(this.map.getCachedCenter());
         }
     },
 
@@ -256,7 +258,7 @@
                 if ( !(newCenter.equals(oldCenter)) || 
                      !(newZoom == oldZoom) ) {
 
-                    if (dragging && this.dragPanMapObject && 
+                    if (!zoomChanged && oldCenter && this.dragPanMapObject && 
                         this.smoothDragPan) {
                         var oldPx = this.map.getViewPortPxFromLonLat(oldCenter);
                         var newPx = this.map.getViewPortPxFromLonLat(newCenter);

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/Google/v3.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/Google/v3.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/Google/v3.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -181,15 +181,16 @@
         if (this.visibility) {
             google.maps.event.trigger(this.mapObject, "resize");
         } else {
-            if (!this._resized) {
+            var cache = OpenLayers.Layer.Google.cache[this.map.id];
+            if (!cache.resized) {
                 var layer = this;
                 google.maps.event.addListenerOnce(this.mapObject, "tilesloaded", function() {
-                    delete layer._resized;
                     google.maps.event.trigger(layer.mapObject, "resize");
                     layer.moveTo(layer.map.getCenter(), layer.map.getZoom());
+                    delete cache.resized;
                 });
             }
-            this._resized = true;
+            cache.resized = true;
         }
     },
 
@@ -202,7 +203,7 @@
      */
     setGMapVisibility: function(visible) {
         var cache = OpenLayers.Layer.Google.cache[this.map.id];
-        if (cache) {
+        if (cache && !cache.resized) {
             var type = this.type;
             var layers = this.map.layers;
             var layer;

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/Grid.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/Grid.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/Grid.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -629,9 +629,7 @@
 
     /**
      * APIMethod: addTile
-     * Gives subclasses of Grid the opportunity to create an 
-     * OpenLayer.Tile of their choosing. The implementer should initialize 
-     * the new tile and take whatever steps necessary to display it.
+     * Create a tile, initialize it, and add it to the layer div. 
      *
      * Parameters
      * bounds - {<OpenLayers.Bounds>}
@@ -641,7 +639,8 @@
      * {<OpenLayers.Tile>} The added OpenLayers.Tile
      */
     addTile:function(bounds, position) {
-        // Should be implemented by subclasses
+        return new OpenLayers.Tile.Image(this, position, bounds, null, 
+                                         this.tileSize, this.tileOptions);
     },
     
     /** 
@@ -702,7 +701,7 @@
         var tlLayer = this.grid[0][0].position;
         var offsetX = parseInt(this.map.layerContainerDiv.style.left);
         var offsetY = parseInt(this.map.layerContainerDiv.style.top);
-        tlViewPort = tlLayer.add(offsetX, offsetY);
+        var tlViewPort = tlLayer.add(offsetX, offsetY);
         if (tlViewPort.x > -this.tileSize.w * (buffer - 1)) {
             this.shiftColumn(true);
         } else if (tlViewPort.x < -this.tileSize.w * buffer) {
@@ -799,7 +798,7 @@
      * 
      * Parameters:
      * rows - {Integer} Maximum number of rows we want our grid to have.
-     * colums - {Integer} Maximum number of columns we want our grid to have.
+     * columns - {Integer} Maximum number of columns we want our grid to have.
      */
     removeExcessTiles: function(rows, columns) {
         

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/KaMap.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/KaMap.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/KaMap.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -94,22 +94,6 @@
                       });
     },
 
-    /**
-     * Method: addTile
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>}
-     */    
-    addTile:function(bounds,position) {
-        var url = this.getURL(bounds);
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                             url, this.tileSize);
-    },
-
     /** 
      * Method: calculateGridLayout
      * ka-Map uses the center point of the map as an origin for 
@@ -118,14 +102,14 @@
      *
      * Parameters:
      * bounds - {<OpenLayers.Bound>}
-     * extent - {<OpenLayers.Bounds>}
+     * origin - {<OpenLayers.LonLat>}
      * resolution - {Number}
      *
      * Returns:
      * Object containing properties tilelon, tilelat, tileoffsetlat,
      * tileoffsetlat, tileoffsetx, tileoffsety
      */
-    calculateGridLayout: function(bounds, extent, resolution) {
+    calculateGridLayout: function(bounds, origin, resolution) {
         var tilelon = resolution*this.tileSize.w;
         var tilelat = resolution*this.tileSize.h;
         

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapGuide.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapGuide.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapGuide.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -116,6 +116,12 @@
      **/
     defaultSize: new OpenLayers.Size(300,300),
 
+    /** 
+     * Property: tileOriginCorner
+     * {String} MapGuide tile server uses top-left as tile origin
+     **/
+    tileOriginCorner: "tl",
+
     /**
      * Constructor: OpenLayers.Layer.MapGuide
      * Create a new Mapguide layer, either tiled or untiled.  
@@ -234,22 +240,6 @@
     },
 
     /**
-     * Method: addTile
-     * Creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-
-    /**
      * Method: getURL
      * Return a query string for this layer
      *
@@ -455,28 +445,28 @@
      *
      * Parameters:
      * bounds - {<OpenLayers.Bound>}
-     * extent - {<OpenLayers.Bounds>}
+     * origin - {<OpenLayers.LonLat>}
      * resolution - {Number}
      *
      * Returns:
      * Object containing properties tilelon, tilelat, tileoffsetlat,
      * tileoffsetlat, tileoffsetx, tileoffsety
      */
-    calculateGridLayout: function(bounds, extent, resolution) {
+    calculateGridLayout: function(bounds, origin, resolution) {
         var tilelon = resolution * this.tileSize.w;
         var tilelat = resolution * this.tileSize.h;
         
-        var offsetlon = bounds.left - extent.left;
+        var offsetlon = bounds.left - origin.lon;
         var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
         var tilecolremain = offsetlon/tilelon - tilecol;
         var tileoffsetx = -tilecolremain * this.tileSize.w;
-        var tileoffsetlon = extent.left + tilecol * tilelon;
+        var tileoffsetlon = origin.lon + tilecol * tilelon;
         
-        var offsetlat = extent.top - bounds.top + tilelat; 
+        var offsetlat = origin.lat - bounds.top + tilelat; 
         var tilerow = Math.floor(offsetlat/tilelat) - this.buffer;
         var tilerowremain = tilerow - offsetlat/tilelat;
         var tileoffsety = tilerowremain * this.tileSize.h;
-        var tileoffsetlat = extent.top - tilelat*tilerow;
+        var tileoffsetlat = origin.lat - tilelat*tilerow;
         
         return { 
           tilelon: tilelon, tilelat: tilelat,

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapServer.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapServer.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/MapServer.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -76,22 +76,6 @@
 
         return obj;
     },
-
-    /**
-     * Method: addTile
-     * Creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
     
     /**
      * Method: getURL

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/PointTrack.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/PointTrack.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/PointTrack.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -20,13 +20,23 @@
   
     /**
      * APIProperty:
-     * dataFrom  - {<OpenLayers.Layer.PointTrack.dataFrom>} optional. If the
-     *             lines should get the data/attributes from one of the two
-     *             points, creating it, which one should it be?
+     * dataFrom  - {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or
+     *     {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines
+     *     should get the data/attributes from one of the two points it is
+     *     composed of, which one should it be?
      */
     dataFrom: null,
     
     /**
+     * APIProperty:
+     * styleFrom  - {<OpenLayers.Layer.PointTrack.TARGET_NODE>} or
+     *     {<OpenLayers.Layer.PointTrack.SOURCE_NODE>} optional. If the lines
+     *     should get the style from one of the two points it is composed of,
+     *     which one should it be?
+     */
+    styleFrom: null,
+    
+    /**
      * Constructor: OpenLayers.PointTrack
      * Constructor for a new OpenLayers.PointTrack instance.
      *
@@ -47,9 +57,12 @@
      * 
      * Parameters:
      * pointFeatures - {Array(<OpenLayers.Feature>)}
+     * options - {Object}
      * 
+     * Supported options:
+     * silent - {Boolean} true to suppress (before)feature(s)added events
      */
-    addNodes: function(pointFeatures) {
+    addNodes: function(pointFeatures, options) {
         if (pointFeatures.length < 2) {
             OpenLayers.Console.error(
                     "At least two point features have to be added to create" +
@@ -78,26 +91,43 @@
                         (pointFeatures[i+this.dataFrom].data ||
                                 pointFeatures[i+this.dataFrom].attributes) :
                         null;
+                var style = (this.styleFrom != null) ?
+                        (pointFeatures[i+this.styleFrom].style) :
+                        null;
                 var line = new OpenLayers.Geometry.LineString([startPoint,
                         endPoint]);
                         
-                lines[i-1] = new OpenLayers.Feature.Vector(line, attributes);
+                lines[i-1] = new OpenLayers.Feature.Vector(line, attributes,
+                    style);
             }
             
             startPoint = endPoint;
         }
 
-        this.addFeatures(lines);
+        this.addFeatures(lines, options);
     },
     
     CLASS_NAME: "OpenLayers.Layer.PointTrack"
 });
 
 /**
+ * Constant: OpenLayers.Layer.PointTrack.SOURCE_NODE
+ * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and
+ * <OpenLayers.Layer.PointTrack.styleFrom>
+ */
+OpenLayers.Layer.PointTrack.SOURCE_NODE = -1;
+
+/**
+ * Constant: OpenLayers.Layer.PointTrack.TARGET_NODE
+ * {Number} value for <OpenLayers.Layer.PointTrack.dataFrom> and
+ * <OpenLayers.Layer.PointTrack.styleFrom>
+ */
+OpenLayers.Layer.PointTrack.TARGET_NODE = 0;
+
+/**
  * Constant: OpenLayers.Layer.PointTrack.dataFrom
- * {Object} with the following keys
+ * {Object} with the following keys - *deprecated*
  * - SOURCE_NODE: take data/attributes from the source node of the line
  * - TARGET_NODE: take data/attributes from the target node of the line
  */
 OpenLayers.Layer.PointTrack.dataFrom = {'SOURCE_NODE': -1, 'TARGET_NODE': 0};
-

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/TMS.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/TMS.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/TMS.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -131,22 +131,6 @@
         return url + path;
     },
 
-    /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-
     /** 
      * APIMethod: setMap
      * When the layer is added to a map, then we can fetch our origin 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/TileCache.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/TileCache.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/TileCache.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -144,23 +144,6 @@
         url = (url.charAt(url.length - 1) == '/') ? url : url + '/';
         return url + path;
     },
-
-    /**
-     * Method: addTile
-     * Create a tile, initialize it, and add it to the layer div. 
-     *
-     * Parameters: 
-     * bounds - {<OpenLayers.Bounds>} 
-     * position - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added <OpenLayers.Tile.Image>
-     */
-    addTile:function(bounds, position) {
-        var url = this.getURL(bounds);
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                             url, this.tileSize);
-    },
     
     CLASS_NAME: "OpenLayers.Layer.TileCache"
 });

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -472,34 +472,38 @@
     moveTo: function(bounds, zoomChanged, dragging) {
         OpenLayers.Layer.prototype.moveTo.apply(this, arguments);
         
-        var coordSysUnchanged = true;
+        var ng = (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG);
+        if (ng) {
+            zoomChanged && this.renderer.updateDimensions();
+        } else {
+            var coordSysUnchanged = true;
 
-        if (!dragging) {
-            this.renderer.root.style.visibility = "hidden";
+            if (!dragging) {
+                this.renderer.root.style.visibility = "hidden";
             
-            this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
-            this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
-            var extent = this.map.getExtent();
-            coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
+                this.div.style.left = -parseInt(this.map.layerContainerDiv.style.left) + "px";
+                this.div.style.top = -parseInt(this.map.layerContainerDiv.style.top) + "px";
+                var extent = this.map.getExtent();
+                coordSysUnchanged = this.renderer.setExtent(extent, zoomChanged);
             
-            this.renderer.root.style.visibility = "visible";
+                this.renderer.root.style.visibility = "visible";
 
-            // Force a reflow on gecko based browsers to prevent jump/flicker.
-            // This seems to happen on only certain configurations; it was originally
-            // noticed in FF 2.0 and Linux.
-            if (OpenLayers.IS_GECKO === true) {
-                this.div.scrollLeft = this.div.scrollLeft;
-            }
+                // Force a reflow on gecko based browsers to prevent jump/flicker.
+                // This seems to happen on only certain configurations; it was originally
+                // noticed in FF 2.0 and Linux.
+                if (OpenLayers.IS_GECKO === true) {
+                    this.div.scrollLeft = this.div.scrollLeft;
+                }
             
-            if(!zoomChanged && coordSysUnchanged) {
-                for(var i in this.unrenderedFeatures) {
-                    var feature = this.unrenderedFeatures[i];
-                    this.drawFeature(feature);
+                if(!zoomChanged && coordSysUnchanged) {
+                    for(var i in this.unrenderedFeatures) {
+                        var feature = this.unrenderedFeatures[i];
+                        this.drawFeature(feature);
+                    }
                 }
             }
         }
-        
-        if (!this.drawn || zoomChanged || !coordSysUnchanged) {
+        if (!this.drawn || (!ng && (zoomChanged || !coordSysUnchanged))) {
             this.drawn = true;
             var feature;
             for(var i=0, len=this.features.length; i<len; i++) {
@@ -510,6 +514,20 @@
         }    
     },
     
+    /**
+     * APIMethod: redraw
+     * Redraws the layer.  Returns true if the layer was redrawn, false if not.
+     *
+     * Returns:
+     * {Boolean} The layer was redrawn.
+     */
+    redraw: function() {
+        if (OpenLayers.Renderer.NG && this.renderer instanceof OpenLayers.Renderer.NG) {
+            this.drawn = false;
+        }
+        return OpenLayers.Layer.prototype.redraw.apply(this, arguments);
+    },
+    
     /** 
      * APIMethod: display
      * Hide or show the Layer

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMS.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMS.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMS.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -203,22 +203,6 @@
     },
 
     /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize, this.tileOptions);
-    },
-
-    /**
      * APIMethod: mergeNewParams
      * Catch changeParams and uppercase the new params to be merged in
      *     before calling changeParams on the super class.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMTS.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMTS.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/WMTS.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -456,21 +456,5 @@
         }
     },
 
-    /**
-     * Method: addTile
-     * Create a tile, initialize it, and add it to the layer div. 
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile: function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-
     CLASS_NAME: "OpenLayers.Layer.WMTS"
 });

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/WorldWind.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/WorldWind.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/WorldWind.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -59,20 +59,6 @@
             this.params, this.DEFAULT_PARAMS
         );
     },
-    /**
-     * Method: addTile
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                             null, this.tileSize);
-    },
 
     /**
      * Method: getZoom

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/XYZ.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/XYZ.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/XYZ.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -151,22 +151,6 @@
         return {'x': x, 'y': y, 'z': z};
     },
     
-    /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div. 
-     * 
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     * 
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds, 
-                                         null, this.tileSize);
-    },
-     
     /* APIMethod: setMap
      * When the layer is added to a map, then we can fetch our origin 
      *    (if we don't have one.) 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/Zoomify.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/Zoomify.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/Zoomify.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -45,6 +45,12 @@
      */
     standardTileSize: 256,
 
+    /** 
+     * Property: tileOriginCorner
+     * {String} This layer uses top-left as tile origin
+     **/
+    tileOriginCorner: "tl",
+
     /**
      * Property: numberOfTiers
      * {Integer} Depth of the Zoomify pyramid, number of tiers (zoom levels)
@@ -239,22 +245,6 @@
     },
 
     /**
-     * Method: addTile
-     * addTile creates a tile, initializes it, and adds it to the layer div.
-     *
-     * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
-     * position - {<OpenLayers.Pixel>}
-     *
-     * Returns:
-     * {<OpenLayers.Tile.Image>} The added OpenLayers.Tile.Image
-     */
-    addTile:function(bounds,position) {
-        return new OpenLayers.Tile.Image(this, position, bounds,
-                                         null, this.tileSize);
-    },
-
-    /**
      * APIMethod: setMap
      * When the layer is added to a map, then we can fetch our origin
      *    (if we don't have one.)
@@ -274,28 +264,28 @@
      *
      * Parameters:
      * bounds - {<OpenLayers.Bound>}
-     * extent - {<OpenLayers.Bounds>}
+     * origin - {<OpenLayers.LonLat>}
      * resolution - {Number}
      *
      * Returns:
      * Object containing properties tilelon, tilelat, tileoffsetlat,
      * tileoffsetlat, tileoffsetx, tileoffsety
      */
-    calculateGridLayout: function(bounds, extent, resolution) {
+    calculateGridLayout: function(bounds, origin, resolution) {
         var tilelon = resolution * this.tileSize.w;
         var tilelat = resolution * this.tileSize.h;
 
-        var offsetlon = bounds.left - extent.left;
+        var offsetlon = bounds.left - origin.lon;
         var tilecol = Math.floor(offsetlon/tilelon) - this.buffer;
         var tilecolremain = offsetlon/tilelon - tilecol;
         var tileoffsetx = -tilecolremain * this.tileSize.w;
-        var tileoffsetlon = extent.left + tilecol * tilelon;
+        var tileoffsetlon = origin.lon + tilecol * tilelon;
 
-        var offsetlat = extent.top - bounds.top + tilelat;
+        var offsetlat = origin.lat - bounds.top + tilelat;
         var tilerow = Math.floor(offsetlat/tilelat) - this.buffer;
         var tilerowremain = tilerow - offsetlat/tilelat;
         var tileoffsety = tilerowremain * this.tileSize.h;
-        var tileoffsetlat = extent.top - tilelat*tilerow;
+        var tileoffsetlat = origin.lat - tilelat*tilerow;
 
         return {
           tilelon: tilelon, tilelat: tilelat,

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -556,7 +556,7 @@
      * Method: moveTo
      * 
      * Parameters:
-     * bound - {<OpenLayers.Bounds>}
+     * bounds - {<OpenLayers.Bounds>}
      * zoomChanged - {Boolean} Tells when zoom has changed, as layers have to
      *     do some init work in that case.
      * dragging - {Boolean}
@@ -715,7 +715,7 @@
      *     subverted.
      * 
      * Parameters:
-     * visible - {Boolean} Whether or not to display the layer (if in range)
+     * visibility - {Boolean} Whether or not to display the layer (if in range)
      */
     setVisibility: function(visibility) {
         if (visibility != this.visibility) {
@@ -1079,7 +1079,7 @@
      * APIMethod: getZoomForExtent
      * 
      * Parameters:
-     * bounds - {<OpenLayers.Bounds>}
+     * extent - {<OpenLayers.Bounds>}
      * closest - {Boolean} Find the zoom level that most closely fits the 
      *     specified bounds. Note that this may result in a zoom that does 
      *     not exactly contain the entire extent.
@@ -1215,7 +1215,7 @@
         var map = this.map;
         if (viewPortPx != null && map.minPx) {
             var res = map.getResolution();
-            var maxExtent = map.getMaxExtent();
+            var maxExtent = map.getMaxExtent({restricted: true});
             var lon = (viewPortPx.x - map.minPx.x) * res + maxExtent.left;
             var lat = (map.minPx.y - viewPortPx.y) * res + maxExtent.top;
             lonlat = new OpenLayers.LonLat(lon, lat);
@@ -1322,7 +1322,8 @@
         if (this.wrapDateLine) {
             // wrap around the date line, within the limits of rounding error
             var wrappingOptions = { 
-                'rightTolerance':this.getResolution()
+                'rightTolerance':this.getResolution(),
+                'leftTolerance':this.getResolution()
             };    
             bounds = bounds.wrapDateLine(this.maxExtent, wrappingOptions);
                               

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Map.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Map.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Map.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -739,7 +739,13 @@
      * options - {Object} Hashtable of options to tag to the map
      */
     setOptions: function(options) {
+        var updatePxExtent = this.minPx &&
+            options.restrictedExtent != this.restrictedExtent;
         OpenLayers.Util.extend(this, options);
+        // force recalculation of minPx and maxPx
+        updatePxExtent && this.moveTo(this.getCachedCenter(), this.zoom, {
+            forceZoomChange: true
+        });
     },
 
     /**
@@ -1564,21 +1570,20 @@
                 this.moveByPx(dx, dy);
             }
         } else {
-            // if we don't have a center, we were using moveByPx previously
-            var forceSetCenter = !this.center;
-            
             // getCenter
             var centerPx = this.getViewPortPxFromLonLat(this.getCachedCenter());
 
             // adjust
             var newCenterPx = centerPx.add(dx, dy);
 
-            if (forceSetCenter || !newCenterPx.equals(centerPx)) {
+            if (this.dragging || !newCenterPx.equals(centerPx)) {
                 var newCenterLonLat = this.getLonLatFromViewPortPx(newCenterPx);
                 if (options.animate) {
                     this.panTo(newCenterLonLat);
                 } else {
-                    this.setCenter(newCenterLonLat, null, options.dragging);
+                    this.moveTo(newCenterLonLat);
+                    this.dragging = false;
+                    this.events.triggerEvent("moveend");
                 }    
             }
         }        
@@ -1591,7 +1596,7 @@
      * If the new lonlat is in the current extent the map will slide smoothly
      * 
      * Parameters:
-     * lonlat - {<OpenLayers.Lonlat>}
+     * lonlat - {<OpenLayers.LonLat>}
      */
     panTo: function(lonlat) {
         if (this.panMethod && this.getExtent().scale(this.panRatio).containsLonLat(lonlat)) {
@@ -1601,8 +1606,7 @@
             var center = this.getCachedCenter();
 
             // center will not change, don't do nothing
-            if (lonlat.lon == center.lon &&
-                lonlat.lat == center.lat) {
+            if (lonlat.equals(center)) {
                 return;
             }
 
@@ -1613,9 +1617,6 @@
 
             this.panTween.start( { x: 0, y: 0 }, vector, this.panDuration, {
                 callbacks: {
-                    start: OpenLayers.Function.bind(function() {
-                        this.events.triggerEvent("movestart");
-                    }, this),
                     eachStep: OpenLayers.Function.bind(function(px) {
                         var x = px.x - last.x,
                             y = px.y - last.y;
@@ -1624,7 +1625,8 @@
                         last.y = Math.round(px.y);
                     }, this),
                     done: OpenLayers.Function.bind(function(px) {
-                        this.moveTo(lonlat, this.zoom, {noEvent: true});
+                        this.moveTo(lonlat);
+                        this.dragging = false;
                         this.events.triggerEvent("moveend");
                     }, this)
                 }
@@ -1649,10 +1651,10 @@
      * TBD: reconsider forceZoomChange in 3.0
      */
     setCenter: function(lonlat, zoom, dragging, forceZoomChange) {
+        this.panTween && this.panTween.stop();             
         this.moveTo(lonlat, zoom, {
             'dragging': dragging,
-            'forceZoomChange': forceZoomChange,
-            'caller': 'setCenter'
+            'forceZoomChange': forceZoomChange
         });
     },
     
@@ -1671,36 +1673,41 @@
         var hh = this.size.h / 2;
         var x = hw + dx;
         var y = hh + dy;
-        var valid = y <= this.maxPx.y &&
-                    y >= this.minPx.y;
-        var minX, maxX;
-        if (this.baseLayer.wrapDateLine === true) {
-            minX = this.minPx.x, maxX = this.maxPx.x;
-        } else {
+        var wrapDateLine = this.baseLayer.wrapDateLine;
+        var xRestriction = 0;
+        var yRestriction = 0;
+        if (this.restrictedExtent) {
+            xRestriction = hw;
+            yRestriction = hh;
+            // wrapping the date line makes no sense for restricted extents
+            wrapDateLine = false;
+        }
+        var valid = y <= this.maxPx.y - yRestriction &&
+                    y >= this.minPx.y + yRestriction;
+        var minX = this.minPx.x, maxX = this.maxPx.x;
+        if (!wrapDateLine) {
             valid = valid &&
-                    x <= this.maxPx.x &&
-                    x >= this.minPx.x;
+                    x <= this.maxPx.x - xRestriction &&
+                    x >= this.minPx.x + xRestriction;
         }
-        if (this.restrictedExtent && valid) {
-            valid = !(this.maxPx.x - x < hw ||
-                      x - this.minPx.x < hw ||
-                      this.maxPx.y - y < hh ||
-                      y - this.minPx.y < hh);
-        }
         if (valid) {
+            if (!this.dragging) {
+                this.dragging = true;
+                this.events.triggerEvent("movestart");
+            }
             this.center = null;
             if (dx) {
                 this.layerContainerDiv.style.left =
                     parseInt(this.layerContainerDiv.style.left) - dx + "px";
                 this.minPx.x -= dx;
                 this.maxPx.x -= dx;
-                if (this.baseLayer.wrapDateLine === true) {
+                if (wrapDateLine) {
                     if (this.maxPx.x > maxX) {
                         this.maxPx.x -= (maxX - minX);
-                    };
+                    }
                     if (this.minPx.x < minX) {
                         this.minPx.x += (maxX - minX);
-                    };
+                    }
                 }
             }
             if (dy) {
@@ -1740,18 +1747,13 @@
             }
         }
         // dragging is false by default
-        var dragging = options.dragging;
+        var dragging = options.dragging || this.dragging;
         // forceZoomChange is false by default
         var forceZoomChange = options.forceZoomChange;
-        // noEvent is false by default
-        var noEvent = options.noEvent;
 
-        if (this.panTween && options.caller == "setCenter") {
-            this.panTween.stop();
-        }    
-             
         if (!this.getCachedCenter() && !this.isValidLonLat(lonlat)) {
             lonlat = this.maxExtent.getCenterLonLat();
+            this.center = lonlat.clone();
         }
 
         if(this.restrictedExtent != null) {
@@ -1795,14 +1797,10 @@
         var centerChanged = (this.isValidLonLat(lonlat)) && 
                             (!lonlat.equals(this.center));
 
-
         // if neither center nor zoom will change, no need to do anything
-        if (zoomChanged || centerChanged || !dragging) {
+        if (zoomChanged || centerChanged || dragging) {
+            dragging || this.events.triggerEvent("movestart");
 
-            if (!this.dragging && !noEvent) {
-                this.events.triggerEvent("movestart");
-            }
-
             if (centerChanged) {
                 if (!zoomChanged && this.center) { 
                     // if zoom hasnt changed, just slide layerContainer
@@ -1843,14 +1841,10 @@
             //send the move call to the baselayer and all the overlays    
 
             if(this.baseLayer.visibility) {
-                this.baseLayer.moveTo(bounds, zoomChanged, dragging);
-                if(dragging) {
-                    this.baseLayer.events.triggerEvent("move");
-                } else {
-                    this.baseLayer.events.triggerEvent("moveend",
-                        {"zoomChanged": zoomChanged}
-                    );
-                }
+                this.baseLayer.moveTo(bounds, zoomChanged, options.dragging);
+                options.dragging || this.baseLayer.events.triggerEvent(
+                    "moveend", {zoomChanged: zoomChanged}
+                );
             }
             
             bounds = this.baseLayer.getExtent();
@@ -1873,38 +1867,25 @@
                         });
                     }
                     if (inRange && layer.visibility) {
-                        layer.moveTo(bounds, zoomChanged, dragging);
-                        if(dragging) {
-                            layer.events.triggerEvent("move");
-                        } else {
-                            layer.events.triggerEvent("moveend",
-                                {"zoomChanged": zoomChanged}
-                            );
-                        }
+                        layer.moveTo(bounds, zoomChanged, options.dragging);
+                        options.dragging || layer.events.triggerEvent(
+                            "moveend", {zoomChanged: zoomChanged}
+                        );
                     }
                 }                
             }
             
+            this.events.triggerEvent("move");
+            dragging || this.events.triggerEvent("moveend");
+
             if (zoomChanged) {
                 //redraw popups
                 for (var i=0, len=this.popups.length; i<len; i++) {
                     this.popups[i].updatePosition();
                 }
-            }    
-            
-            this.events.triggerEvent("move");
-    
-            if (zoomChanged) { this.events.triggerEvent("zoomend"); }
+                this.events.triggerEvent("zoomend");
+            }
         }
-
-        // even if nothing was done, we want to notify of this
-        if (!dragging && !noEvent) {
-            this.events.triggerEvent("moveend");
-        }
-        
-        // Store the map dragging state for later use
-        this.dragging = !!dragging; 
-
     },
 
     /** 
@@ -2250,8 +2231,6 @@
     /**
      * APIMethod: zoomIn
      * 
-     * Parameters:
-     * zoom - {int}
      */
     zoomIn: function() {
         this.zoomTo(this.getZoom() + 1);
@@ -2260,8 +2239,6 @@
     /**
      * APIMethod: zoomOut
      * 
-     * Parameters:
-     * zoom - {int}
      */
     zoomOut: function() {
         this.zoomTo(this.getZoom() - 1);

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Popup.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Popup.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Popup.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -431,7 +431,7 @@
      * Makes the popup visible.
      */
     show: function() {
-        OpenLayers.Element.show(this.div);
+        this.div.style.display = '';
 
         if (this.panMapIfOutOfView) {
             this.panIntoView();
@@ -443,7 +443,7 @@
      * Makes the popup invisible.
      */
     hide: function() {
-        OpenLayers.Element.hide(this.div);
+        this.div.style.display = 'none';
     },
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Projection.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Projection.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Projection.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -30,7 +30,7 @@
     
     /**
      * Property: titleRegEx
-     * {RegEx} regular expression to strip the title from a proj4js definition
+     * {RegExp} regular expression to strip the title from a proj4js definition
      */
     titleRegEx: /\+title=[^\+]*/,
 
@@ -174,10 +174,10 @@
  *     the input point is transformed in place.
  * 
  * Parameters:
- * point - {{OpenLayers.Geometry.Point> | Object} An object with x and y
+ * point - {<OpenLayers.Geometry.Point> | Object} An object with x and y
  *     properties representing coordinates in those dimensions.
- * sourceProj - {OpenLayers.Projection} Source map coordinate system
- * destProj - {OpenLayers.Projection} Destination map coordinate system
+ * source - {OpenLayers.Projection} Source map coordinate system
+ * dest - {OpenLayers.Projection} Destination map coordinate system
  *
  * Returns:
  * point - {object} A transformed coordinate.  The original point is modified.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Protocol/HTTP.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Protocol/HTTP.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Protocol/HTTP.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -14,6 +14,11 @@
  */
 
 /**
+ * TODO: remove this dependency in 3.0
+ * @requires OpenLayers/Format/QueryStringFilter.js
+ */
+
+/**
  * Class: OpenLayers.Protocol.HTTP
  * A basic HTTP protocol for vector layers.  Create a new instance with the
  *     <OpenLayers.Protocol.HTTP> constructor.
@@ -109,6 +114,16 @@
         this.params = {};
         this.headers = {};
         OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
+
+        if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
+            var format = new OpenLayers.Format.QueryStringFilter({
+                wildcarded: this.wildcarded,
+                srsInBBOX: this.srsInBBOX
+            });
+            this.filterToParams = function(filter, params) {
+                return format.write(filter, params);
+            }
+        }
     },
     
     /**
@@ -120,8 +135,23 @@
         this.headers = null;
         OpenLayers.Protocol.prototype.destroy.apply(this);
     },
-   
+
     /**
+     * APIMethod: filterToParams
+     * Optional method to translate an <OpenLayers.Filter> object into an object
+     *     that can be serialized as request query string provided.  If a custom
+     *     method is not provided, the filter will be serialized using the 
+     *     <OpenLayers.Protocol.simpleFilterSerializer> method.
+     *
+     * Parameters:
+     * filter - {<OpenLayers.Filter>} filter to convert.
+     * params - {Object} The parameters object.
+     *
+     * Returns:
+     * {Object} The resulting parameters object.
+     */
+    
+    /**
      * APIMethod: read
      * Construct a request for reading new features.
      *
@@ -148,9 +178,10 @@
         options = OpenLayers.Util.applyDefaults(options, this.options);
         options.params = OpenLayers.Util.applyDefaults(
             options.params, this.options.params);
-        if(options.filter) {
+        if (options.filter && this.filterToParams) {
             options.params = this.filterToParams(
-                options.filter, options.params);
+                options.filter, options.params
+            );
         }
         var readWithPOST = (options.readWithPOST !== undefined) ?
                            options.readWithPOST : this.readWithPOST;
@@ -190,124 +221,6 @@
     },
 
     /**
-     * Method: filterToParams
-     * Convert an <OpenLayers.Filter> object to parameters.
-     *
-     * Parameters:
-     * filter - {OpenLayers.Filter} filter to convert.
-     * params - {Object} The parameters object.
-     *
-     * Returns:
-     * {Object} The resulting parameters object.
-     */
-    filterToParams: function(filter, params) {
-        params = params || {};
-        var className = filter.CLASS_NAME;
-        var filterType = className.substring(className.lastIndexOf(".") + 1);
-        switch(filterType) {
-            case "Spatial":
-                switch(filter.type) {
-                    case OpenLayers.Filter.Spatial.BBOX:
-                        params.bbox = filter.value.toArray();
-                        if (this.srsInBBOX && filter.projection) {
-                            params.bbox.push(filter.projection.getCode());
-                        }
-                        break;
-                    case OpenLayers.Filter.Spatial.DWITHIN:
-                        params.tolerance = filter.distance;
-                        // no break here
-                    case OpenLayers.Filter.Spatial.WITHIN:
-                        params.lon = filter.value.x;
-                        params.lat = filter.value.y;
-                        break;
-                    default:
-                        OpenLayers.Console.warn(
-                            "Unknown spatial filter type " + filter.type);
-                }
-                break;
-            case "Comparison":
-                var op = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR[filter.type];
-                if(op !== undefined) {
-                    var value = filter.value;
-                    if(filter.type == OpenLayers.Filter.Comparison.LIKE) {
-                        value = this.regex2value(value);
-                        if(this.wildcarded) {
-                            value = "%" + value + "%";
-                        }
-                    }
-                    params[filter.property + "__" + op] = value;
-                    params.queryable = params.queryable || [];
-                    params.queryable.push(filter.property);
-                } else {
-                    OpenLayers.Console.warn(
-                        "Unknown comparison filter type " + filter.type);
-                }
-                break;
-            case "Logical":
-                if(filter.type === OpenLayers.Filter.Logical.AND) {
-                    for(var i=0,len=filter.filters.length; i<len; i++) {
-                        params = this.filterToParams(filter.filters[i], params);
-                    }
-                } else {
-                    OpenLayers.Console.warn(
-                        "Unsupported logical filter type " + filter.type);
-                }
-                break;
-            default:
-                OpenLayers.Console.warn("Unknown filter type " + filterType);
-        }
-        return params;
-    },
-
-    /**
-     * Method: regex2value
-     * Convert the value from a regular expression string to a LIKE/ILIKE
-     * string known to the web service.
-     *
-     * Parameters:
-     * value - {String} The regex string.
-     *
-     * Returns:
-     * {String} The converted string.
-     */
-    regex2value: function(value) {
-
-        // highly sensitive!! Do not change this without running the
-        // Protocol/HTTP.html unit tests
-
-        // convert % to \%
-        value = value.replace(/%/g, "\\%");
-
-        // convert \\. to \\_ (\\.* occurences converted later)
-        value = value.replace(/\\\\\.(\*)?/g, function($0, $1) {
-            return $1 ? $0 : "\\\\_";
-        });
-
-        // convert \\.* to \\%
-        value = value.replace(/\\\\\.\*/g, "\\\\%");
-
-        // convert . to _ (\. and .* occurences converted later)
-        value = value.replace(/(\\)?\.(\*)?/g, function($0, $1, $2) {
-            return $1 || $2 ? $0 : "_";
-        });
-
-        // convert .* to % (\.* occurnces converted later)
-        value = value.replace(/(\\)?\.\*/g, function($0, $1) {
-            return $1 ? $0 : "%";
-        });
-
-        // convert \. to .
-        value = value.replace(/\\\./g, ".");
-
-        // replace \* with * (watching out for \\*)
-        value = value.replace(/(\\)?\\\*/g, function($0, $1) {
-            return $1 ? $0 : "*";
-        });
-
-        return value;
-    },
-
-    /**
      * APIMethod: create
      * Construct a request for writing newly created features.
      *
@@ -648,21 +561,3 @@
 
     CLASS_NAME: "OpenLayers.Protocol.HTTP" 
 });
-
-/**
- * Property: OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR
- * {Object} A private class-level property mapping the
- *     OpenLayers.Filter.Comparison types to the operation
- *     strings of the protocol.
- */
-(function() {
-    var o = OpenLayers.Protocol.HTTP.COMP_TYPE_TO_OP_STR = {};
-    o[OpenLayers.Filter.Comparison.EQUAL_TO]                 = "eq";
-    o[OpenLayers.Filter.Comparison.NOT_EQUAL_TO]             = "ne";
-    o[OpenLayers.Filter.Comparison.LESS_THAN]                = "lt";
-    o[OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO]    = "lte";
-    o[OpenLayers.Filter.Comparison.GREATER_THAN]             = "gt";
-    o[OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO] = "gte";
-    o[OpenLayers.Filter.Comparison.LIKE]                     = "ilike";
-})();
-

Copied: sandbox/tschaub/canvas/lib/OpenLayers/Protocol/Script.js (from rev 11762, trunk/openlayers/lib/OpenLayers/Protocol/Script.js)
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Protocol/Script.js	                        (rev 0)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Protocol/Script.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,377 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Protocol.js
+ * @requires OpenLayers/Feature/Vector.js
+ * @requires OpenLayers/Format/GeoJSON.js
+ */
+
+/**
+ * Class: OpenLayers.Protocol.Script
+ * A basic Script protocol for vector layers.  Create a new instance with the
+ *     <OpenLayers.Protocol.Script> constructor.  A script protocol is used to
+ *     get around the same origin policy.  It works with services that return
+ *     JSONP - that is, JSON wrapped in a client-specified callback.  The
+ *     protocol handles fetching and parsing of feature data and sends parsed
+ *     features to the <callback> configured with the protocol.  The protocol
+ *     expects features serialized as GeoJSON by default, but can be configured
+ *     to work with other formats by setting the <format> property.
+ *
+ * Inherits from:
+ *  - <OpenLayers.Protocol>
+ */
+OpenLayers.Protocol.Script = OpenLayers.Class(OpenLayers.Protocol, {
+
+    /**
+     * APIProperty: url
+     * {String} Service URL.  The service is expected to return serialized 
+     *     features wrapped in a named callback (where the callback name is
+     *     generated by this protocol).
+     *     Read-only, set through the options passed to the constructor.
+     */
+    url: null,
+
+    /**
+     * APIProperty: params
+     * {Object} Query string parameters to be appended to the URL.
+     *     Read-only, set through the options passed to the constructor.
+     *     Example: {maxFeatures: 50}
+     */
+    params: null,
+    
+    /**
+     * APIProperty: callback
+     * {Object} Function to be called when the <read> operation completes.
+     */
+    callback: null,
+
+    /**
+     * APIProperty: scope
+     * {Object} Optional ``this`` object for the callback. Read-only, set 
+     *     through the options passed to the constructor.
+     */
+    scope: null,
+
+    /**
+     * APIProperty: format
+     * {<OpenLayers.Format>} Format for parsing features.  Default is an 
+     *     <OpenLayers.Format.GeoJSON> format.  If an alternative is provided,
+     *     the format's read method must take an object and return an array
+     *     of features.
+     */
+    format: null,
+
+    /**
+     * APIProperty: callbackKey
+     * {String} The name of the query string parameter that the service 
+     *     recognizes as the callback identifier.  Default is "callback".
+     *     This key is used to generate the URL for the script.  For example
+     *     setting <callbackKey> to "myCallback" would result in a URL like 
+     *     http://example.com/?myCallback=...
+     */
+    callbackKey: "callback",
+
+    /**
+     * APIProperty: callbackPrefix
+     * {String} Where a service requires that the callback query string 
+     *     parameter value is prefixed by some string, this value may be set.
+     *     For example, setting <callbackPrefix> to "foo:" would result in a
+     *     URL like http://example.com/?callback=foo:...  Default is "".
+     */
+    callbackPrefix: "",
+
+    /**
+     * Property: pendingRequests
+     * {Object} References all pending requests.  Property names are script 
+     *     identifiers and property values are script elements.
+     */
+    pendingRequests: null,
+
+    /**
+     * APIProperty: srsInBBOX
+     * {Boolean} Include the SRS identifier in BBOX query string parameter.
+     *     Setting this property has no effect if a custom filterToParams method
+     *     is provided.   Default is false.  If true and the layer has a 
+     *     projection object set, any BBOX filter will be serialized with a 
+     *     fifth item identifying the projection.  
+     *     E.g. bbox=-1000,-1000,1000,1000,EPSG:900913
+     */
+    srsInBBOX: false,
+
+    /**
+     * Constructor: OpenLayers.Protocol.Script
+     * A class for giving layers generic Script protocol.
+     *
+     * Parameters:
+     * options - {Object} Optional object whose properties will be set on the
+     *     instance.
+     *
+     * Valid options include:
+     * url - {String}
+     * params - {Object}
+     * callback - {Function}
+     * scope - {Object}
+     */
+    initialize: function(options) {
+        options = options || {};
+        this.params = {};
+        this.pendingRequests = {};
+        OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
+        if (!this.format) {
+            this.format = new OpenLayers.Format.GeoJSON();
+        }
+
+        if (!this.filterToParams && OpenLayers.Format.QueryStringFilter) {
+            var format = new OpenLayers.Format.QueryStringFilter({
+                srsInBBOX: this.srsInBBOX
+            });
+            this.filterToParams = function(filter, params) {
+                return format.write(filter, params);
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: read
+     * Construct a request for reading new features.
+     *
+     * Parameters:
+     * options - {Object} Optional object for configuring the request.
+     *     This object is modified and should not be reused.
+     *
+     * Valid options:
+     * url - {String} Url for the request.
+     * params - {Object} Parameters to get serialized as a query string.
+     * filter - {<OpenLayers.Filter>} Filter to get serialized as a
+     *     query string.
+     *
+     * Returns:
+     * {<OpenLayers.Protocol.Response>} A response object, whose "priv" property
+     *     references the injected script.  This object is also passed to the
+     *     callback function when the request completes, its "features" property
+     *     is then populated with the features received from the server.
+     */
+    read: function(options) {
+        OpenLayers.Protocol.prototype.read.apply(this, arguments);
+        options = OpenLayers.Util.applyDefaults(options, this.options);
+        options.params = OpenLayers.Util.applyDefaults(
+            options.params, this.options.params
+        );
+        if (options.filter && this.filterToParams) {
+            options.params = this.filterToParams(
+                options.filter, options.params
+            );
+        }
+        var response = new OpenLayers.Protocol.Response({requestType: "read"});
+        var request = this.createRequest(
+            options.url, 
+            options.params, 
+            OpenLayers.Function.bind(function(data) {
+                response.data = data;
+                this.handleRead(response, options);
+            }, this)
+        );
+        response.priv = request;
+        return response;
+    },
+
+    /** 
+     * APIMethod: filterToParams 
+     * Optional method to translate an <OpenLayers.Filter> object into an object 
+     *     that can be serialized as request query string provided.  If a custom 
+     *     method is not provided, any filter will not be serialized. 
+     * 
+     * Parameters: 
+     * filter - {<OpenLayers.Filter>} filter to convert. 
+     * params - {Object} The parameters object. 
+     * 
+     * Returns: 
+     * {Object} The resulting parameters object. 
+     */
+
+    /** 
+     * Method: createRequest
+     * Issues a request for features by creating injecting a script in the 
+     *     document head.
+     *
+     * Parameters:
+     * url - {String} Service URL.
+     * params - {Object} Query string parameters.
+     * callback - {Function} Callback to be called with resulting data.
+     *
+     * Returns:
+     * {HTMLScriptElement} The script pending execution.
+     */
+    createRequest: function(url, params, callback) {
+        var id = OpenLayers.Protocol.Script.register(callback);
+        var name = "OpenLayers.Protocol.Script.getCallback(" + id + ")";
+        params = OpenLayers.Util.extend({}, params);
+        params[this.callbackKey] = this.callbackPrefix + name;
+        url = OpenLayers.Util.urlAppend(
+            url, OpenLayers.Util.getParameterString(params)
+        );
+        var script = document.createElement("script");
+        script.type = "text/javascript";
+        script.src = url;
+        script.id = "OpenLayers_Protocol_Script_" + id;
+        this.pendingRequests[script.id] = script;
+        var head = document.getElementsByTagName("head")[0];
+        head.appendChild(script);
+        return script;
+    },
+    
+    /** 
+     * Method: destroyRequest
+     * Remove a script node associated with a response from the document.  Also
+     *     unregisters the callback and removes the script from the 
+     *     <pendingRequests> object.
+     *
+     * Parameters:
+     * script - {HTMLScriptElement}
+     */
+    destroyRequest: function(script) {
+        OpenLayers.Protocol.Script.unregister(script.id.split("_").pop());
+        delete this.pendingRequests[script.id];
+        if (script.parentNode) {
+            script.parentNode.removeChild(script);
+        }
+    },
+
+    /**
+     * Method: handleRead
+     * Individual callbacks are created for read, create and update, should
+     *     a subclass need to override each one separately.
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     the user callback.
+     * options - {Object} The user options passed to the read call.
+     */
+    handleRead: function(response, options) {
+        this.handleResponse(response, options);
+    },
+
+    /**
+     * Method: handleResponse
+     * Called by CRUD specific handlers.
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>} The response object to pass to
+     *     any user callback.
+     * options - {Object} The user options passed to the create, read, update,
+     *     or delete call.
+     */
+    handleResponse: function(response, options) {
+        if (options.callback) {
+            if (response.data) {
+                response.features = this.parseFeatures(response.data);
+                response.code = OpenLayers.Protocol.Response.SUCCESS;
+            } else {
+                response.code = OpenLayers.Protocol.Response.FAILURE;
+            }
+            this.destroyRequest(response.priv);
+            options.callback.call(options.scope, response);
+        }
+    },
+
+    /**
+     * Method: parseFeatures
+     * Read Script response body and return features.
+     *
+     * Parameters:
+     * data - {Object} The data sent to the callback function by the server.
+     *
+     * Returns:
+     * {Array({<OpenLayers.Feature.Vector>})} or
+     *     {<OpenLayers.Feature.Vector>} Array of features or a single feature.
+     */
+    parseFeatures: function(data) {
+        return this.format.read(data);
+    },
+
+    /**
+     * APIMethod: abort
+     * Abort an ongoing request.  If no response is provided, all pending 
+     *     requests will be aborted.
+     *
+     * Parameters:
+     * response - {<OpenLayers.Protocol.Response>} The response object returned
+     *     from a <read> request.
+     */
+    abort: function(response) {
+        if (response) {
+            this.destroyRequest(response.priv);
+        } else {
+            for (var key in this.pendingRequests) {
+                this.destroyRequest(this.pendingRequests[key]);
+            }
+        }
+    },
+    
+    /**
+     * APIMethod: destroy
+     * Clean up the protocol.
+     */
+    destroy: function() {
+        this.abort();
+        delete this.params;
+        delete this.format;
+        OpenLayers.Protocol.prototype.destroy.apply(this);
+    },
+
+    CLASS_NAME: "OpenLayers.Protocol.Script" 
+});
+
+(function() {
+    var o = OpenLayers.Protocol.Script;
+    var counter = 0;
+    var registry = {};
+    
+    /**
+     * Function: OpenLayers.Protocol.Script.register
+     * Register a callback for a newly created script.
+     *
+     * Parameters:
+     * callback: {Function} The callback to be executed when the newly added
+     *     script loads.  This callback will be called with a single argument
+     *     that is the JSON returned by the service.
+     *
+     * Returns:
+     * {Number} An identifier for retreiving the registered callback.
+     */
+    o.register = function(callback) {
+        var id = ++counter;
+        registry[id] = callback;
+        return id;
+    };
+    
+    /**
+     * Function: OpenLayers.Protocol.Script.unregister
+     * Unregister a callback previously registered with the register function.
+     *
+     * Parameters:
+     * id: {Number} The identifer returned by the register function.
+     */
+    o.unregister = function(id) {
+        delete registry[id];
+    };
+    
+    /**
+     * Function: OpenLayers.Protocol.Script.getCallback
+     * Retreive and unregister a callback.  A call to this function is the "P" 
+     * in JSONP.  For example, a script may be added with a src attribute 
+     * http://example.com/features.json?callback=OpenLayers.Protocol.Script.getCallback(1)
+     *
+     * Parameters:
+     * id: {Number} The identifer returned by the register function.
+     */
+    o.getCallback = function(id) {
+        var callback = registry[id];
+        o.unregister(id);
+        return callback;
+    };
+})();
+

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Protocol/WFS/v1.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Protocol/WFS/v1.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Protocol/WFS/v1.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -149,6 +149,10 @@
      *     format's read method (that is only about reading transaction
      *     responses).
      *
+     * Parameters:
+     * options - {Object} Options for the read operation, in addition to the
+     *     options set on the instance (options set here will take precedence).
+     *
      * To use a configured protocol to get e.g. a WFS hit count, applications
      * could do the following:
      *
@@ -162,6 +166,16 @@
      *     }
      * });
      * (end)
+     *
+     * To limit the attributes returned by the GetFeature request, applications
+     * can use the propertyNames option to specify the properties to include in
+     * the response:
+     *
+     * (code)
+     * protocol.read({
+     *     propertyNames: ["DURATION", "INTENSITY"]
+     * });
+     * (end)
      */
     read: function(options) {
         OpenLayers.Protocol.prototype.read.apply(this, arguments);
@@ -183,6 +197,30 @@
 
         return response;
     },
+
+    /**
+     * APIMethod: setFeatureType
+     * Change the feature type on the fly.
+     *
+     * Parameters:
+     * featureType - {String} Local (without prefix) feature typeName.
+     */
+    setFeatureType: function(featureType) {
+        this.featureType = featureType;
+        this.format.featureType = featureType;
+    },
+ 
+    /**
+     * APIMethod: setGeometryName
+     * Sets the geometryName option after instantiation.
+     *
+     * Parameters:
+     * geometryName - {String} Name of geometry attribute.
+     */
+    setGeometryName: function(geometryName) {
+        this.geometryName = geometryName;
+        this.format.geometryName = geometryName;
+    },
     
     /**
      * Method: handleRead
@@ -250,7 +288,7 @@
      *     is used.
      *
      * Parameters:
-     * features - {Array(<OpenLayers.Feature.Vector>}
+     * features - {Array(<OpenLayers.Feature.Vector>)}
      *
      * Returns:
      * {<OpenLayers.Protocol.Response>} A response object with a features

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -485,7 +485,11 @@
         this.setCanvasStyle("reset");
         this.canvas.fillStyle = style.fontColor;
         this.canvas.globalAlpha = style.fontOpacity || 1.0;
-        var fontStyle = style.fontWeight + " " + style.fontSize + " " + style.fontFamily;
+        var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
+                         "normal", // "font-variant" not supported
+                         style.fontWeight ? style.fontWeight : "normal",
+                         style.fontSize ? style.fontSize : "10px",
+                         style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
         if (this.canvas.fillText) {
             // HTML5
             var labelAlign =

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Elements.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Elements.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Elements.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -390,7 +390,7 @@
     BACKGROUND_ID_SUFFIX: "_background",
     
     /**
-     * Constant: BACKGROUND_ID_SUFFIX
+     * Constant: LABEL_ID_SUFFIX
      * {String}
      */
     LABEL_ID_SUFFIX: "_label",

Copied: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/NG.js (from rev 11762, trunk/openlayers/lib/OpenLayers/Renderer/NG.js)
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/NG.js	                        (rev 0)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/NG.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,141 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Renderer/Elements.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.NG
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Renderer.Elements>
+ */
+OpenLayers.Renderer.NG = OpenLayers.Class(OpenLayers.Renderer.Elements, {
+    
+    /**
+     * Constant: labelNodeType
+     * {String} The node type for text label containers. To be defined by
+     * subclasses.
+     */
+    labelNodeType: null,
+    
+    /**
+     * Constructor: OpenLayers.Renderer.NG
+     * 
+     * Parameters:
+     * containerID - {String}
+     * options - {Object} options for this renderer. Supported options are:
+     *     * yOrdering - {Boolean} Whether to use y-ordering
+     *     * zIndexing - {Boolean} Whether to use z-indexing. Will be ignored
+     *         if yOrdering is set to true.
+     */
+    initialize: function(containerID, options) {
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, arguments);
+    },
+
+    /**
+     * Method: destroy
+     */
+    destroy: function() {
+        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
+    },
+
+    /**
+     * Method: updateDimensions
+     * To be extended by subclasses - here we set positioning related styles
+     * on HTML elements, subclasses have to do the same for renderer specific
+     * elements (e.g. viesBox, width and height of the rendererRoot)
+     */
+    updateDimensions: function() {
+        var mapExtent = this.map.getExtent();
+        var renderExtent = this.map.getMaxExtent();
+        this.setExtent(renderExtent, true);
+        var res = this.getResolution();
+        var div = this.rendererRoot.parentNode;
+        var layerLeft = parseFloat(div.parentNode.style.left);
+        var layerTop = parseFloat(div.parentNode.style.top);
+        div.style.left = ((renderExtent.left - mapExtent.left) / res - layerLeft) + "px";
+        div.style.top = ((mapExtent.top - renderExtent.top) / res - layerTop) + "px";
+    },
+    
+    /**
+     * Method: resize
+     */
+    setSize: function() {
+        this.map.getExtent() && this.updateDimensions();
+    },
+
+    /**
+     * Method: drawFeature
+     * Draw the feature.  The optional style argument can be used
+     * to override the feature's own style.  This method should only
+     * be called from layer.drawFeature().
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>} 
+     * style - {<Object>}
+     * 
+     * Returns:
+     * {Boolean} true if the feature has been drawn completely, false if not,
+     *     undefined if the feature had no geometry
+     */
+    drawFeature: function(feature, style) {
+        if(style == null) {
+            style = feature.style;
+        }
+        if (feature.geometry) {
+            var rendered = this.drawGeometry(feature.geometry, style, feature.id);
+            if(rendered !== false && style.label) {
+                var location = feature.geometry.getCentroid(); 
+                this.drawText(feature.id, style, location);
+            } else {
+                this.removeText(feature.id);
+            }
+            return rendered;
+        }
+    },
+    
+    /**
+     * Method: drawText
+     * Function for drawing text labels.
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * featureId - {String|DOMElement}
+     * style - {Object}
+     * location - {<OpenLayers.Geometry.Point>}, will be modified inline
+     *
+     * Returns:
+     * {DOMElement} container holding the text label (to be populated by
+     * subclasses)
+     */
+    drawText: function(featureId, style, location) {
+        var label;
+        if (typeof featureId !== "string") {
+            label = featureId;
+        } else {
+            label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, this.labelNodeType);
+            label._featureId = featureId;
+        }
+        label._style = style;
+        label._x = location.x;
+        label._y = location.y;
+        if(style.labelXOffset || style.labelYOffset) {
+            var xOffset = isNaN(style.labelXOffset) ? 0 : style.labelXOffset;
+            var yOffset = isNaN(style.labelYOffset) ? 0 : style.labelYOffset;
+            var res = this.getResolution();
+            location.move(xOffset*res, yOffset*res);
+        }
+
+        if(label.parentNode !== this.textRoot) {
+            this.textRoot.appendChild(label);
+        }   
+
+        return label;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.NG"
+});

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -260,6 +260,10 @@
                 
                 if (style.graphicTitle) {
                     node.setAttributeNS(null, "title", style.graphicTitle);
+                    //Standards-conformant SVG
+                    var label = this.nodeFactory(null, "title");
+                    label.textContent = style.graphicTitle;
+                    node.appendChild(label);
                 }
                 if (style.graphicWidth && style.graphicHeight) {
                   node.setAttributeNS(null, "preserveAspectRatio", "none");
@@ -281,13 +285,14 @@
                 node.setAttributeNS(null, "height", height);
                 node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
                 node.setAttributeNS(null, "style", "opacity: "+opacity);
+                node.onclick = OpenLayers.Renderer.SVG.preventDefault;
             } else if (this.isComplexSymbol(style.graphicName)) {
                 // the symbol viewBox is three times as large as the symbol
                 var offset = style.pointRadius * 3;
                 var size = offset * 2;
-                var id = this.importSymbol(style.graphicName);
+                var src = this.importSymbol(style.graphicName);
                 pos = this.getPosition(node);
-                widthFactor = this.symbolMetrics[id][0] * 3 / size;
+                widthFactor = this.symbolMetrics[src.id][0] * 3 / size;
                 
                 // remove the node from the dom before we modify it. This
                 // prevents various rendering issues in Safari and FF
@@ -303,7 +308,6 @@
                 // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985 
                 // and this email thread
                 // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html
-                var src = document.getElementById(id);
                 node.firstChild && node.removeChild(node.firstChild);
                 node.appendChild(src.firstChild.cloneNode(true));
                 node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox"));
@@ -334,7 +338,7 @@
                         "rotate(" + rotation + " " + pos.x + " " + 
                         pos.y + ")"); 
                 } else {
-                    var metrics = this.symbolMetrics[id];
+                    var metrics = this.symbolMetrics[src.id];
                     node.firstChild.setAttributeNS(null, "transform", "rotate(" 
                         + rotation + " " 
                         + metrics[1] + " "
@@ -708,6 +712,9 @@
         if (style.fontWeight) {
             label.setAttributeNS(null, "font-weight", style.fontWeight);
         }
+        if (style.fontStyle) {
+            label.setAttributeNS(null, "font-style", style.fontStyle);
+        }
         if(style.labelSelect === true) {
             label.setAttributeNS(null, "pointer-events", "visible");
             label._featureId = featureId;
@@ -795,9 +802,9 @@
      * inside the valid range.
      * 
      * Parameters:
-     * badComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
+     * badComponent - {<OpenLayers.Geometry.Point>} original geometry of the
      *     invalid point
-     * goodComponent - {<OpenLayers.Geometry.Point>)} original geometry of the
+     * goodComponent - {<OpenLayers.Geometry.Point>} original geometry of the
      *     valid point
      * Returns
      * {String} the SVG coordinate pair of the clipped point (like
@@ -876,7 +883,7 @@
      * graphicName - {String} name of the symbol to import
      * 
      * Returns:
-     * {String} - id of the imported symbol
+     * {DOMElement} - the imported symbol
      */      
     importSymbol: function (graphicName)  {
         if (!this.defs) {
@@ -886,8 +893,9 @@
         var id = this.container.id + "-" + graphicName;
         
         // check if symbol already exists in the defs
-        if (document.getElementById(id) != null) {
-            return id;
+        var existing = document.getElementById(id)
+        if (existing != null) {
+            return existing;
         }
         
         var symbol = OpenLayers.Renderer.symbol[graphicName];
@@ -929,7 +937,7 @@
         ];
         
         this.defs.appendChild(symbolNode);
-        return symbolNode.id;
+        return symbolNode;
     },
     
     /**
@@ -979,3 +987,12 @@
     "t": "-70%",
     "b": "0"    
 };
+
+/**
+ * Function: OpenLayers.Renderer.SVG.preventDefault
+ * Used to prevent default events (especially opening images in a new tab on
+ * ctrl-click) from being executed for externalGraphic symbols
+ */
+OpenLayers.Renderer.SVG.preventDefault = function(e) {
+    e.preventDefault && e.preventDefault();
+};

Copied: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js (from rev 11762, trunk/openlayers/lib/OpenLayers/Renderer/SVG2.js)
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js	                        (rev 0)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,796 @@
+/* Copyright (c) 2006-2011 by OpenLayers Contributors (see authors.txt for 
+ * full list of contributors). Published under the Clear BSD license.  
+ * See http://svn.openlayers.org/trunk/openlayers/license.txt for the
+ * full text of the license. */
+
+/**
+ * @requires OpenLayers/Renderer/NG.js
+ */
+
+/**
+ * Class: OpenLayers.Renderer.SVG2
+ * 
+ * Inherits from:
+ *  - <OpenLayers.Renderer.NG>
+ */
+OpenLayers.Renderer.SVG2 = OpenLayers.Class(OpenLayers.Renderer.NG, {
+
+    /** 
+     * Property: xmlns
+     * {String}
+     */
+    xmlns: "http://www.w3.org/2000/svg",
+    
+    /**
+     * Property: xlinkns
+     * {String}
+     */
+    xlinkns: "http://www.w3.org/1999/xlink",
+
+    /**
+     * Property: symbolMetrics
+     * {Object} Cache for symbol metrics according to their svg coordinate
+     *     space. This is an object keyed by the symbol's id, and values are
+     *     an object with size, x and y properties.
+     */
+    symbolMetrics: null,
+    
+    /**
+     * Constant: labelNodeType
+     * {String} The node type for text label containers.
+     */
+    labelNodeType: "g",
+
+    /**
+     * Constructor: OpenLayers.Renderer.SVG2
+     * 
+     * Parameters:
+     * containerID - {String}
+     */
+    initialize: function(containerID) {
+        if (!this.supported()) { 
+            return; 
+        }
+        OpenLayers.Renderer.Elements.prototype.initialize.apply(this, 
+                                                                arguments);
+        
+        this.symbolMetrics = {};
+    },
+
+    /**
+     * APIMethod: destroy
+     */
+    destroy: function() {
+        OpenLayers.Renderer.Elements.prototype.destroy.apply(this, arguments);
+    },
+    
+    /**
+     * APIMethod: supported
+     * 
+     * Returns:
+     * {Boolean} Whether or not the browser supports the SVG renderer
+     */
+    supported: function() {
+        var svgFeature = "http://www.w3.org/TR/SVG11/feature#";
+        return (document.implementation && 
+           (document.implementation.hasFeature("org.w3c.svg", "1.0") || 
+            document.implementation.hasFeature(svgFeature + "SVG", "1.1") || 
+            document.implementation.hasFeature(svgFeature + "BasicStructure", "1.1") ));
+    },    
+
+    /**
+     * Method: updateDimensions
+     */
+    updateDimensions: function() {
+        OpenLayers.Renderer.NG.prototype.updateDimensions.apply(this, arguments);
+        
+        var res = this.getResolution();
+        
+        var width = this.extent.getWidth();
+        var height = this.extent.getHeight();
+        
+        var extentString = [
+            this.extent.left,
+            -this.extent.top,
+            width,
+            height
+        ].join(" ");
+        this.rendererRoot.setAttributeNS(null, "viewBox", extentString);
+        this.rendererRoot.setAttributeNS(null, "width", width / res);
+        this.rendererRoot.setAttributeNS(null, "height", height / res);
+
+        // update styles for the new resolution
+        var nodes = this.vectorRoot.childNodes;
+        for (var i=0, len=nodes.length; i<len; ++i) {
+            this.setStyle(nodes[i]);
+        }
+        var textNodes = this.textRoot.childNodes;
+        var label;
+        for (var i=0, len=textNodes.length; i<len; ++i) {
+            var label = textNodes[i];
+            this.drawText(label, label._style,
+                new OpenLayers.Geometry.Point(label._x, label._y)
+            );
+        }
+    },
+    
+    /** 
+     * Method: getNodeType
+     * 
+     * Parameters:
+     * geometry - {<OpenLayers.Geometry>}
+     * style - {Object}
+     * 
+     * Returns:
+     * {String} The corresponding node type for the specified geometry
+     */
+    getNodeType: function(geometry, style) {
+        var nodeType = null;
+        switch (geometry.CLASS_NAME) {
+            case "OpenLayers.Geometry.Point":
+                if (style.externalGraphic) {
+                    nodeType = "image";
+                } else if (this.isComplexSymbol(style.graphicName)) {
+                    nodeType = "svg";
+                } else {
+                    nodeType = "circle";
+                }
+                break;
+            case "OpenLayers.Geometry.Rectangle":
+                nodeType = "rect";
+                break;
+            case "OpenLayers.Geometry.LineString":
+                nodeType = "polyline";
+                break;
+            case "OpenLayers.Geometry.LinearRing":
+                nodeType = "polygon";
+                break;
+            case "OpenLayers.Geometry.Polygon":
+            case "OpenLayers.Geometry.Curve":
+            case "OpenLayers.Geometry.Surface":
+                nodeType = "path";
+                break;
+            default:
+                break;
+        }
+        return nodeType;
+    },
+
+    /** 
+     * Method: setStyle
+     * Use to set all the style attributes to a SVG node.
+     * 
+     * Takes care to adjust stroke width and point radius to be
+     * resolution-relative
+     *
+     * Parameters:
+     * node - {SVGDomElement} An SVG element to decorate
+     * style - {Object}
+     * options - {Object} Currently supported options include 
+     *                              'isFilled' {Boolean} and
+     *                              'isStroked' {Boolean}
+     */
+    setStyle: function(node, style, options) {
+        style = style  || node._style;
+        options = options || node._options;
+        var resolution = this.getResolution();
+        var r = node._radius;
+        var widthFactor = resolution;
+        if (node._geometryClass == "OpenLayers.Geometry.Point" && r) {
+            node.style.visibility = "";
+            if (style.graphic === false) {
+                node.style.visibility = "hidden";
+            } else if (style.externalGraphic) {
+                
+                if (style.graphicTitle) {
+                    node.setAttributeNS(null, "title", style.graphicTitle);
+                    //Standards-conformant SVG 
+                    var label = this.nodeFactory(null, "title"); 
+                    label.textContent = style.graphicTitle; 
+                    node.appendChild(label); 
+                }
+                if (style.graphicWidth && style.graphicHeight) {
+                    node.setAttributeNS(null, "preserveAspectRatio", "none");
+                }
+                var width = style.graphicWidth || style.graphicHeight;
+                var height = style.graphicHeight || style.graphicWidth;
+                width = width ? width : style.pointRadius*2;
+                height = height ? height : style.pointRadius*2;
+                width *= resolution;
+                height *= resolution;
+                
+                var xOffset = (style.graphicXOffset != undefined) ?
+                    style.graphicXOffset * resolution : -(0.5 * width);
+                var yOffset = (style.graphicYOffset != undefined) ?
+                    style.graphicYOffset * resolution : -(0.5 * height);
+
+                var opacity = style.graphicOpacity || style.fillOpacity;
+                
+                node.setAttributeNS(null, "x", node._x + xOffset);
+                node.setAttributeNS(null, "y", node._y + yOffset);
+                node.setAttributeNS(null, "width", width);
+                node.setAttributeNS(null, "height", height);
+                node.setAttributeNS(this.xlinkns, "href", style.externalGraphic);
+                node.setAttributeNS(null, "style", "opacity: "+opacity);
+                node.onclick = OpenLayers.Renderer.SVG2.preventDefault;
+            } else if (this.isComplexSymbol(style.graphicName)) {
+                // the symbol viewBox is three times as large as the symbol
+                var offset = style.pointRadius * 3 * resolution;
+                var size = offset * 2;
+                var src = this.importSymbol(style.graphicName);
+                widthFactor = this.symbolMetrics[src.id].size * 3 / size * resolution;
+                
+                // remove the node from the dom before we modify it. This
+                // prevents various rendering issues in Safari and FF
+                var parent = node.parentNode;
+                var nextSibling = node.nextSibling;
+                if(parent) {
+                    parent.removeChild(node);
+                }
+                
+                // The more appropriate way to implement this would be use/defs, 
+                // but due to various issues in several browsers, it is safer to 
+                // copy the symbols instead of referencing them.  
+                // See e.g. ticket http://trac.osgeo.org/openlayers/ticket/2985  
+                // and this email thread 
+                // http://osgeo-org.1803224.n2.nabble.com/Select-Control-Ctrl-click-on-Feature-with-a-graphicName-opens-new-browser-window-tc5846039.html 
+                node.firstChild && node.removeChild(node.firstChild); 
+                node.appendChild(src.firstChild.cloneNode(true)); 
+                node.setAttributeNS(null, "viewBox", src.getAttributeNS(null, "viewBox")); 
+
+                node.setAttributeNS(null, "width", size);
+                node.setAttributeNS(null, "height", size);
+                node.setAttributeNS(null, "x", node._x - offset);
+                node.setAttributeNS(null, "y", node._y - offset);
+                
+                // now that the node has all its new properties, insert it
+                // back into the dom where it was
+                if(nextSibling) {
+                    parent.insertBefore(node, nextSibling);
+                } else if(parent) {
+                    parent.appendChild(node);
+                }
+            } else {
+                node.setAttributeNS(null, "r", style.pointRadius * resolution);
+            }
+
+            var rotation = style.rotation;
+            if (rotation !== undefined || node._rotation !== undefined) {
+                node._rotation = rotation;
+                rotation |= 0;
+                if (node.nodeName !== "svg") { 
+                    node.setAttributeNS(null, "transform", 
+                        ["rotate(", rotation, node._x, node._y, ")"].join(" ")
+                    ); 
+                } else {
+                    var metrics = this.symbolMetrics[src.id]; 
+                    node.firstChild.setAttributeNS(null, "transform",
+                        ["rotate(", rotation, metrics.x, metrics.y, ")"].join(" ")
+                    );
+                }
+            }
+        }
+        
+        if (options.isFilled) {
+            node.setAttributeNS(null, "fill", style.fillColor);
+            node.setAttributeNS(null, "fill-opacity", style.fillOpacity);
+        } else {
+            node.setAttributeNS(null, "fill", "none");
+        }
+
+        if (options.isStroked) {
+            node.setAttributeNS(null, "stroke", style.strokeColor);
+            node.setAttributeNS(null, "stroke-opacity", style.strokeOpacity);
+            node.setAttributeNS(null, "stroke-width", style.strokeWidth * widthFactor);
+            node.setAttributeNS(null, "stroke-linecap", style.strokeLinecap || "round");
+            // Hard-coded linejoin for now, to make it look the same as in VML.
+            // There is no strokeLinejoin property yet for symbolizers.
+            node.setAttributeNS(null, "stroke-linejoin", "round");
+            style.strokeDashstyle && node.setAttributeNS(null,
+                "stroke-dasharray", this.dashStyle(style, widthFactor));
+        } else {
+            node.setAttributeNS(null, "stroke", "none");
+        }
+        
+        if (style.pointerEvents) {
+            node.setAttributeNS(null, "pointer-events", style.pointerEvents);
+        }
+                
+        if (style.cursor != null) {
+            node.setAttributeNS(null, "cursor", style.cursor);
+        }
+        
+        return node;
+    },
+
+    /** 
+     * Method: dashStyle
+     * 
+     * Parameters:
+     * style - {Object}
+     * widthFactor - {Number}
+     * 
+     * Returns:
+     * {String} A SVG compliant 'stroke-dasharray' value
+     */
+    dashStyle: function(style, widthFactor) {
+        var w = style.strokeWidth * widthFactor;
+        var str = style.strokeDashstyle;
+        switch (str) {
+            case 'solid':
+                return 'none';
+            case 'dot':
+                return [widthFactor, 4 * w].join();
+            case 'dash':
+                return [4 * w, 4 * w].join();
+            case 'dashdot':
+                return [4 * w, 4 * w, widthFactor, 4 * w].join();
+            case 'longdash':
+                return [8 * w, 4 * w].join();
+            case 'longdashdot':
+                return [8 * w, 4 * w, widthFactor, 4 * w].join();
+            default:
+                var parts = OpenLayers.String.trim(str).split(/\s+/g);
+                for (var i=0, ii=parts.length; i<ii; i++) {
+                    parts[i] = parts[i] * widthFactor;
+                }
+                return parts.join();            
+        }
+    },
+    
+    /** 
+     * Method: createNode
+     * 
+     * Parameters:
+     * type - {String} Kind of node to draw
+     * id - {String} Id for node
+     * 
+     * Returns:
+     * {DOMElement} A new node of the given type and id
+     */
+    createNode: function(type, id) {
+        var node = document.createElementNS(this.xmlns, type);
+        if (id) {
+            node.setAttributeNS(null, "id", id);
+        }
+        return node;    
+    },
+    
+    /** 
+     * Method: nodeTypeCompare
+     * 
+     * Parameters:
+     * node - {SVGDomElement} An SVG element
+     * type - {String} Kind of node
+     * 
+     * Returns:
+     * {Boolean} Whether or not the specified node is of the specified type
+     */
+    nodeTypeCompare: function(node, type) {
+        return (type == node.nodeName);
+    },
+   
+    /**
+     * Method: createRenderRoot
+     * 
+     * Returns:
+     * {DOMElement} The specific render engine's root element
+     */
+    createRenderRoot: function() {
+        return this.nodeFactory(this.container.id + "_svgRoot", "svg");
+    },
+
+    /**
+     * Method: createRoot
+     * 
+     * Parameter:
+     * suffix - {String} suffix to append to the id
+     * 
+     * Returns:
+     * {DOMElement}
+     */
+    createRoot: function(suffix) {
+        return this.nodeFactory(this.container.id + suffix, "g");
+    },
+
+    /**
+     * Method: createDefs
+     *
+     * Returns:
+     * {DOMElement} The element to which we'll add the symbol definitions
+     */
+    createDefs: function() {
+        var defs = this.nodeFactory(this.container.id + "_defs", "defs");
+        this.rendererRoot.appendChild(defs);
+        return defs;
+    },
+
+    /**************************************
+     *                                    *
+     *     GEOMETRY DRAWING FUNCTIONS     *
+     *                                    *
+     **************************************/
+
+    /**
+     * Method: drawPoint
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the point
+     */ 
+    drawPoint: function(node, geometry) {
+        return this.drawCircle(node, geometry, 1);
+    },
+
+    /**
+     * Method: drawCircle
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * radius - {Float}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the circle
+     */
+    drawCircle: function(node, geometry, radius) {
+        var x = geometry.x;
+        var y = -geometry.y;
+        node.setAttributeNS(null, "cx", x);
+        node.setAttributeNS(null, "cy", y);
+        node._x = x;
+        node._y = y;
+        node._radius = radius;
+        return node;
+    },
+    
+    /**
+     * Method: drawLineString
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components of
+     *     the linestring, or false if nothing could be drawn
+     */ 
+    drawLineString: function(node, geometry) {
+        var path = this.getComponentsString(geometry.components);
+        node.setAttributeNS(null, "points", path);
+        return node;
+    },
+    
+    /**
+     * Method: drawLinearRing
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the linear ring, or false if nothing could be drawn
+     */ 
+    drawLinearRing: function(node, geometry) {
+        var path = this.getComponentsString(geometry.components);
+        node.setAttributeNS(null, "points", path);
+        return node;
+    },
+    
+    /**
+     * Method: drawPolygon
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or null if the renderer could not draw all components
+     *     of the polygon, or false if nothing could be drawn
+     */ 
+    drawPolygon: function(node, geometry) {
+        var d = [];
+        var draw = true;
+        var complete = true;
+        var linearRingResult, path;
+        for (var j=0, len=geometry.components.length; j<len; j++) {
+            d.push("M");
+            path = this.getComponentsString(
+                geometry.components[j].components, " ");
+            d.push(path);
+        }
+        d.push("z");
+        node.setAttributeNS(null, "d", d.join(" "));
+        node.setAttributeNS(null, "fill-rule", "evenodd");
+        return node;
+    },
+    
+    /**
+     * Method: drawRectangle
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the rectangle
+     */ 
+    drawRectangle: function(node, geometry) {
+        node.setAttributeNS(null, "x", geometry.x);
+        node.setAttributeNS(null, "y", -geometry.y);
+        node.setAttributeNS(null, "width", geometry.width);
+        node.setAttributeNS(null, "height", geometry.height);
+        return node;
+    },
+    
+    /**
+     * Method: drawSurface
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * node - {DOMElement}
+     * geometry - {<OpenLayers.Geometry>}
+     * 
+     * Returns:
+     * {DOMElement} or false if the renderer could not draw the surface
+     */ 
+    drawSurface: function(node, geometry) {
+
+        // create the svg path string representation
+        var d = [];
+        var draw = true;
+        for (var i=0, len=geometry.components.length; i<len; i++) {
+            if ((i%3) == 0 && (i/3) == 0) {
+                var component = this.getShortString(geometry.components[i]);
+                d.push("M", component);
+            } else if ((i%3) == 1) {
+                var component = this.getShortString(geometry.components[i]);
+                d.push("C", component);
+            } else {
+                var component = this.getShortString(geometry.components[i]);
+                d.push(component);
+            }
+        }
+        d.push("Z");
+        node.setAttributeNS(null, "d", d.join(" "));
+        return node;
+    },
+    
+    /**
+     * Method: drawText
+     * Function for drawing text labels.
+     * This method is only called by the renderer itself.
+     * 
+     * Parameters: 
+     * featureId - {String|DOMElement}
+     * style - {Object}
+     * location - {<OpenLayers.Geometry.Point>}, will be modified inline
+     *
+     * Returns:
+     * {DOMElement} container holding the text label
+     */
+    drawText: function(featureId, style, location) {
+        var g = OpenLayers.Renderer.NG.prototype.drawText.apply(this, arguments);
+        var text = g.firstChild ||
+            this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_text", "text");
+        var tspan = text.firstChild ||
+            this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan");
+
+        var res = this.getResolution();
+        text.setAttributeNS(null, "x", location.x / res);
+        text.setAttributeNS(null, "y", - location.y / res);
+        g.setAttributeNS(null, "transform", "scale(" + res + ")");
+        
+        if (style.fontColor) {
+            text.setAttributeNS(null, "fill", style.fontColor);
+        }
+        if (style.fontOpacity) {
+            text.setAttributeNS(null, "opacity", style.fontOpacity);
+        }
+        if (style.fontFamily) {
+            text.setAttributeNS(null, "font-family", style.fontFamily);
+        }
+        if (style.fontSize) {
+            text.setAttributeNS(null, "font-size", style.fontSize);
+        }
+        if (style.fontWeight) {
+            text.setAttributeNS(null, "font-weight", style.fontWeight);
+        }
+        if (style.fontStyle) {
+            text.setAttributeNS(null, "font-style", style.fontStyle);
+        }
+        if(style.labelSelect === true) {
+            text.setAttributeNS(null, "pointer-events", "visible");
+            text._featureId = featureId;
+            tspan._featureId = featureId;
+        } else {
+            text.setAttributeNS(null, "pointer-events", "none");
+        }
+        var align = style.labelAlign || "cm";
+        text.setAttributeNS(null, "text-anchor",
+            OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[0]] || "middle");
+
+        if (OpenLayers.IS_GECKO === true) {
+            text.setAttributeNS(null, "dominant-baseline",
+                OpenLayers.Renderer.SVG2.LABEL_ALIGN[align[1]] || "central");
+        } else {
+            tspan.setAttributeNS(null, "baseline-shift",
+                OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
+        }
+
+        tspan.textContent = style.label;
+        
+        if(!text.parentNode) {
+            text.appendChild(tspan);
+            g.appendChild(text);
+        }
+        
+        return g;
+    },
+    
+    /** 
+     * Method: getComponentString
+     * 
+     * Parameters:
+     * components - {Array(<OpenLayers.Geometry.Point>)} Array of points
+     * separator - {String} character between coordinate pairs. Defaults to ","
+     * 
+     * Returns:
+     * {Object} hash with properties "path" (the string created from the
+     *     components and "complete" (false if the renderer was unable to
+     *     draw all components)
+     */
+    getComponentsString: function(components, separator) {
+        var len = components.length;
+        var strings = new Array(len);
+        for (var i=0; i<len; i++) {
+            strings[i] = this.getShortString(components[i]);
+        }
+
+        return strings.join(separator || ",");
+    },
+    
+    /** 
+     * Method: getShortString
+     * 
+     * Parameters:
+     * point - {<OpenLayers.Geometry.Point>}
+     * 
+     * Returns:
+     * {String} or false if point is outside the valid range
+     */
+    getShortString: function(point) {
+        return point.x + "," + (-point.y);
+    },
+    
+    /**
+     * Method: importSymbol
+     * add a new symbol definition from the rendererer's symbol hash
+     * 
+     * Parameters:
+     * graphicName - {String} name of the symbol to import
+     * 
+     * Returns:
+     * {DOMElement} - the imported symbol
+     */      
+    importSymbol: function (graphicName)  {
+        if (!this.defs) {
+            // create svg defs tag
+            this.defs = this.createDefs();
+        }
+        var id = this.container.id + "-" + graphicName;
+        
+        // check if symbol already exists in the defs
+        var existing = document.getElementById(id);
+        if (existing != null) {
+            return existing;
+        }
+        
+        var symbol = OpenLayers.Renderer.symbol[graphicName];
+        if (!symbol) {
+            throw new Error(graphicName + ' is not a valid symbol name');
+        }
+
+        var symbolNode = this.nodeFactory(id, "symbol");
+        var node = this.nodeFactory(null, "polygon");
+        symbolNode.appendChild(node);
+        var symbolExtent = new OpenLayers.Bounds(
+                                    Number.MAX_VALUE, Number.MAX_VALUE, 0, 0);
+
+        var points = [];
+        var x,y;
+        for (var i=0, len=symbol.length; i<len; i=i+2) {
+            x = symbol[i];
+            y = symbol[i+1];
+            symbolExtent.left = Math.min(symbolExtent.left, x);
+            symbolExtent.bottom = Math.min(symbolExtent.bottom, y);
+            symbolExtent.right = Math.max(symbolExtent.right, x);
+            symbolExtent.top = Math.max(symbolExtent.top, y);
+            points.push(x, ",", y);
+        }
+        
+        node.setAttributeNS(null, "points", points.join(" "));
+        
+        var width = symbolExtent.getWidth();
+        var height = symbolExtent.getHeight();
+        // create a viewBox three times as large as the symbol itself,
+        // to allow for strokeWidth being displayed correctly at the corners.
+        var viewBox = [symbolExtent.left - width,
+                        symbolExtent.bottom - height, width * 3, height * 3];
+        symbolNode.setAttributeNS(null, "viewBox", viewBox.join(" "));
+        this.symbolMetrics[id] = {
+            size: Math.max(width, height),
+            x: symbolExtent.getCenterLonLat().lon,
+            y: symbolExtent.getCenterLonLat().lat
+        };
+        
+        this.defs.appendChild(symbolNode);
+        return symbolNode;
+    },
+    
+    /**
+     * Method: getFeatureIdFromEvent
+     * 
+     * Parameters:
+     * evt - {Object} An <OpenLayers.Event> object
+     *
+     * Returns:
+     * {<OpenLayers.Geometry>} A geometry from an event that 
+     *     happened on a layer.
+     */
+    getFeatureIdFromEvent: function(evt) {
+        var featureId = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent.apply(this, arguments);
+        if(!featureId) {
+            var target = evt.target;
+            featureId = target.parentNode && target != this.rendererRoot &&
+                target.parentNode._featureId;
+        }
+        return featureId;
+    },
+
+    CLASS_NAME: "OpenLayers.Renderer.SVG2"
+});
+
+/**
+ * Constant: OpenLayers.Renderer.SVG2.LABEL_ALIGN
+ * {Object}
+ */
+OpenLayers.Renderer.SVG2.LABEL_ALIGN = {
+    "l": "start",
+    "r": "end",
+    "b": "bottom",
+    "t": "hanging"
+};
+
+/**
+ * Constant: OpenLayers.Renderer.SVG2.LABEL_VSHIFT
+ * {Object}
+ */
+OpenLayers.Renderer.SVG2.LABEL_VSHIFT = {
+    // according to
+    // http://www.w3.org/Graphics/SVG/Test/20061213/htmlObjectHarness/full-text-align-02-b.html
+    // a baseline-shift of -70% shifts the text exactly from the
+    // bottom to the top of the baseline, so -35% moves the text to
+    // the center of the baseline.
+    "t": "-70%",
+    "b": "0"    
+};
+
+/** 
+ * Function: OpenLayers.Renderer.SVG2.preventDefault 
+ * Used to prevent default events (especially opening images in a new tab on 
+ * ctrl-click) from being executed for externalGraphic and graphicName symbols 
+ */ 
+OpenLayers.Renderer.SVG2.preventDefault = function(e) { 
+    e.preventDefault && e.preventDefault(); 
+};

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/VML.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/VML.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/VML.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -837,6 +837,9 @@
 
         textbox.innerText = style.label;
 
+        if (style.cursor != "inherit" && style.cursor != null) {
+            textbox.style.cursor = style.cursor;
+        }
         if (style.fontColor) {
             textbox.style.color = style.fontColor;
         }
@@ -852,6 +855,9 @@
         if (style.fontWeight) {
             textbox.style.fontWeight = style.fontWeight;
         }
+        if (style.fontStyle) {
+            textbox.style.fontStyle = style.fontStyle;
+        }
         if(style.labelSelect === true) {
             label._featureId = featureId;
             textbox._featureId = featureId;

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Request.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Request.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Request.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -229,6 +229,10 @@
                 config.failure;
         }
 
+        if (OpenLayers.Util.createUrlObject(config.url).protocol == "file:" &&
+                                                        request.responseText) {
+            request.status = 200;
+        }
         complete(request);
 
         if (!request.status || (request.status >= 200 && request.status < 300)) {

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Strategy/BBOX.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Strategy/BBOX.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Strategy/BBOX.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -68,9 +68,6 @@
      * options - {Object} Optional object whose properties will be set on the
      *     instance.
      */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
     
     /**
      * Method: activate
@@ -90,6 +87,14 @@
                 "refresh": this.update,
                 scope: this
             });
+            if(this.layer.visibility == true) {
+                this.update();
+            } else {
+                this.layer.events.on({
+                    "visibilitychanged": this.update,
+                    scope: this
+                });
+            }
         }
         return activated;
     },
@@ -106,10 +111,8 @@
         if(deactivated) {
             this.layer.events.un({
                 "moveend": this.update,
-                scope: this
-            });
-            this.layer.events.un({
                 "refresh": this.update,
+                "visibilitychanged": this.update,
                 scope: this
             });
         }
@@ -127,10 +130,11 @@
      */
     update: function(options) {
         var mapBounds = this.getMapBounds();
-        if ((options && options.force) || this.invalidBounds(mapBounds)) {
+        if (mapBounds !== null && ((options && options.force) ||
+                                   this.invalidBounds(mapBounds))) {
             this.calculateBounds(mapBounds);
             this.resolution = this.layer.map.getResolution(); 
-            this.triggerRead();
+            this.triggerRead(options);
         }
     },
     
@@ -142,8 +146,12 @@
      * {<OpenLayers.Bounds>} Map bounds in the projection of the layer.
      */
     getMapBounds: function() {
+        if (this.layer.map === null) {
+            return null;
+        }
         var bounds = this.layer.map.getExtent();
-        if(!this.layer.projection.equals(this.layer.map.getProjectionObject())) {
+        if(bounds && !this.layer.projection.equals(
+                this.layer.map.getProjectionObject())) {
             bounds = bounds.clone().transform(
                 this.layer.map.getProjectionObject(), this.layer.projection
             );
@@ -202,21 +210,25 @@
     /**
      * Method: triggerRead
      *
+     * Parameters:
+     * options - Additional options for the protocol's read method (optional)
+     *
      * Returns:
      * {<OpenLayers.Protocol.Response>} The protocol response object
      *      returned by the layer protocol.
      */
-    triggerRead: function() {
+    triggerRead: function(options) {
         if (this.response) {
             this.layer.protocol.abort(this.response);
             this.layer.events.triggerEvent("loadend");
         }
         this.layer.events.triggerEvent("loadstart");
-        this.response = this.layer.protocol.read({
-            filter: this.createFilter(),
-            callback: this.merge,
-            scope: this
-        });
+        this.response = this.layer.protocol.read(
+            OpenLayers.Util.applyDefaults({
+                filter: this.createFilter(),
+                callback: this.merge,
+                scope: this
+        }, options));
     },
  
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Cluster.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Cluster.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Cluster.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -66,9 +66,6 @@
      * options - {Object} Optional object whose properties will be set on the
      *     instance.
      */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
     
     /**
      * APIMethod: activate

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Filter.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Filter.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Filter.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -47,9 +47,6 @@
      * options - {Object} Optional object whose properties will be set on the
      *     instance.
      */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
 
     /**
      * APIMethod: activate

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Fixed.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Fixed.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Fixed.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -32,19 +32,8 @@
      * options - {Object} Optional object whose properties will be set on the
      *     instance.
      */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
 
     /**
-     * APIMethod: destroy
-     * Clean up the strategy.
-     */
-    destroy: function() {
-        OpenLayers.Strategy.prototype.destroy.apply(this, arguments);
-    },
-
-    /**
      * Method: activate
      * Activate the strategy: load data or add listener to load when visible
      *

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Paging.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Paging.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Paging.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -48,9 +48,6 @@
      * options - {Object} Optional object whose properties will be set on the
      *     instance.
      */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
     
     /**
      * APIMethod: activate

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Refresh.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Refresh.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Strategy/Refresh.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -45,9 +45,6 @@
      * options - {Object} Optional object whose properties will be set on the
      *     instance.
      */
-    initialize: function(options) {
-        OpenLayers.Strategy.prototype.initialize.apply(this, [options]);
-    },
    
     /**
      * APIMethod: activate

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Tile/Image.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Tile/Image.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Tile/Image.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -52,14 +52,6 @@
     isBackBuffer: false,
     
     /**
-     * Property: lastRatio
-     * {Float} Used in transition code only.  This is the previous ratio
-     *     of the back buffer tile resolution to the map resolution.  Compared
-     *     with the current ratio to determine if zooming occurred.
-     */
-    lastRatio: 1,
-
-    /**
      * Property: isFirstDraw
      * {Boolean} Is this the first time the tile is being drawn?
      *     This is used to force resetBackBuffer to synchronize
@@ -532,7 +524,7 @@
         
         // if the ratio is not the same as it was last time (i.e. we are
         // zooming), then we need to adjust the backBuffer tile
-        if (ratio != this.lastRatio) {
+        if (ratio != 1) {
             if (this.layer.transitionEffect == 'resize') {
                 // In this case, we can just immediately resize the 
                 // backBufferTile.
@@ -575,7 +567,6 @@
                 this.backBufferTile.hide();
             }
         }
-        this.lastRatio = ratio;
 
     },
     

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Util.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Util.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Util.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -22,6 +22,11 @@
 /** 
  * Function: getElement
  * This is the old $() from prototype
+ *
+ * Parameters:
+ * e - {String or DOMElement or Window}
+ * Return:
+ * {Array(DOMElement)}
  */
 OpenLayers.Util.getElement = function() {
     var elements = [];
@@ -105,7 +110,7 @@
  * 
  * Parameters:
  * array - {Array}
- * obj - {Object}
+ * obj - {*}
  * 
  * Returns:
  * {Integer} The index at, which the first object was found in the array.
@@ -134,6 +139,7 @@
  * null to an individual parameter will avoid setting the attribute.
  *
  * Parameters:
+ * element - {DOMElement} DOM element to modify.
  * id - {String} The element id attribute to set.
  * px - {<OpenLayers.Pixel>} The left and top style position.
  * sz - {<OpenLayers.Size>}  The width and height style attributes.
@@ -404,6 +410,7 @@
 /** 
  * Function: modifyAlphaImageDiv
  * 
+ * Parameters:
  * div - {DOMElement} Div containing Alpha-adjusted Image
  * id - {String}
  * px - {<OpenLayers.Pixel>}
@@ -411,7 +418,7 @@
  * imgURL - {String}
  * position - {String}
  * border - {String}
- * sizing {String} 'crop', 'scale', or 'image'. Default is "scale"
+ * sizing - {String} 'crop', 'scale', or 'image'. Default is "scale"
  * opacity - {Float} Fractional value (0.0 - 1.0)
  */ 
 OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
@@ -452,6 +459,7 @@
 /** 
  * Function: createAlphaImageDiv
  * 
+ * Parameters:
  * id - {String}
  * px - {<OpenLayers.Pixel>}
  * sz - {<OpenLayers.Size>}
@@ -938,7 +946,7 @@
  * lonlat  - {<OpenLayers.LonLat>} (or any object with both .lat, .lon
  *     properties) The start point.
  * brng     - {Float} The bearing (degrees).
- * distance - {Float} The ground distance (meters).
+ * dist     - {Float} The ground distance (meters).
  *
  * Returns:
  * {<OpenLayers.LonLat>} The destination point.
@@ -1663,6 +1671,8 @@
  *     useful in the case where we have a limit in one dimension and must 
  *     therefore meaure the flow in the other dimension.
  * options - {Object}
+ *
+ * Allowed Options:
  *     displayClass - {String} Optional parameter.  A CSS class name(s) string
  *         to provide the CSS context of the rendered content.
  *     containerElement - {DOMElement} Optional parameter. Insert the HTML to 

Modified: sandbox/tschaub/canvas/lib/OpenLayers.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/OpenLayers.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -155,6 +155,7 @@
                 "OpenLayers/Layer/TMS.js",
                 "OpenLayers/Layer/TileCache.js",
                 "OpenLayers/Layer/Zoomify.js",
+                "OpenLayers/Layer/ArcGISCache.js",
                 "OpenLayers/Popup/Anchored.js",
                 "OpenLayers/Popup/AnchoredBubble.js",
                 "OpenLayers/Popup/Framed.js",
@@ -223,7 +224,9 @@
                 "OpenLayers/Geometry/Surface.js",
                 "OpenLayers/Renderer.js",
                 "OpenLayers/Renderer/Elements.js",
+                "OpenLayers/Renderer/NG.js",
                 "OpenLayers/Renderer/SVG.js",
+                "OpenLayers/Renderer/SVG2.js",
                 "OpenLayers/Renderer/Canvas.js",
                 "OpenLayers/Renderer/VML.js",
                 "OpenLayers/Layer/Vector.js",
@@ -241,6 +244,7 @@
                 "OpenLayers/Filter/Logical.js",
                 "OpenLayers/Filter/Comparison.js",
                 "OpenLayers/Filter/Spatial.js",
+                "OpenLayers/Filter/Function.js",                
                 "OpenLayers/Protocol.js",
                 "OpenLayers/Protocol/HTTP.js",
                 "OpenLayers/Protocol/SQL.js",
@@ -249,6 +253,7 @@
                 "OpenLayers/Protocol/WFS/v1.js",
                 "OpenLayers/Protocol/WFS/v1_0_0.js",
                 "OpenLayers/Protocol/WFS/v1_1_0.js",
+                "OpenLayers/Protocol/Script.js",
                 "OpenLayers/Protocol/SOS.js",
                 "OpenLayers/Protocol/SOS/v1_0_0.js",
                 "OpenLayers/Layer/PointTrack.js",
@@ -258,6 +263,7 @@
                 "OpenLayers/StyleMap.js",
                 "OpenLayers/Rule.js",
                 "OpenLayers/Format.js",
+                "OpenLayers/Format/QueryStringFilter.js",
                 "OpenLayers/Format/XML.js",
                 "OpenLayers/Format/Context.js",
                 "OpenLayers/Format/ArcXML.js",

Modified: sandbox/tschaub/canvas/lib/Rico/Color.js
===================================================================
--- sandbox/tschaub/canvas/lib/Rico/Color.js	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/lib/Rico/Color.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -1,5 +1,6 @@
 /** 
  * @requires OpenLayers/BaseTypes/Class.js
+ * @requires OpenLayers/BaseTypes/Element.js
  */
 
 
@@ -124,9 +125,8 @@
 OpenLayers.Rico.Color.createColorFromBackground = function(elem) {
 
    var actualColor = 
-      RicoUtil.getElementsComputedStyle(OpenLayers.Util.getElement(elem), 
-                                        "backgroundColor", 
-                                        "background-color");
+      OpenLayers.Element.getStyle(OpenLayers.Util.getElement(elem), 
+                                        "backgroundColor");
 
    if ( actualColor == "transparent" && elem.parentNode ) {
       return OpenLayers.Rico.Color.createColorFromBackground(elem.parentNode);

Modified: sandbox/tschaub/canvas/tests/BaseTypes/Bounds.html
===================================================================
--- sandbox/tschaub/canvas/tests/BaseTypes/Bounds.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/BaseTypes/Bounds.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -76,7 +76,7 @@
     function test_Bounds_toString(t) {
         t.plan( 1 );
         bounds = new OpenLayers.Bounds(1,2,3,4);
-        t.eq( bounds.toString(), "left-bottom=(1,2) right-top=(3,4)", "toString() returns correct value." ); 
+        t.eq( bounds.toString(), "1,2,3,4", "toString() returns correct value." ); 
     }
     function test_Bounds_toArray(t) {
         t.plan( 1 );

Modified: sandbox/tschaub/canvas/tests/Control/DrawFeature.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/DrawFeature.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/DrawFeature.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -94,7 +94,8 @@
         log = {};
         control.activate();
         t.eq(log.sketchstarted.type, "sketchstarted", "[activate] sketchstarted triggered");
-        t.geom_eq(log.sketchstarted.vertex, new OpenLayers.Geometry.Point(-250, 175), "[activate] correct vertex");
+        t.ok(isNaN(log.sketchstarted.vertex.x) && isNaN(log.sketchstarted.vertex.y),
+             "[activate] correct vertex (NaN)");
 
         log = {};
         map.events.triggerEvent("mousemove", {xy: new OpenLayers.Pixel(0, 0)});
@@ -123,7 +124,8 @@
                   ]),
                   "[dblclick] correct geometry");
         t.eq(log.sketchstarted.type, "sketchstarted", "[dblclick] sketchstarted triggered");
-        t.geom_eq(log.sketchstarted.vertex, new OpenLayers.Geometry.Point(-250, 175), "[dblclick] correct vertex");
+        t.ok(isNaN(log.sketchstarted.vertex.x) && isNaN(log.sketchstarted.vertex.y),
+             "[dblclick] correct vertex (NaN)");
 
         map.destroy();
     }

Modified: sandbox/tschaub/canvas/tests/Control/KeyboardDefaults.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/KeyboardDefaults.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/KeyboardDefaults.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -12,6 +12,18 @@
         t.eq( control.displayClass,  "olControlKeyboardDefaults", "displayClass is correct" );
     }
 
+    function test_Control_KeyboardDefaults_destroy (t) {
+        t.plan(2);
+    
+        map = new OpenLayers.Map('map');
+        var control = new OpenLayers.Control.KeyboardDefaults();
+        map.addControl(control);
+        t.ok(control.handler != null, "control.handler is created");
+        control.destroy();
+        t.ok(control.handler == null, "control.handler is null after destroy");
+        map.destroy();
+    }
+
     function test_Control_KeyboardDefaults_addControl (t) {
         t.plan( 4 );
 

Modified: sandbox/tschaub/canvas/tests/Control/Measure.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/Measure.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/Measure.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -96,6 +96,10 @@
                     measure: function(evt){
                         log.push(evt);
                     }
+                },
+                handlerOptions: {
+                    pixelTolerance: 0,
+                    dblclickTolerance: 0
                 }
             }
         );
@@ -131,8 +135,8 @@
             // wait for delay then confirm event was logged
             delay, function() {
                 t.eq(log.length, 1, "a) event logged")
-                t.ok(log[0] && log[0].type == "measurepartial", "a) event logged");
-                t.ok(log[0] && log[0].measure == 10, "a) correct measure");
+                t.eq(log[0] && log[0].type, "measurepartial", "a) event logged");
+                t.eq(log[0] && log[0].measure, 10, "a) correct measure");
                 
                 // b) move 10 pixels and click
                 trigger("mousemove", 0, 20);
@@ -145,8 +149,8 @@
             },
             delay, function() {
                 t.eq(log.length, 2, "b) event logged");
-                t.ok(log[1] && log[1].type == "measurepartial", "b) correct type");
-                t.ok(log[1] && log[1].measure == 20, "b) correct measure");
+                t.eq(log[1] && log[1].type, "measurepartial", "b) correct type");
+                t.eq(log[1] && log[1].measure, 20, "b) correct measure");
 
                 // c) move 10 pixels and click
                 trigger("mousemove", 0, 30);
@@ -161,8 +165,8 @@
             // wait for rest of delay and confirm event logged
             delay / 2, function() {
                 t.eq(log.length, 3, "c) event logged");
-                t.ok(log[2] && log[2].type == "measurepartial", "c) correct type");
-                t.ok(log[2] && log[2].measure == 30, "c) correct measure");
+                t.eq(log[2] && log[2].type, "measurepartial", "c) correct type");
+                t.eq(log[2] && log[2].measure, 30, "c) correct measure");
                 
                 // d) move 10 pixels and click
                 trigger("mousemove", 0, 40);
@@ -176,8 +180,8 @@
                 trigger("dblclick", 0, 40);
 
                 t.eq(log.length, 4, "e) event logged");
-                t.ok(log[3] && log[3].type == "measure", "e) correct type");
-                t.ok(log[3] && log[3].measure == 40, "e) correct measure");                
+                t.eq(log[3] && log[3].type, "measure", "e) correct type");
+                t.eq(log[3] && log[3].measure, 40, "e) correct measure");                
             },
             // wait for rest of delay and confirm no measurepartial logged
             delay, function() {
@@ -198,22 +202,22 @@
                 trigger("mousemove", 10, 0);
 
                 t.eq(log.length, 1, "g) event logged");
-                t.ok(log[0] && log[0].type == "measurepartial", "g) correct type");
-                t.ok(log[0] && log[0].measure == 10, "g) correct measure");
+                t.eq(log[0] && log[0].type, "measurepartial", "g) correct type");
+                t.eq(log[0] && log[0].measure, 10, "g) correct measure");
                 
                 // h) move 10 pixels
                 trigger("mousemove", 20, 0);
 
                 t.eq(log.length, 2, "h) event logged");
-                t.ok(log[1] && log[1].type == "measurepartial", "h) correct type");
-                t.ok(log[1] && log[1].measure == 20, "h) correct measure");
+                t.eq(log[1] && log[1].type, "measurepartial", "h) correct type");
+                t.eq(log[1] && log[1].measure, 20, "h) correct measure");
 
                 // i) mouse up to finish
                 trigger("mouseup", 20, 0);
 
                 t.eq(log.length, 3, "i) event logged");
-                t.ok(log[2] && log[2].type == "measure", "i) correct type");
-                t.ok(log[2] && log[2].measure == 20, "i) correct measure");
+                t.eq(log[2] && log[2].type, "measure", "i) correct type");
+                t.eq(log[2] && log[2].measure, 20, "i) correct measure");
 
                 // j) clean up
                 log = [];
@@ -254,6 +258,10 @@
                     measure: function(evt){
                         log.push(evt);
                     }
+                },
+                handlerOptions: {
+                    pixelTolerance: 0,
+                    dblclickTolerance: 0
                 }
             }
         );
@@ -296,10 +304,10 @@
 
                 // confirm measurepartial is fired 2 times
                 t.eq(log.length, 3, "b) event logged");
-                t.ok(log[1] && log[1].type == "measurepartial", "b) correct type");
-                t.ok(log[1] && log[1].measure == 20, "b) correct measure");
-                t.ok(log[2] && log[2].type == "measurepartial", "c) correct type");
-                t.ok(log[2] && log[2].measure == 30, "c) correct measure");
+                t.eq(log[1] && log[1].type, "measurepartial", "b) correct type");
+                t.eq(log[1] && log[1].measure, 20, "b) correct measure");
+                t.eq(log[2] && log[2].type, "measurepartial", "c) correct type");
+                t.eq(log[2] && log[2].measure, 30, "c) correct measure");
 
                 // d) switch immediate measurement off
                 control.setImmediate(false);
@@ -346,23 +354,23 @@
                 t.eq(log.length, 7, "i) no event fired yet");
             },
             delay, function() {
-                t.eq(log.length, 8, "i) event logged");
-                t.eq(log[7] && log[7].type, "measurepartial", "i) correct type");
-                t.eq(log[7] && log[7].measure, 60, "i) correct measure");
+                t.eq(log.length, 8, "j) event logged");
+                t.eq(log[7] && log[7].type, "measurepartial", "j) correct type");
+                t.eq(log[7] && log[7].measure, 60, "j) correct measure");
 
                 trigger("dblclick", 0, 60);
-                t.eq(log.length, 9, "i) event logged");
-                t.eq(log[8] && log[8].type, "measure", "i) correct type");
-                t.eq(log[8] && log[8].measure, 60, "i) correct measure");
+                t.eq(log.length, 9, "k) event logged");
+                t.eq(log[8] && log[8].type, "measure", "k) correct type");
+                t.eq(log[8] && log[8].measure, 60, "k) correct measure");
                 // clear log
                 log = [];
 
-                // j) clean up
+                // l) clean up
                 map.destroy();
                 // wait for delay and confirm event not logged
             },
             delay, function() {
-                t.eq(log.length, 0, "j) no event fired after destroy");
+                t.eq(log.length, 0, "l) no event fired after destroy");
             }
         );
     }

Modified: sandbox/tschaub/canvas/tests/Control/PanZoomBar.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/PanZoomBar.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/PanZoomBar.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -44,8 +44,9 @@
         control = new OpenLayers.Control.PanZoomBar();
         map.addControl(control);
         control.removeButtons();
+        var div = control.div;
         map.destroy();
-        t.eq(control.div.childNodes.length, 0, "control's div cleared.");
+        t.eq(div.childNodes.length, 0, "control's div cleared.");
         t.eq(control.zoombarDiv, null, "zoombar div nullified.")
     }
 
@@ -165,6 +166,50 @@
             t.ok(map.zoom >= 0, 'map.zoom is never < 0 after random handle drag with forceFixedZoomLevel=true and fractionalZoom=true');
         }
     }
+
+    function test_Control_PanZoomBar_shows (t) {
+        t.plan(22);
+
+        var control, map;
+
+        control = new OpenLayers.Control.PanZoomBar({panIcons: true, zoomWorldIcon: false});
+        map = new OpenLayers.Map('map', {controls: [control]});
+        t.eq(control.buttons.length, 6, "(a) pan, no world - expected number of buttons");
+        t.ok(control.buttons[0].id.match("_panup$"), "(a) pan, no world - pan up");
+        t.ok(control.buttons[1].id.match("_panleft$"), "(a) pan, no world - pan left");
+        t.ok(control.buttons[2].id.match("_panright$"), "(a) pan, no world - pan right");
+        t.ok(control.buttons[3].id.match("_pandown$"), "(a) pan, no world - pan down");
+        t.ok(control.buttons[4].id.match("_zoomin$"), "(a) pan, no world - zoom in");
+        t.ok(control.buttons[5].id.match("_zoomout$"), "(a) pan, no world - zoom out");
+        map.destroy();
+
+        control = new OpenLayers.Control.PanZoomBar({panIcons: true, zoomWorldIcon: true});
+        map = new OpenLayers.Map('map', {controls:[control]});
+        t.eq(control.buttons.length, 7, "(b) pan, world - expected number of buttons");
+        t.ok(control.buttons[0].id.match("_panup$"), "(b) pan, world - pan up");
+        t.ok(control.buttons[1].id.match("_panleft$"), "(b) pan, world - pan left");
+        t.ok(control.buttons[2].id.match("_zoomworld$"), "(b) pan, world - zoom world");
+        t.ok(control.buttons[3].id.match("_panright$"), "(b) pan, world - pan right");
+        t.ok(control.buttons[4].id.match("_pandown$"), "(b) pan, world - pan down");
+        t.ok(control.buttons[5].id.match("_zoomin$"), "(b) pan, world - zoom in");
+        t.ok(control.buttons[6].id.match("_zoomout$"), "(b) pan, world - zoom out");
+        map.destroy();
+
+        control = new OpenLayers.Control.PanZoomBar({panIcons: false, zoomWorldIcon: false});
+        map = new OpenLayers.Map('map', {controls:[control]});
+        t.eq(control.buttons.length, 2, "(c) no pan, no world - expected number of buttons");
+        t.ok(control.buttons[0].id.match("_zoomin$"), "(c) no pan, no world - zoom in");
+        t.ok(control.buttons[1].id.match("_zoomout$"), "(c) no pan, no world - zoom out");
+        map.destroy();
+
+        control = new OpenLayers.Control.PanZoomBar({panIcons: false, zoomWorldIcon: true});
+        map = new OpenLayers.Map('map', {controls:[control]});
+        t.eq(control.buttons.length, 3, "(d) no pan, world - expected number of buttons");
+        t.ok(control.buttons[0].id.match("_zoomin$"), "(d) no pan, world - zoom in");
+        t.ok(control.buttons[1].id.match("_zoomout$"), "(d) no pan, world - zoom out");
+        t.ok(control.buttons[2].id.match("_zoomworld$"), "(d) no pan, world - zoom world");
+        map.destroy();
+    }
   </script>
 </head>
 <body>

Modified: sandbox/tschaub/canvas/tests/Control/SLDSelect.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/SLDSelect.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/SLDSelect.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -82,7 +82,7 @@
         t.eq(map.layers.length, 2, "Selection layer has been created and added to the map");
         t.eq(map.layers[1] instanceof OpenLayers.Layer.WMS, true, "A WMS layer has been created as the selection layer");
         t.eq(map.layers[1].tileOptions.maxGetUrlLength, 2048, "Selection layer will automatically switch to HTTP Post if content gets longer than 2048");
-        var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>AAA64</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:Featur
 eTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+        var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>AAA64</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></s
 ld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
 
         t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "SLD generated correctly");
 
@@ -169,7 +169,7 @@
         var geometry = OpenLayers.Geometry.Polygon.createRegularPolygon(
             new OpenLayers.Geometry.Point(0, 0), 5, 4);
         control.select(geometry);
-        var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>KGNAT.VKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PolygonSymbolizer><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill></sld:PolygonSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sl
 d:NamedLayer><sld:Name>KGNAT.LKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.PKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml"><gml:coordinates de
 cimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill><sld:Stroke/></sld:Mark><sld:Size>10</sld:Size></sld:Graphic></sld:PointSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
+        var expected_sld = '<sld:StyledLayerDescriptor xmlns:sld="http://www.opengis.net/sld" version="1.0.0" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ogc="http://www.opengis.net/ogc" xmlns:gml="http://www.opengis.net/gml"><sld:NamedLayer><sld:Name>KGNAT.VKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PolygonSymbolizer><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill></sld:PolygonSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle>
 </sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.LKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:LineSymbolizer><sld:Stroke><sld:CssParameter name="stroke">#FF0000</sld:CssParameter><sld:CssParameter name="stroke-width">2</sld:CssParameter></sld:Stroke></sld:LineSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer><sld:NamedLayer><sld:Name>KGNAT.PKUNSTWERK</sld:Name><sld:UserStyle><sld:Name>default</sld:Name><sld:FeatureTypeStyle><sld:Rule><ogc:Filter xmlns:ogc="http://www.opengis.net/ogc"><ogc:BBOX><ogc:PropertyName>geometry</ogc:PropertyName><gml:Box xmlns:gml="http://
 www.opengis.net/gml" srsName="EPSG:4326"><gml:coordinates decimal="." cs="," ts=" ">-3.5355339059327,-3.5355339059327 3.5355339059327,3.5355339059327</gml:coordinates></gml:Box></ogc:BBOX></ogc:Filter><sld:PointSymbolizer><sld:Graphic><sld:Mark><sld:WellKnownName>square</sld:WellKnownName><sld:Fill><sld:CssParameter name="fill">#FF0000</sld:CssParameter></sld:Fill><sld:Stroke/></sld:Mark><sld:Size>10</sld:Size></sld:Graphic></sld:PointSymbolizer></sld:Rule></sld:FeatureTypeStyle></sld:UserStyle></sld:NamedLayer></sld:StyledLayerDescriptor>';
 
         t.xml_eq(map.layers[1].params.SLD_BODY, expected_sld, "SLD generated correctly");
         map.destroy();

Modified: sandbox/tschaub/canvas/tests/Control/SelectFeature.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/SelectFeature.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/SelectFeature.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -373,6 +373,114 @@
         map.events.triggerEvent("click", evt);
     }
 
+    // test for http://trac.openlayers.org/ticket/2812
+    function test_highlightOnly_toggle(t) {
+        t.plan(8);
+
+        /*
+         * setup
+         */
+
+        var map, layer, ctrl1, ctrl2, _feature, feature, evt, _style;
+
+        map = new OpenLayers.Map("map");
+        layer = new OpenLayers.Layer.Vector("name", {isBaseLayer: true});
+        map.addLayer(layer);
+
+        ctrl1 = new OpenLayers.Control.SelectFeature(layer, {
+            highlightOnly: false,
+            hover: false,
+            clickout: false,
+            toggle: true
+        });
+        map.addControl(ctrl1);
+
+        ctrl2 = new OpenLayers.Control.SelectFeature(layer, {
+            highlightOnly: true,
+            hover: true
+        });
+        map.addControl(ctrl2);
+
+        ctrl2.activate();
+        ctrl1.activate();
+
+        feature = new OpenLayers.Feature.Vector();
+        feature.layer = layer;
+
+        // override the layer's getFeatureFromEvent func so that it always
+        // returns the feature referenced to by _feature
+        layer.getFeatureFromEvent = function(evt) { return _feature; };
+
+        evt = {xy: new OpenLayers.Pixel(Math.random(), Math.random())};
+
+        map.zoomToMaxExtent();
+
+        /*
+         * tests
+         */
+
+        // with renderIntent
+
+        ctrl1.renderIntent = "select";
+        ctrl2.renderIntent = "temporary";
+
+        // mouse over feature, feature is drawn with "temporary"
+        _feature = feature;
+        evt.type = "mousemove";
+        map.events.triggerEvent("mousemove", evt);
+        t.eq(feature.renderIntent, "temporary",
+             "feature drawn with expected render intent after \"mouseover\"");
+
+        // click in feature, feature is drawn with "select"
+        _feature = feature;
+        evt.type = "click";
+        map.events.triggerEvent("click", evt);
+        t.eq(feature.renderIntent, "select",
+             "feature drawn with expected render intent after \"clickin\"");
+
+        // mouse out of feature, feature is still drawn with "select"
+        _feature = null;
+        evt.type = "mousemove";
+        map.events.triggerEvent("mousemove", evt);
+        t.eq(feature.renderIntent, "select",
+             "feature drawn with expected render intent after \"mouseout\"");
+
+        // mouse over feature again, feature is drawn with "temporary"
+        _feature = feature;
+        evt.type = "mousemove";
+        map.events.triggerEvent("mousemove", evt);
+        t.eq(feature.renderIntent, "temporary",
+             "feature drawn with expected render intent after \"mouseover\"");
+
+        // click in feature again, feature is drawn with "default"
+        _feature = feature;
+        evt.type = "click";
+        map.events.triggerEvent("click", evt);
+        t.eq(feature.renderIntent, "default",
+             "feature drawn with expected render intent after \"clickin\"");
+
+        // mouse out of feature again, feature is still drawn with "default"
+        _feature = null;
+        evt.type = "mousemove";
+        map.events.triggerEvent("mousemove", evt);
+        t.eq(feature.renderIntent, "default",
+             "feature drawn with expected render intent after \"mouseout\"");
+
+        // mouse over feature again, feature is drawn with "temporary"
+        _feature = feature;
+        evt.type = "mousemove";
+        map.events.triggerEvent("mousemove", evt);
+        t.eq(feature.renderIntent, "temporary",
+             "feature drawn with expected render intent after \"mouseover\"");
+
+        // mouse out of feature again, feature is still drawn with "default"
+        _feature = null;
+        evt.type = "mousemove";
+        map.events.triggerEvent("mousemove", evt);
+        t.eq(feature.renderIntent, "default",
+             "feature drawn with expected render intent after \"mouseout\"");
+    }
+
     function test_setLayer(t) {
         t.plan(5);
         var map = new OpenLayers.Map("map");

Modified: sandbox/tschaub/canvas/tests/Control/TouchNavigation.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/TouchNavigation.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/TouchNavigation.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -102,6 +102,29 @@
 
     }
 
+    function test_clickHandlerOptions(t) {
+
+        t.plan(3);
+
+         var nav = new OpenLayers.Control.TouchNavigation();
+         t.eq(nav.clickHandlerOptions, null, "clickHandlerOptions null by default");
+
+         var map = new OpenLayers.Map({
+             div: document.body,
+             controls: [
+                 new OpenLayers.Control.TouchNavigation({
+                     clickHandlerOptions: {foo: "bar"}
+                 })
+             ]
+         });
+         nav = map.controls[0];
+
+         t.eq(nav.handlers.click.foo, "bar", "foo property is set on the click handler");
+         t.eq(nav.handlers.click.pixelTolerance, 2, "pixelTolerance is 2 by default");
+         map.destroy();
+
+    }
+
     function test_zoomOut(t) {
         t.plan(1);
 
@@ -114,7 +137,7 @@
         var control = new OpenLayers.Control.TouchNavigation();
         map.addControl(control);
         var handler = control.handlers.click;
-        handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo", "bar"]});
+        handler.touchstart({xy: new OpenLayers.Pixel(1 ,1), touches: ["foo", "bar"]});
         handler.touchend({});
         t.delay_call(1, function() {
             t.eq(map.getZoom(), 4, "Did we zoom out?");

Modified: sandbox/tschaub/canvas/tests/Control/WMSGetFeatureInfo.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/WMSGetFeatureInfo.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control/WMSGetFeatureInfo.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -420,7 +420,7 @@
     }
 
     function test_drillDown(t) {
-        t.plan(4);
+        t.plan(6);
         var map = new OpenLayers.Map("map", {
             getExtent: function() {return(new OpenLayers.Bounds(-180,-90,180,90));}
             }
@@ -434,14 +434,17 @@
             layers: "c"
         });
 
+        // this service does not support application/vnd.ogc.gml for GetFeatureInfo, only text/xml
         var c = new OpenLayers.Layer.WMS("dummy","http://myhost/wms", {
-            layers: "x"
+            layers: "x",
+            info_format: "text/xml"
         });
 
         map.addLayers([a, b, c]);
 
         var click = new OpenLayers.Control.WMSGetFeatureInfo({
-            drillDown: true
+            drillDown: true,
+            infoFormat: "application/vnd.ogc.gml"
         });
 
         map.addControl(click);
@@ -451,9 +454,11 @@
         OpenLayers.Request.GET = function(options) {
             count++;
             if (count == 1) {
+                t.eq(options.params["INFO_FORMAT"], "application/vnd.ogc.gml", "Default info format of the control is used");
                 t.eq(options.params["QUERY_LAYERS"].join(","), "a,c", "Layers should be grouped by service url");
                 t.eq(options.url, "http://localhost/wms", "Correct url used for first request");
             } else if (count == 2) {
+                t.eq(options.params["INFO_FORMAT"], "text/xml", "Overridden info format is used instead of the control's infoFormat");
                 t.eq(options.url, "http://myhost/wms", "Correct url used for second request");
             }
         };

Modified: sandbox/tschaub/canvas/tests/Control.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Control.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -60,7 +60,7 @@
     }
 
     function test_Control_destroy(t) {
-        t.plan(3);
+        t.plan(4);
     
         var map = new OpenLayers.Map('map');
         var control = new OpenLayers.Control();
@@ -70,6 +70,7 @@
         t.ok(map.controls[map.controls.length - 1] != control, "map.controls doesn't contains control");
 
         t.ok(control.map == null, "Control.map is null");
+        t.ok(control.div == null, "Control.div is null");
         t.ok(control.handler == null, "Control.handler is null");
     }
     

Modified: sandbox/tschaub/canvas/tests/Events.html
===================================================================
--- sandbox/tschaub/canvas/tests/Events.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Events.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -318,8 +318,8 @@
                    touches: [{clientX: 1, clientY: 1}, {clientX: 2, clientY: 2}]
                   };
         events.handleBrowserEvent(evt);
-        t.eq(evt.clientX, 1, "evt.clientX value is correct");
-        t.eq(evt.clientY, 1, "evt.clientY value is correct");
+        t.eq(evt.clientX, 1.5, "evt.clientX value is correct");
+        t.eq(evt.clientY, 1.5, "evt.clientY value is correct");
     }
 
     function test_Events_destroy (t) {

Modified: sandbox/tschaub/canvas/tests/Feature.html
===================================================================
--- sandbox/tschaub/canvas/tests/Feature.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Feature.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -122,7 +122,7 @@
         t.ok( !feature2.onScreen(), "feature knows it's offscreen" );
     }
 
-    function test_Feature_createPopup(t) {
+    function test_Feature_createPopup_2(t) {
         t.plan(11);
 
     //no lonlat        

Modified: sandbox/tschaub/canvas/tests/Format/Filter/v1.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/Filter/v1.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/Filter/v1.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -166,9 +166,96 @@
 
     }
 
+    function test_logical_fid(t) {
+        // the Filter Encoding spec doesn't allow for FID filters inside logical filters
+        // however, to be liberal, we will write them without complaining
+        t.plan(3);
 
+        var filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.OR,
+            filters: [
+                new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LIKE,
+                    property: "person",
+                    value: "me"
+                }),
+                new OpenLayers.Filter.FeatureId({fids: ["foo.1", "foo.2"]})
+            ]
+        });
+        var format = new OpenLayers.Format.Filter.v1_0_0();
+        
+        var got = format.write(filter);
+        var exp = readXML("LogicalFeatureId");
+        t.xml_eq(got, exp, "wrote FID filter in logical OR without complaint");
+
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: [
+                new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LIKE,
+                    property: "person",
+                    value: "me"
+                }),
+                new OpenLayers.Filter.FeatureId({fids: ["foo.1", "foo.2"]})
+            ]
+        });
+        got = format.write(filter);
+        exp = readXML("LogicalFeatureIdAnd");
+        t.xml_eq(got, exp, "wrote FID filter in logical AND without complaint");
+
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.NOT,
+            filters: [
+                new OpenLayers.Filter.FeatureId({fids: ["foo.2"]})
+            ]
+        });
+        got = format.write(filter);
+        exp = readXML("LogicalFeatureIdNot");
+        t.xml_eq(got, exp, "wrote FID filter in logical NOT without complaint");
+    }
+
+
+    function readXML(id) {
+        var xml = document.getElementById(id).firstChild.nodeValue;
+        return new OpenLayers.Format.XML().read(xml).documentElement;
+    }
+
+
     </script> 
 </head> 
 <body>
+
+<div id="LogicalFeatureId"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+    <ogc:Or>
+        <ogc:PropertyIsLike wildCard="*" singleChar="." escape="!">
+            <ogc:PropertyName>person</ogc:PropertyName>
+            <ogc:Literal>me</ogc:Literal>
+        </ogc:PropertyIsLike>
+        <ogc:FeatureId fid="foo.1"/>
+        <ogc:FeatureId fid="foo.2"/>
+    </ogc:Or>
+</ogc:Filter>
+--></div>
+<div id="LogicalFeatureIdAnd"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+    <ogc:And>
+        <ogc:PropertyIsLike wildCard="*" singleChar="." escape="!">
+            <ogc:PropertyName>person</ogc:PropertyName>
+            <ogc:Literal>me</ogc:Literal>
+        </ogc:PropertyIsLike>
+        <ogc:FeatureId fid="foo.1"/>
+        <ogc:FeatureId fid="foo.2"/>
+    </ogc:And>
+</ogc:Filter>
+--></div>
+<div id="LogicalFeatureIdNot"><!--
+<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
+    <ogc:Not>
+        <ogc:FeatureId fid="foo.2"/>
+    </ogc:Not>
+</ogc:Filter>
+--></div>
+
 </body> 
 </html> 

Modified: sandbox/tschaub/canvas/tests/Format/Filter/v1_0_0.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/Filter/v1_0_0.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/Filter/v1_0_0.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -152,7 +152,117 @@
 
     }
 
+    function test_FilterFunctions(t) {
+        t.plan(2);
 
+        var parser = new OpenLayers.Format.Filter.v1_0_0();
+
+        //test spatial intersects with filter function
+        var filter = new OpenLayers.Filter.Spatial({
+            property: 'the_geom',
+            type: OpenLayers.Filter.Spatial.INTERSECTS,
+            value: new OpenLayers.Filter.Function({
+                name  : 'querySingle',
+                params: ['sf:restricted', 'the_geom', 'cat=3']
+            })
+        });
+
+        var out =
+            '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+                '<ogc:Intersects>' +
+                    '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+                    '<ogc:Function name="querySingle">' +
+                        '<ogc:Literal>sf:restricted</ogc:Literal>' +
+                        '<ogc:Literal>the_geom</ogc:Literal>' +
+                        '<ogc:Literal>cat=3</ogc:Literal>' +
+                    '</ogc:Function>' +
+                '</ogc:Intersects>' +
+            '</ogc:Filter>';
+
+
+        var node = parser.write(filter);
+
+        //test writer
+        t.xml_eq(node, out, "spatial intersect filter with functions correctly written");
+
+        //test logical filter with custom function
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: [
+                new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+                    property: "FOO",
+                    value: new OpenLayers.Filter.Function({
+                        name : 'customFunction',
+                        params : ['param1', 'param2']
+                    })
+                })
+            ]
+        });
+
+        out =
+            '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+                '<ogc:And>' +
+                    '<ogc:PropertyIsNotEqualTo>' +
+                        '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+                        '<ogc:Function name="customFunction">' +
+                            '<ogc:Literal>param1</ogc:Literal>' +
+                            '<ogc:Literal>param2</ogc:Literal>' +
+                        '</ogc:Function>' +
+                    '</ogc:PropertyIsNotEqualTo>' +
+                '</ogc:And>' +
+            '</ogc:Filter>';
+
+        node = parser.write(filter);
+
+        //test writer
+        t.xml_eq(node, out, "comparison filter with filter functions correctly written");
+
+    }
+
+    function test_NestedFilterFunctions(t) {
+        t.plan(1);
+
+        //test spatial dwithin with nested filter function
+        var filter = new OpenLayers.Filter.Spatial({
+           property: 'the_geom',
+           type: OpenLayers.Filter.Spatial.DWITHIN,
+           value: new OpenLayers.Filter.Function({
+               name : 'collectGeometries',
+               params: [
+                    new OpenLayers.Filter.Function({
+                        name  : 'queryCollection',
+                        params: ['sf:roads', 'the_geom', 'INCLUDE']
+                    })
+               ]
+           }),
+           distanceUnits: "meters",
+           distance: 200
+        });
+
+        var out =
+            '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+                '<ogc:DWithin>' +
+                    '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+                    '<ogc:Function name="collectGeometries">' +
+                        '<ogc:Function name="queryCollection">' +
+                            '<ogc:Literal>sf:roads</ogc:Literal>' +
+                            '<ogc:Literal>the_geom</ogc:Literal>' +
+                            '<ogc:Literal>INCLUDE</ogc:Literal>' +
+                        '</ogc:Function>' +
+                    '</ogc:Function>' +
+                    '<ogc:Distance units="meters">200</ogc:Distance>' +
+                '</ogc:DWithin>' +
+            '</ogc:Filter>';
+
+        var parser = new OpenLayers.Format.Filter.v1_0_0();
+        var node = parser.write(filter);
+
+        //test writer
+        t.xml_eq(node, out, "spatial dwithin filter with nested functions correctly written");
+    }
+
+
     </script> 
 </head> 
 <body>

Modified: sandbox/tschaub/canvas/tests/Format/Filter/v1_1_0.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/Filter/v1_1_0.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/Filter/v1_1_0.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -203,6 +203,118 @@
 
     }
 
+    function test_FilterFunctions(t) {
+        t.plan(2);
+
+        var parser = new OpenLayers.Format.Filter.v1_1_0();
+
+        //test spatial intersects with filter function
+        var filter = new OpenLayers.Filter.Spatial({
+            property: 'the_geom',
+            type: OpenLayers.Filter.Spatial.INTERSECTS,
+            value: new OpenLayers.Filter.Function({
+                name  : 'querySingle',
+                params: ['sf:restricted', 'the_geom', 'cat=3']
+            })
+        });
+
+        var out =
+            '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+                '<ogc:Intersects>' +
+                    '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+                    '<ogc:Function name="querySingle">' +
+                        '<ogc:Literal>sf:restricted</ogc:Literal>' +
+                        '<ogc:Literal>the_geom</ogc:Literal>' +
+                        '<ogc:Literal>cat=3</ogc:Literal>' +
+                    '</ogc:Function>' +
+                '</ogc:Intersects>' +
+            '</ogc:Filter>';
+
+
+        var node = parser.write(filter);
+
+        //test writer
+        t.xml_eq(node, out, "spatial intersect filter with functions correctly written");
+
+        //test logical filter with custom function
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: [
+                new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+                    matchCase: false,
+                    property: "FOO",
+                    value: new OpenLayers.Filter.Function({
+                        name : 'customFunction',
+                        params : ['param1', 'param2']
+                    })
+                })
+            ]
+        });
+
+        out =
+            '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+                '<ogc:And>' +
+                    '<ogc:PropertyIsNotEqualTo matchCase="false">' +
+                        '<ogc:PropertyName>FOO</ogc:PropertyName>' +
+                        '<ogc:Function name="customFunction">' +
+                            '<ogc:Literal>param1</ogc:Literal>' +
+                            '<ogc:Literal>param2</ogc:Literal>' +
+                        '</ogc:Function>' +
+                    '</ogc:PropertyIsNotEqualTo>' +
+                '</ogc:And>' +
+            '</ogc:Filter>';
+
+        node = parser.write(filter);
+
+        //test writer
+        t.xml_eq(node, out, "comparison filter with filter functions correctly written");
+
+    }
+
+    function test_NestedFilterFunctions(t) {
+        t.plan(1);
+
+        //test spatial dwithin with nested filter function
+        var filter = new OpenLayers.Filter.Spatial({
+           property: 'the_geom',
+           type: OpenLayers.Filter.Spatial.DWITHIN,
+           value: new OpenLayers.Filter.Function({
+               name : 'collectGeometries',
+               params: [
+                    new OpenLayers.Filter.Function({
+                        name  : 'queryCollection',
+                        params: ['sf:roads', 'the_geom', 'INCLUDE']
+                    })
+               ]
+           }),
+           distanceUnits: "meters",
+           distance: 200
+        });
+
+        var out =
+            '<ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">' +
+                '<ogc:DWithin>' +
+                    '<ogc:PropertyName>the_geom</ogc:PropertyName>' +
+                    '<ogc:Function name="collectGeometries">' +
+                        '<ogc:Function name="queryCollection">' +
+                            '<ogc:Literal>sf:roads</ogc:Literal>' +
+                            '<ogc:Literal>the_geom</ogc:Literal>' +
+                            '<ogc:Literal>INCLUDE</ogc:Literal>' +
+                        '</ogc:Function>' +
+                    '</ogc:Function>' +
+                    '<ogc:Distance units="meters">200</ogc:Distance>' +
+                '</ogc:DWithin>' +
+            '</ogc:Filter>';
+
+        var parser = new OpenLayers.Format.Filter.v1_1_0();
+        var node = parser.write(filter);
+
+        //test writer
+        t.xml_eq(node, out, "spatial dwithin filter with nested functions correctly written");
+    }
+
+
     </script> 
 </head> 
 <body>

Modified: sandbox/tschaub/canvas/tests/Format/GeoJSON.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/GeoJSON.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/GeoJSON.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -95,6 +95,19 @@
         t.eq(data.components[1].y, 1, "y of second component is right");    
     }
 
+    function test_Format_GeoJSON_multipoint_projected(t) {
+        t.plan(1);
+        var f = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint([
+            new OpenLayers.Geometry.Point(15555162, 4247484)]));
+        var format = new OpenLayers.Format.GeoJSON({
+            internalProjection: new OpenLayers.Projection("EPSG:900913"),
+            externalProjection: new OpenLayers.Projection("EPSG:4326")
+        });
+        var data = format.write(f);
+        var found = (data.search('139.734') != -1);
+        t.ok(found, "Found 139.734 (correct reprojection) in data output.");
+    }        
+
     function test_Format_GeoJSON_multiline(t) {
         t.plan(3);
 

Modified: sandbox/tschaub/canvas/tests/Format/GeoRSS.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/GeoRSS.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/GeoRSS.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -73,6 +73,15 @@
             t.xml_eq(out, expected_result, "Output gave expected value");
         }
     }
+
+    function test_leading_space(t) {
+        t.plan(2);
+        
+        var parser = new OpenLayers.Format.GeoRSS();
+        var items = parser.read('<rss version="2.0" xmlns:georss="http://www.georss.org/georss"><item><description>  <![CDATA[foo]]></description></item></rss>');
+        t.eq(items.length, 1, "item created");
+        t.eq(items[0].attributes.description, "  foo", "description value is ok");
+    }
              
     var shell_start = '<feed xmlns="http://www.w3.org/2005/Atom" \n              xmlns:georss="http://www.georss.org/georss">\n              <title>scribble</title>\n              <id>http://featureserver.org/featureserver.cgi/scribble?format=atom</id>\n              <author><name>FeatureServer</name></author>\n';             
     var shell_end = '</feed>'; 

Modified: sandbox/tschaub/canvas/tests/Format/KML.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/KML.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/KML.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -9,7 +9,6 @@
     var test_style_outline = '<kml xmlns="http://earth.google.com/kml/2.0"> <Placemark>    <Style> <PolyStyle> <outline>0</outline> <color>870000ff</color> <width>10</width> </PolyStyle> </Style>  <LineString> <coordinates> -112,36 -113,37 </coordinates> </LineString> </Placemark></kml>';
     var test_style_font = '<kml xmlns="http://earth.google.com/kml/2.0"> <Placemark><Style><LabelStyle><color>870000ff</color><scale>1.5</scale></LabelStyle></Style><LineString><coordinates> -112,36 -113,37 </coordinates></LineString></Placemark></kml>';
     var test_nl = '<kml xmlns="http://earth.google.com/kml/2.2"> <Document> <NetworkLink> <Link> <href>http://maker.geocommons.com/maps/1717/overlays/0</href> </Link> </NetworkLink> </Document></kml>';
-
     function test_Format_KML_constructor(t) { 
         t.plan(5); 
          
@@ -23,7 +22,20 @@
         t.eq(format.externalProjection.getCode(), "EPSG:4326", 
              "default external projection is EPSG:4326"); 
     }
-
+    function test_Format_KML_multipoint(t) {
+        t.plan(1);
+        var f = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.MultiPoint([
+            new OpenLayers.Geometry.Point(15555162, 4247484)]));
+        var format = new OpenLayers.Format.KML({
+            extractStyles:      true, 
+            extractAttributes:  true,
+            internalProjection: new OpenLayers.Projection("EPSG:900913"),
+            externalProjection: new OpenLayers.Projection("EPSG:4326")
+        });
+        var data = format.write(f);
+        var found = (data.search('139.734') != -1);
+        t.ok(found, "Found 139.734 (correct reprojection) in data output.");
+    }        
     function test_Format_KML_read(t) {
         t.plan(5);
         var features = (new OpenLayers.Format.KML()).read(this.test_content);
@@ -35,6 +47,7 @@
              "read geometry collection");
     }
 
+    
     function test_Format_KML_readCdataAttributes_20(t) {
         t.plan(2);
         var cdata = '<kml xmlns="http://earth.google.com/kml/2.0"><Document><Placemark><name><![CDATA[Pezinok]]> </name><description><![CDATA[Full of text.]]></description><styleUrl>#rel1.0</styleUrl><Point> <coordinates>17.266666, 48.283333</coordinates></Point></Placemark></Document></kml>';

Modified: sandbox/tschaub/canvas/tests/Format/OSM.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/OSM.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/OSM.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -95,6 +95,18 @@
             output = output.replace(/<\?[^>]*\?>/, '');
             t.eq(output, osm_serialized_data[key], key + " serialized correctly");
         }
+    }   
+    function test_Format_OSM_write_reproject(t) {
+        t.plan(1);
+        var f = new OpenLayers.Format.OSM({'internalProjection': new OpenLayers.Projection("EPSG:900913")});
+        var feat = new OpenLayers.Feature.Vector(
+            new OpenLayers.Geometry.Point(100000, 100000)
+            );
+        var data = f.write([feat]);
+        var f = new OpenLayers.Format.OSM();
+        var features = f.read(data);
+
+        t.eq(OpenLayers.Util.toFloat(features[0].geometry.x, 3), .898, "exported to lonlat and re-read as lonlat correctly")
     }    
     </script> 
 </head> 

Modified: sandbox/tschaub/canvas/tests/Format/OWSContext/v0_3_1.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/OWSContext/v0_3_1.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/OWSContext/v0_3_1.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -4,7 +4,7 @@
     <script type="text/javascript">
 
     function test_read_wmswfs(t) {
-        t.plan(16);
+        t.plan(17);
         // taken from http://www.ogcnetwork.net/schemas/owc/0.3.1/context_nested.xml
         // adapted: add an extra slash (roads/railways) in the Title of the WMS layer to test nesting
         var text = '<?xml version="1.0" encoding="UTF-8"?>' +
@@ -61,6 +61,7 @@
         map.zoomToExtent(new OpenLayers.Bounds(-117, 32, -116, 33));
         var owc = parser.write(map, {id: 'ows-context-ex-1-v3', title: 'OWS Context version 0.3.1 showing nested layers'});
         t.xml_eq(text, owc, "Can we roundtrip this nested OWSContext with a WMS and WFS layer?");
+        t.eq(context.layers[1].metadata.nestingPath[0], "Tiger 2005fe major roads/railways", "Nesting path is preserved even after calling write");
     }
 
     function test_write_wmswfs(t) {

Copied: sandbox/tschaub/canvas/tests/Format/QueryStringFilter.html (from rev 11762, trunk/openlayers/tests/Format/QueryStringFilter.html)
===================================================================
--- sandbox/tschaub/canvas/tests/Format/QueryStringFilter.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/Format/QueryStringFilter.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,306 @@
+<html>
+<head>
+  <script src="../../lib/OpenLayers.js"></script>
+  <script type="text/javascript">
+
+    function test_constructor(t) {
+        t.plan(4);
+        var options = {'foo': 'bar'};
+        var format  = new OpenLayers.Format.QueryStringFilter(options);
+        t.ok(format instanceof OpenLayers.Format.QueryStringFilter,
+           "new OpenLayers.Format.QueryStringFilter object");
+        t.eq(format.foo, "bar", "constructor sets options correctly")
+        t.eq(typeof format.write, 'function', 'format has a write function');
+        t.eq(format.options, options, "format.options correctly set");
+    }
+
+    function test_write(t) {
+        t.plan(30);
+
+        // setup
+
+        var format, filter, params;
+
+        format = new OpenLayers.Format.QueryStringFilter();
+
+        // 1 test
+        filter = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.BBOX,
+            value: new OpenLayers.Bounds(0, 1, 2, 3)
+        });
+        params = format.write(filter);
+        t.eq(params.bbox, [0, 1, 2, 3], "correct bbox param if passed a BBOX filter");
+
+        // 3 tests
+        var lon = 100, lat = 200, tolerance = 10;
+        filter = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.DWITHIN,
+            value: new OpenLayers.Geometry.Point(lon, lat),
+            distance: tolerance
+        });
+        params = format.write(filter);
+        t.eq(params.lon, lon, "correct lon param if passed a DWITHIN filter");
+        t.eq(params.lat, lat, "correct lat param if passed a DWITHIN filter");
+        t.eq(params.tolerance, tolerance, "correct tolerance param if passed a DWITHIN filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.WITHIN,
+            value: new OpenLayers.Geometry.Point(lon, lat)
+        });
+        params = format.write(filter);
+        t.eq(params.lon, lon, "correct lon param if passed a WITHIN filter");
+        t.eq(params.lat, lat, "correct lat param if passed a WITHIN filter");
+
+        // Some bbox filters used in the next tests.
+
+        var bboxFilter1 = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.BBOX,
+            value:  new OpenLayers.Bounds(0, 0, 10, 10)
+        });
+
+        var bboxFilter2 = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.BBOX,
+            value:  new OpenLayers.Bounds(0, 0, 20, 20)
+        });
+
+        // 1 test
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: []
+        });
+        params = format.write(filter);
+        t.eq(params, {}, "returns empty object if given empty AND Logical filter");
+
+        // 1 test
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.OR,
+            filters: [
+                bboxFilter1
+            ]
+        });
+        params = format.write(filter);
+        t.eq(params, {}, "does not support OR Logical filter");
+
+        // 1 test
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: [
+                bboxFilter1
+            ]
+        });
+        params = format.write(filter);
+        t.eq(params.bbox, [0, 0, 10, 10],
+             "correct bbox param if passed a Logical filter containing a BBOX");
+
+        // 1 test
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: [
+                bboxFilter1, bboxFilter2
+            ]
+        });
+        params = format.write(filter);
+        t.eq(params.bbox, [0, 0, 20, 20],
+             "correct bbox param if passed multiple BBOX filter in a Logical filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.EQUAL_TO,
+            property: "foo",
+            value: "bar"
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an EQUAL_TO filter");
+        t.eq(params["foo__eq"], "bar",
+             "correct param key and value if passed an EQUAL_TO filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
+            property: "foo",
+            value: "bar"
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an NOT_EQUAL_TO filter");
+        t.eq(params["foo__ne"], "bar",
+             "correct param key and value if passed an NOT_EQUAL_TO filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.LESS_THAN,
+            property: "foo",
+            value: "bar"
+        });
+        var params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an LESS_THAN filter");
+        t.eq(params["foo__lt"], "bar",
+             "correct param key and value if passed an LESS_THAN filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
+            property: "foo",
+            value: "bar"
+        });
+        var params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an LESS_THAN_OR_EQUAL_TO filter");
+        t.eq(params["foo__lte"], "bar",
+             "correct param key and value if passed an LESS_THAN_OR_EQUAL_TO filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.GREATER_THAN,
+            property: "foo",
+            value: "bar"
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an GREATER_THAN filter");
+        t.eq(params["foo__gt"], "bar",
+             "correct param key and value if passed an GREATER_THAN filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
+            property: "foo",
+            value: "bar"
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an GREATER_THAN_OR_EQUAL_TO filter");
+        t.eq(params["foo__gte"], "bar",
+             "correct param key and value if passed an GREATER_THAN_OR_EQUAL_TO filter");
+
+        // 2 tests
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.LIKE,
+            property: "foo",
+            value: "bar"
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed a LIKE filter");
+        t.eq(params["foo__ilike"], "bar",
+             "correct param key and value if passed an LIKE filter");
+
+        // 4 tests
+        filter = new OpenLayers.Filter.Logical({
+            type: OpenLayers.Filter.Logical.AND,
+            filters: [
+                new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
+                    property: "foo",
+                    value: "bar"
+                }),
+                new OpenLayers.Filter.Comparison({
+                    type: OpenLayers.Filter.Comparison.LESS_THAN,
+                    property: "foo2",
+                    value: "baz"
+                })
+            ]
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed an EQUAL_TO filter within a AND filter");
+        t.eq(params["foo__eq"], "bar",
+             "correct param key and value if passed an EQUAL_TO filter within a AND filter");
+        t.eq(params.queryable[1], "foo2",
+             "correct queryable param if passed a LESS_THAN filter within a AND filter");
+        t.eq(params["foo2__lt"], "baz",
+             "correct param key and value if passed a LESS_THAN filter within a AND filter");
+
+        // 2 tests
+        format = new OpenLayers.Format.QueryStringFilter({wildcarded: true});
+        filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.LIKE,
+            property: "foo",
+            value: "bar"
+        });
+        params = format.write(filter);
+        t.eq(params.queryable[0], "foo",
+             "correct queryable param if passed a LIKE filter (wildcarded true)");
+        t.eq(params["foo__ilike"], "%bar%",
+             "correct param key and value if passed an LIKE filter (wildcarded true)");
+    }
+
+    function test_regex2value(t) {
+        t.plan(16);
+
+        // setup
+
+        var format = new OpenLayers.Format.QueryStringFilter();
+
+        var value;
+        var filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.LIKE,
+            property: "prop"
+        });
+        
+        function serialize(value) {
+            filter.value = value;
+            return format.write(filter).prop__ilike;
+        }
+
+        // test
+
+        value = serialize("foo");
+        t.eq(value, "foo", 'regex2value converts "foo" to "foo"');
+
+        value = serialize("foo%");
+        t.eq(value, "foo\\%", 'regex2value converts "foo%" to "foo\\%"');
+
+        value = serialize("foo.*");
+        t.eq(value, "foo%", 'regex2value converts "foo.*" to "foo%"');
+
+        value = serialize("f.*oo.*");
+        t.eq(value, "f%oo%", 'regex2value converts "f.*oo.*" to "f%oo%"');
+
+        value = serialize("foo.");
+        t.eq(value, "foo_", 'regex2value converts "foo." to "foo_"');
+
+        value = serialize("f.oo.");
+        t.eq(value, "f_oo_", 'regex2value converts "f.oo." to "f_oo_"');
+
+        value = serialize("f.oo.*");
+        t.eq(value, "f_oo%", 'regex2value converts "f.oo.*" to "f_oo%"');
+
+        value = serialize("foo\\\\");
+        t.eq(value, "foo\\\\", 'regex2value converts "foo\\\\" to "foo\\\\"');
+
+        value = serialize("foo\\.");
+        t.eq(value, "foo.", 'regex2value converts "foo\\." to "foo."');
+
+        value = serialize("foo\\\\.");
+        t.eq(value, "foo\\\\_", 'regex2value converts "foo\\\\." to "foo\\\\_"');
+
+        value = serialize("foo\\*");
+        t.eq(value, "foo*", 'regex2value converts "foo\\*" to "foo*"');
+
+        value = serialize("foo\\\\*");
+        t.eq(value, "foo\\\\*", 'regex2value converts "foo\\\\*" to "foo\\\\*"');
+
+        value = serialize("foo\\\\.*");
+        t.eq(value, "foo\\\\%", 'regex2value converts "foo\\\\.*" to "foo\\\\%"');
+
+        value = serialize("fo\\.o.*");
+        t.eq(value, "fo.o%", 'regex2value converts from "fo\\.o.*" to "fo.o%"');
+
+        value = serialize("fo.*o\\.");
+        t.eq(value, "fo%o.", 'regex2value converts from "fo.*o\\." to "to%o."');
+
+        value = serialize("\\*\\..*.\\\\.*\\\\.%");
+        t.eq(value, "*.%_\\\\%\\\\_\\%",
+             'regex2value converts from "\\*\\..*.\\\\.*\\\\.%" ' +
+             'to "*.%_\\\\%\\\\_\\%"');
+    }
+
+  </script>
+</head>
+<body>
+</body>
+</html>

Modified: sandbox/tschaub/canvas/tests/Format/WFSCapabilities/v1.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/WFSCapabilities/v1.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/WFSCapabilities/v1.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -4,7 +4,7 @@
     <script type="text/javascript">
     
     function test_read(t) {
-        t.plan(30);
+        t.plan(33);
        
         var parser = new OpenLayers.Format.WFSCapabilities();
 
@@ -17,6 +17,7 @@
         t.eq(ft[0]["title"], "Manhattan (NY) landmarks", "title of first feature type correct");
         t.eq(ft[0]["name"], "poly_landmarks", "name of first feature type correct");
         t.eq(ft[0]["featureNS"], "http://www.census.gov", "ns of first feature type correct");
+        t.eq(ft[0]["srs"], "urn:x-ogc:def:crs:EPSG:4326", "srs of first feature type correct");
 
         // GeoServer, v1.0.0
         text = '<?xml version="1.0" encoding="UTF-8"?><WFS_Capabilities version="1.0.0" xmlns="http://www.opengis.net/wfs" xmlns:it.geosolutions="http://www.geo-solutions.it" xmlns:cite="http://www.opengeospatial.net/cite" xmlns:tiger="http://www.census.gov" xmlns:sde="http://geoserver.sf.net" xmlns:topp="http://www.openplans.org/topp" xmlns:sf="http://www.openplans.org/spearfish" xmlns:nurc="http://www.nurc.nato.int" xmlns:ogc="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/wfs http://localhost:80/geoserver/schemas/wfs/1.0.0/WFS-capabilities.xsd"><Service><Name>WFS</Name><Title>GeoServer Web Feature Service</Title><Abstract>This is the reference implementation of WFS 1.0.0 and WFS 1.1.0, supports all WFS operations including Transaction.</Abstract><Keywords>WFS, WMS, GEOSERVER</Keywords><OnlineResource>http://localhost:80/geoserver/wfs</OnlineResource><Fees>NONE</Fees><AccessConstraints>NONE</Acce
 ssConstraints></Service><Capability><Request><GetCapabilities><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=GetCapabilities"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></GetCapabilities><DescribeFeatureType><SchemaDescriptionLanguage><XMLSCHEMA/></SchemaDescriptionLanguage><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=DescribeFeatureType"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></DescribeFeatureType><GetFeature><ResultFormat><GML2/><SHAPE-ZIP/><GEOJSON/><csv/><GML3/></ResultFormat><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=GetFeature"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></GetFeature><Transaction><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=T
 ransaction"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></Transaction><LockFeature><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=LockFeature"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></LockFeature><GetFeatureWithLock><ResultFormat><GML2/></ResultFormat><DCPType><HTTP><Get onlineResource="http://localhost:80/geoserver/wfs?request=GetFeatureWithLock"/></HTTP></DCPType><DCPType><HTTP><Post onlineResource="http://localhost:80/geoserver/wfs?"/></HTTP></DCPType></GetFeatureWithLock></Request></Capability><FeatureTypeList><Operations><Query/><Insert/><Update/><Delete/><Lock/></Operations><FeatureType><Name>tiger:poly_landmarks</Name><Title>Manhattan (NY) landmarks</Title><Abstract>Manhattan landmarks, identifies water, lakes, parks, interesting buildilngs</Abstract><Keywords>DS_poly_landmarks, poly_landmarks, landmarks,
  manhattan</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-74.047185" miny="40.679648" maxx="-73.90782" maxy="40.882078"/></FeatureType><FeatureType><Name>tiger:poi</Name><Title>Manhattan (NY) points of interest</Title><Abstract>Points of interest in New York, New York (on Manhattan). One of the attributes contains the name of a file with a picture of the point of interest.</Abstract><Keywords>poi, DS_poi, points_of_interest, Manhattan</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-74.0118315772888" miny="40.70754683896324" maxx="-74.00857344353275" maxy="40.711945649065406"/></FeatureType><FeatureType><Name>tiger:tiger_roads</Name><Title>Manhattan (NY) roads</Title><Abstract>Highly simplified road layout of Manhattan in New York..</Abstract><Keywords>DS_tiger_roads, tiger_roads, roads</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-74.02722" miny="40.684221" maxx="-73.907005" maxy="40.878178"/></FeatureType><FeatureType><Name>sf:archsites</Name><
 Title>Spearfish archeological sites</Title><Abstract>Sample data from GRASS, archeological sites location, Spearfish, South Dakota, USA</Abstract><Keywords>archsites, sfArchsites, spearfish, archeology</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.8725637911543" miny="44.37740330855979" maxx="-103.63794182141925" maxy="44.48804280772808"/></FeatureType><FeatureType><Name>sf:bugsites</Name><Title>Spearfish bug locations</Title><Abstract>Sample data from GRASS, bug sites location, Spearfish, South Dakota, USA</Abstract><Keywords>sfBugsites, bugsites, insects, spearfish, tiger_beetles</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.86796131703647" miny="44.373938816704396" maxx="-103.63773523234195" maxy="44.43418821380063"/></FeatureType><FeatureType><Name>sf:restricted</Name><Title>Spearfish restricted areas</Title><Abstract>Sample data from GRASS, restricted areas, Spearfish, South Dakota, USA</Abstract><Keywords>restricted, sfRestricted, spearf
 ish, areas</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.85057172920756" miny="44.39436387625042" maxx="-103.74741494853805" maxy="44.48215752041131"/></FeatureType><FeatureType><Name>sf:roads</Name><Title>Spearfish roads</Title><Abstract>Sample data from GRASS, road layout, Spearfish, South Dakota, USA</Abstract><Keywords>sfRoads, roads, spearfish</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.87741691493184" miny="44.37087275281798" maxx="-103.62231404880659" maxy="44.50015918338962"/></FeatureType><FeatureType><Name>sf:streams</Name><Title>Spearfish streams</Title><Abstract>Sample data from GRASS, streams, Spearfish, South Dakota, USA</Abstract><Keywords>sfStreams, streams, spearfish</Keywords><SRS>EPSG:26713</SRS><LatLongBoundingBox minx="-103.87789019829768" miny="44.372335260095554" maxx="-103.62287788915457" maxy="44.502218486214815"/></FeatureType><FeatureType><Name>topp:tasmania_cities</Name><Title>Tasmania cities</Title><Abstract>Citi
 es in Tasmania (actually, just the capital)</Abstract><Keywords>cities, Tasmania</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/></FeatureType><FeatureType><Name>topp:tasmania_roads</Name><Title>Tasmania roads</Title><Abstract>Main Tasmania roads</Abstract><Keywords>Roads, Tasmania</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="145.19754" miny="-43.423512" maxx="148.27298000000002" maxy="-40.852802"/></FeatureType><FeatureType><Name>topp:tasmania_state_boundaries</Name><Title>Tasmania state boundaries</Title><Abstract>Tasmania state boundaries</Abstract><Keywords>tasmania_state_boundaries, Tasmania, boundaries</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="143.83482400000003" miny="-43.648056" maxx="148.47914100000003" maxy="-39.573891"/></FeatureType><FeatureType><Name>topp:tasmania_water_bodies</Name><Title>Tasmania water bodies</Title><Abstract>Tasmania water bodies</Abstra
 ct><Keywords>Lakes, Bodies, Australia, Water, Tasmania</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="145.97161899999998" miny="-43.031944" maxx="147.219696" maxy="-41.775558"/></FeatureType><FeatureType><Name>topp:states</Name><Title>USA Population</Title><Abstract>This is some census data on the states.</Abstract><Keywords>census, united, boundaries, state, states</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-124.731422" miny="24.955967" maxx="-66.969849" maxy="49.371735"/></FeatureType><FeatureType><Name>tiger:giant_polygon</Name><Title>World rectangle</Title><Abstract>A simple rectangular polygon covering most of the world, it\'s only used for the purpose of providing a background (WMS bgcolor could be used instead)</Abstract><Keywords>DS_giant_polygon, giant_polygon</Keywords><SRS>EPSG:4326</SRS><LatLongBoundingBox minx="-180.0" miny="-90.0" maxx="180.0" maxy="90.0"/></FeatureType></FeatureTypeList><ogc:Filter_Capabilities><ogc:Spatial_Capabilities><
 ogc:Spatial_Operators><ogc:Disjoint/><ogc:Equals/><ogc:DWithin/><ogc:Beyond/><ogc:Intersect/><ogc:Touches/><ogc:Crosses/><ogc:Within/><ogc:Contains/><ogc:Overlaps/><ogc:BBOX/></ogc:Spatial_Operators></ogc:Spatial_Capabilities><ogc:Scalar_Capabilities><ogc:Logical_Operators/><ogc:Comparison_Operators><ogc:Simple_Comparisons/><ogc:Between/><ogc:Like/><ogc:NullCheck/></ogc:Comparison_Operators><ogc:Arithmetic_Operators><ogc:Simple_Arithmetic/><ogc:Functions><ogc:Function_Names><ogc:Function_Name nArgs="1">abs</ogc:Function_Name><ogc:Function_Name nArgs="1">abs_2</ogc:Function_Name><ogc:Function_Name nArgs="1">abs_3</ogc:Function_Name><ogc:Function_Name nArgs="1">abs_4</ogc:Function_Name><ogc:Function_Name nArgs="1">acos</ogc:Function_Name><ogc:Function_Name nArgs="1">Area</ogc:Function_Name><ogc:Function_Name nArgs="1">asin</ogc:Function_Name><ogc:Function_Name nArgs="1">atan</ogc:Function_Name><ogc:Function_Name nArgs="2">atan2</ogc:Function_Name><ogc:Function_Name nArgs="3">b
 etween</ogc:Function_Name><ogc:Function_Name nArgs="1">boundary</ogc:Function_Name><ogc:Function_Name nArgs="1">boundaryDimension</ogc:Function_Name><ogc:Function_Name nArgs="2">buffer</ogc:Function_Name><ogc:Function_Name nArgs="3">bufferWithSegments</ogc:Function_Name><ogc:Function_Name nArgs="1">ceil</ogc:Function_Name><ogc:Function_Name nArgs="1">centroid</ogc:Function_Name><ogc:Function_Name nArgs="2">classify</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Average</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Bounds</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Count</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Max</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Median</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Min</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Sum</ogc:Function_Name><ogc:Function_Name nArgs="1">Collection_Unique</ogc:Function_Name><ogc:Function_Name nArgs="2">Con
 catenate</ogc:Function_Name><ogc:Function_Name nArgs="2">contains</ogc:Function_Name><ogc:Function_Name nArgs="1">convexHull</ogc:Function_Name><ogc:Function_Name nArgs="1">cos</ogc:Function_Name><ogc:Function_Name nArgs="2">crosses</ogc:Function_Name><ogc:Function_Name nArgs="2">dateFormat</ogc:Function_Name><ogc:Function_Name nArgs="2">dateParse</ogc:Function_Name><ogc:Function_Name nArgs="2">difference</ogc:Function_Name><ogc:Function_Name nArgs="1">dimension</ogc:Function_Name><ogc:Function_Name nArgs="2">disjoint</ogc:Function_Name><ogc:Function_Name nArgs="2">distance</ogc:Function_Name><ogc:Function_Name nArgs="1">double2bool</ogc:Function_Name><ogc:Function_Name nArgs="1">endPoint</ogc:Function_Name><ogc:Function_Name nArgs="1">envelope</ogc:Function_Name><ogc:Function_Name nArgs="2">EqualInterval</ogc:Function_Name><ogc:Function_Name nArgs="2">equalsExact</ogc:Function_Name><ogc:Function_Name nArgs="3">equalsExactTolerance</ogc:Function_Name><ogc:Function_Name nArgs
 ="2">equalTo</ogc:Function_Name><ogc:Function_Name nArgs="1">exp</ogc:Function_Name><ogc:Function_Name nArgs="1">exteriorRing</ogc:Function_Name><ogc:Function_Name nArgs="1">floor</ogc:Function_Name><ogc:Function_Name nArgs="1">geometryType</ogc:Function_Name><ogc:Function_Name nArgs="1">geomFromWKT</ogc:Function_Name><ogc:Function_Name nArgs="1">geomLength</ogc:Function_Name><ogc:Function_Name nArgs="2">getGeometryN</ogc:Function_Name><ogc:Function_Name nArgs="1">getX</ogc:Function_Name><ogc:Function_Name nArgs="1">getY</ogc:Function_Name><ogc:Function_Name nArgs="1">getZ</ogc:Function_Name><ogc:Function_Name nArgs="2">greaterEqualThan</ogc:Function_Name><ogc:Function_Name nArgs="2">greaterThan</ogc:Function_Name><ogc:Function_Name nArgs="0">id</ogc:Function_Name><ogc:Function_Name nArgs="2">IEEEremainder</ogc:Function_Name><ogc:Function_Name nArgs="3">if_then_else</ogc:Function_Name><ogc:Function_Name nArgs="11">in10</ogc:Function_Name><ogc:Function_Name nArgs="3">in2</ogc
 :Function_Name><ogc:Function_Name nArgs="4">in3</ogc:Function_Name><ogc:Function_Name nArgs="5">in4</ogc:Function_Name><ogc:Function_Name nArgs="6">in5</ogc:Function_Name><ogc:Function_Name nArgs="7">in6</ogc:Function_Name><ogc:Function_Name nArgs="8">in7</ogc:Function_Name><ogc:Function_Name nArgs="9">in8</ogc:Function_Name><ogc:Function_Name nArgs="10">in9</ogc:Function_Name><ogc:Function_Name nArgs="1">int2bbool</ogc:Function_Name><ogc:Function_Name nArgs="1">int2ddouble</ogc:Function_Name><ogc:Function_Name nArgs="1">interiorPoint</ogc:Function_Name><ogc:Function_Name nArgs="2">interiorRingN</ogc:Function_Name><ogc:Function_Name nArgs="2">intersection</ogc:Function_Name><ogc:Function_Name nArgs="2">intersects</ogc:Function_Name><ogc:Function_Name nArgs="1">isClosed</ogc:Function_Name><ogc:Function_Name nArgs="1">isEmpty</ogc:Function_Name><ogc:Function_Name nArgs="2">isLike</ogc:Function_Name><ogc:Function_Name nArgs="1">isNull</ogc:Function_Name><ogc:Function_Name nArgs
 ="1">isRing</ogc:Function_Name><ogc:Function_Name nArgs="1">isSimple</ogc:Function_Name><ogc:Function_Name nArgs="1">isValid</ogc:Function_Name><ogc:Function_Name nArgs="3">isWithinDistance</ogc:Function_Name><ogc:Function_Name nArgs="1">length</ogc:Function_Name><ogc:Function_Name nArgs="2">lessEqualThan</ogc:Function_Name><ogc:Function_Name nArgs="2">lessThan</ogc:Function_Name><ogc:Function_Name nArgs="1">log</ogc:Function_Name><ogc:Function_Name nArgs="2">max</ogc:Function_Name><ogc:Function_Name nArgs="2">max_2</ogc:Function_Name><ogc:Function_Name nArgs="2">max_3</ogc:Function_Name><ogc:Function_Name nArgs="2">max_4</ogc:Function_Name><ogc:Function_Name nArgs="2">min</ogc:Function_Name><ogc:Function_Name nArgs="2">min_2</ogc:Function_Name><ogc:Function_Name nArgs="2">min_3</ogc:Function_Name><ogc:Function_Name nArgs="2">min_4</ogc:Function_Name><ogc:Function_Name nArgs="1">not</ogc:Function_Name><ogc:Function_Name nArgs="2">notEqualTo</ogc:Function_Name><ogc:Function_N
 ame nArgs="1">numGeometries</ogc:Function_Name><ogc:Function_Name nArgs="1">numInteriorRing</ogc:Function_Name><ogc:Function_Name nArgs="1">numPoints</ogc:Function_Name><ogc:Function_Name nArgs="2">overlaps</ogc:Function_Name><ogc:Function_Name nArgs="1">parseBoolean</ogc:Function_Name><ogc:Function_Name nArgs="1">parseDouble</ogc:Function_Name><ogc:Function_Name nArgs="1">parseInt</ogc:Function_Name><ogc:Function_Name nArgs="0">pi</ogc:Function_Name><ogc:Function_Name nArgs="2">pointN</ogc:Function_Name><ogc:Function_Name nArgs="2">pow</ogc:Function_Name><ogc:Function_Name nArgs="1">PropertyExists</ogc:Function_Name><ogc:Function_Name nArgs="2">Quantile</ogc:Function_Name><ogc:Function_Name nArgs="0">random</ogc:Function_Name><ogc:Function_Name nArgs="2">relate</ogc:Function_Name><ogc:Function_Name nArgs="3">relatePattern</ogc:Function_Name><ogc:Function_Name nArgs="1">rint</ogc:Function_Name><ogc:Function_Name nArgs="1">round</ogc:Function_Name><ogc:Function_Name nArgs="1"
 >round_2</ogc:Function_Name><ogc:Function_Name nArgs="1">roundDouble</ogc:Function_Name><ogc:Function_Name nArgs="1">sin</ogc:Function_Name><ogc:Function_Name nArgs="1">sqrt</ogc:Function_Name><ogc:Function_Name nArgs="2">StandardDeviation</ogc:Function_Name><ogc:Function_Name nArgs="1">startPoint</ogc:Function_Name><ogc:Function_Name nArgs="2">strConcat</ogc:Function_Name><ogc:Function_Name nArgs="2">strEndsWith</ogc:Function_Name><ogc:Function_Name nArgs="2">strEqualsIgnoreCase</ogc:Function_Name><ogc:Function_Name nArgs="2">strIndexOf</ogc:Function_Name><ogc:Function_Name nArgs="2">strLastIndexOf</ogc:Function_Name><ogc:Function_Name nArgs="1">strLength</ogc:Function_Name><ogc:Function_Name nArgs="2">strMatches</ogc:Function_Name><ogc:Function_Name nArgs="4">strReplace</ogc:Function_Name><ogc:Function_Name nArgs="2">strStartsWith</ogc:Function_Name><ogc:Function_Name nArgs="3">strSubstring</ogc:Function_Name><ogc:Function_Name nArgs="2">strSubstringStart</ogc:Function_Nam
 e><ogc:Function_Name nArgs="1">strToLowerCase</ogc:Function_Name><ogc:Function_Name nArgs="1">strToUpperCase</ogc:Function_Name><ogc:Function_Name nArgs="1">strTrim</ogc:Function_Name><ogc:Function_Name nArgs="2">symDifference</ogc:Function_Name><ogc:Function_Name nArgs="1">tan</ogc:Function_Name><ogc:Function_Name nArgs="1">toDegrees</ogc:Function_Name><ogc:Function_Name nArgs="1">toRadians</ogc:Function_Name><ogc:Function_Name nArgs="2">touches</ogc:Function_Name><ogc:Function_Name nArgs="1">toWKT</ogc:Function_Name><ogc:Function_Name nArgs="2">union</ogc:Function_Name><ogc:Function_Name nArgs="2">UniqueInterval</ogc:Function_Name><ogc:Function_Name nArgs="2">within</ogc:Function_Name></ogc:Function_Names></ogc:Functions></ogc:Arithmetic_Operators></ogc:Scalar_Capabilities></ogc:Filter_Capabilities></WFS_Capabilities>';
@@ -27,6 +28,7 @@
         t.eq(ft[0]["title"], "Manhattan (NY) landmarks", "title of first feature type correct");
         t.eq(ft[0]["name"], "poly_landmarks", "name of first feature type correct");
         t.eq(ft[0]["featureNS"], "http://www.census.gov", "ns of first feature type correct");
+        t.eq(ft[0]["srs"], "EPSG:4326", "srs of first feature type correct");
 
         var service = res.service;
         t.eq(service.name, 'WFS', "service name correct");
@@ -137,6 +139,7 @@
         t.eq(ft.length, 2, "number of feature types correct");
         t.eq(ft[0]["title"], "Parks", "title of first feature type correct");
         t.eq(ft[0]["name"], "park", "name of first feature type correct");
+        t.eq(ft[0]["srs"], "EPSG:42304", "srs of first feature type correct");
         
         var service = res.service;
         t.eq(service.name, 'MapServer WFS', "service name correct");

Modified: sandbox/tschaub/canvas/tests/Format/WMC.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/WMC.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/WMC.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -5,9 +5,10 @@
 
     var v1_0_0 = '<ViewContext xmlns="http://www.opengis.net/context" version="1.0.0" id="OpenLayers_Context_233" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.0.0/context.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><Window width="512" height="256"/><BoundingBox minx="-109.9709708" miny="27.01451459" maxx="-80.02902918" maxy="41.98548541" SRS="EPSG:4326"/><Title/><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/></Extension></General><LayerList><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://t1.hypercube.telascience.org/cgi-bin/landsat7"/></Server><Name>landsat7</Name><Title>NASA Global Mosaic</Title><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><N
 ame/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/></Extension></Layer><Layer queryable="1" hidden="1"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://labs.metacarta.com/wms/vmap0"/></Server><Name>basic</Name><Title>OpenLayers WMS</Title><FormatList><Format current="1">im
 age/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://lioapp.lrc.gov.on.ca/cubeserv/cubeserv.pl"/></Server><Name>na_road:CCRS</Name><Title>Transportation Network</Title><FormatList><Format curre
 nt="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-166.5320000" miny="4.050460000" maxx="-0.2068180000" maxy="70.28700000"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.6</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" x
 link:href="http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms"/></Server><Name>3:1</Name><Title>Radar 3:1</Title><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-131.0294952" miny="14.56289673" maxx="-61.02950287" maxy="54.56289673"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.8</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">true</ol:singleTile></Extension
 ></Layer></LayerList></ViewContext>';
     var v1_1_0 = '<ViewContext xmlns="http://www.opengis.net/context" version="1.1.0" id="OpenLayers_Context_232" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><General><Window width="512" height="256"/><BoundingBox minx="-109.9709708" miny="27.01451459" maxx="-80.02902918" maxy="41.98548541" SRS="EPSG:4326"/><Title/><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/></Extension></General><LayerList><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://t1.hypercube.telascience.org/cgi-bin/landsat7"/></Server><Name>landsat7</Name><Title>NASA Global Mosaic</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenomin
 ator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="1"><Server service="OGC:WMS" version="1.1.1"><OnlineResource x
 link:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://labs.metacarta.com/wms/vmap0"/></Server><Name>basic</Name><Title>OpenLayers WMS</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/jpeg</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-130.0000000" miny="14.00000000" maxx="-60.00000000" maxy="55.00000000"/><ol:tileSize xmlns:ol="http://openlayers.org/context" width="512" height="1024"/><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">true</ol:isBaseLayer><ol:displayInLayerSwitch
 er xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://lioapp.lrc.gov.on.ca/cubeserv/cubeserv.pl"/></Server><Name>na_road:CCRS</Name><Title>Transportation Network</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6200000.000</sld:MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">32000000.00</sld:MaxScaleDenominator><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-166.5320000" miny="4.050460000" maxx="-0.2068180000" maxy="70.28700000"/><ol:tileSize xmlns:ol="http://openlayer
 s.org/context" width="512" height="1024"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.6</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">false</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">false</ol:singleTile></Extension></Layer><Layer queryable="1" hidden="0"><Server service="OGC:WMS" version="1.1.1"><OnlineResource xlink:type="simple" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://columbo.nrlssc.navy.mil/ogcwms/servlet/WMSServlet/AccuWeather_Maps.wms"/></Server><Name>3:1</Name><Title>Radar 3:1</Title><sld:MinScaleDenominator xmlns:sld="http://www.opengis.net/sld">6299645.760</sld:
 MinScaleDenominator><sld:MaxScaleDenominator xmlns:sld="http://www.opengis.net/sld">31498228.80</sld:MaxScaleDenominator><FormatList><Format current="1">image/png</Format></FormatList><StyleList><Style current="1"><Name/><Title>Default</Title></Style></StyleList><Extension><ol:maxExtent xmlns:ol="http://openlayers.org/context" minx="-131.0294952" miny="14.56289673" maxx="-61.02950287" maxy="54.56289673"/><ol:transparent xmlns:ol="http://openlayers.org/context">TRUE</ol:transparent><ol:numZoomLevels xmlns:ol="http://openlayers.org/context">4</ol:numZoomLevels><ol:units xmlns:ol="http://openlayers.org/context">degrees</ol:units><ol:isBaseLayer xmlns:ol="http://openlayers.org/context">false</ol:isBaseLayer><ol:opacity xmlns:ol="http://openlayers.org/context">0.8</ol:opacity><ol:displayInLayerSwitcher xmlns:ol="http://openlayers.org/context">true</ol:displayInLayerSwitcher><ol:singleTile xmlns:ol="http://openlayers.org/context">true</ol:singleTile></Extension></Layer></LayerList
 ></ViewContext>';
+    var polar = '<ViewContext xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:default="http://www.opengis.net/context" xmlns:ol="http://openlayers.org/context" xmlns="http://www.opengis.net/context" xmlns:wms="http://www.opengis.net/wms" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1.0" id="OpenLayers_Context_466" xsi:schemaLocation="http://www.opengis.net/context http://schemas.opengis.net/context/1.1.0/context.xsd"><General><BoundingBox minx="-3000000" miny="-3000000" maxx="7000000" maxy="7000000" SRS="EPSG:32661"/><Title>WMS viewer</Title><Extension><ol:maxExtent minx="-3000000" miny="-3000000" maxx="7000000" maxy="7000000"/><ol:units>m</ol:units></Extension></General><LayerList xmlns="http://www.opengis.net/context"><Layer queryable="0" hidden="0"><Server service="OGC:WMS" version="1.1.1" foo="bar"><OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple" xlink:href="http://wms.met.no/maps/northpole.map"/></Server><Name>world</Na
 me><Title>The World</Title><FormatList><Format>image/jpeg</Format><Format current="1">image/png</Format></FormatList></Layer></LayerList></ViewContext>';
 
     function test_Format_WMC_read(t) {
-        t.plan(36);
+        t.plan(37);
 
         var format = new OpenLayers.Format.WMC();        
         var map, layer;
@@ -53,7 +54,7 @@
         map.destroy();
         
         // test v1.1.0
-        var map = format.read(v1_1_0, {map: "map"});
+        map = format.read(v1_1_0, {map: "map"});
         t.eq(map.center.lon.toPrecision(6), (-95).toPrecision(6), "(v1.1.0) map center correctly set");
         t.eq(map.center.lat.toPrecision(6), (34.5).toPrecision(6), "(v1.1.0) map center correctly set");
         t.eq(map.maxExtent.left.toPrecision(6), (-130).toPrecision(6), "(v1.1.0) map maxExtent correctly set");
@@ -91,8 +92,14 @@
              "(v1.1.0) layer opacity correctly set");
         map.destroy();
 
+        // Check if maxResolution is set correctly
+        map = format.read(polar, {map: "map"});
+        t.eq(map.maxResolution, 39062.5,
+             "maxResolution correctly set");
+        map.destroy();
+
         // test mapOptions
-        var map = format.read(v1_1_0, {map: {foo: 'bar', div: 'map'}});
+        map = format.read(v1_1_0, {map: {foo: 'bar', div: 'map'}});
         t.eq(map.foo, "bar",
             "mapOptions correctly passed to the created map object");
         map.destroy();

Modified: sandbox/tschaub/canvas/tests/Format/WMSGetFeatureInfo.html
===================================================================
--- sandbox/tschaub/canvas/tests/Format/WMSGetFeatureInfo.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Format/WMSGetFeatureInfo.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -4,7 +4,7 @@
     <script type="text/javascript">
     
     function test_read_FeatureInfoResponse(t) {
-        t.plan(5);
+        t.plan(7);
         
         var parser = new OpenLayers.Format.WMSGetFeatureInfo();
 
@@ -49,10 +49,29 @@
         t.eq(features[1].attributes.STATE_NAME, 'Wyoming',
              "Attribute STATE_NAME contains the right value");
 
+        text = '<FeatureInfoResponse>' +
+            '<FIELDS>' +
+            '<FIELD name="ID" value="B31A0154"/>' +
+            '<FIELD name="FID" value="31AL0011"/>' +
+            '</FIELDS>' +
+            '<FIELDS>' +
+            '<FIELD name="ID" value="B31A0153"/>' +
+            '<FIELD name="FID" value="31AL0011"/>' +
+            '</FIELDS>' +
+            '</FeatureInfoResponse>';
+
+        features = parser.read(text);
+
+        t.eq(features.length, 2,
+             "Parsed 2 features in total");
+
+        t.eq(features[1].attributes.FID, '31AL0011',
+             "Attribute FID contains the right value");
+
     }
 
     function test_read_msGMLOutput(t) {
-        t.plan(12);
+        t.plan(13);
         
         var parser = new OpenLayers.Format.WMSGetFeatureInfo();
 
@@ -69,6 +88,28 @@
         t.eq(features.length, 0,
              "Parsing empty msGMLOutput response succesfull");
 
+        // read empty attribute
+        text =
+            '<?xml version="1.0" encoding="ISO-8859-1"?>' +
+            '<msGMLOutput ' +
+            '    xmlns:gml="http://www.opengis.net/gml"' +
+            '    xmlns:xlink="http://www.w3.org/1999/xlink"' +
+            '    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' +
+            '   <AAA64_layer>' +
+            '           <AAA64_feature>' +
+            '                   <gml:boundedBy>' +
+            '                           <gml:Box srsName="EPSG:28992">' +
+            '                                   <gml:coordinates>107397.266000,460681.063000 116568.188000,480609.250000</gml:coordinates>' +
+            '                           </gml:Box>' +
+            '                   </gml:boundedBy>' +
+            '                   <FOO>bar</FOO>' +
+            '                   <EMPTY></EMPTY>' +
+            '           </AAA64_feature>' +
+            '   </AAA64_layer>' +
+            '</msGMLOutput>';
+        features = parser.read(text);
+        t.eq((features[0].attributes.EMPTY === null), true, "Empty attribute is parsed as null");
+
         // read 1 feature from 1 layer
         text = 
             '<?xml version="1.0" encoding="ISO-8859-1"?>' +

Modified: sandbox/tschaub/canvas/tests/Handler/Box.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Box.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Handler/Box.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -2,6 +2,64 @@
 <head>
   <script src="../OLLoader.js"></script>
   <script type="text/javascript">
+    function test_Handler_Box_constructor(t) {
+        t.plan(4);
+        var control = new OpenLayers.Control();
+        control.id = Math.random();
+        var callbacks = {done: "bar"};
+        var options = {bar: "foo"};
+        
+        var handler = new OpenLayers.Handler.Box(control, callbacks, options);
+
+        t.eq(handler.control.id, control.id, "handler created with the correct control");
+        t.eq(handler.callbacks.done, "bar", "handler created with the correct callback");
+        t.eq(handler.bar, "foo", "handler created with the correct options");
+        t.ok(handler.dragHandler instanceof OpenLayers.Handler.Drag, "drag handler created");
+    }
+
+    function test_Handler_Box_draw(t) {
+        var testAll = true;
+        if (document.defaultView && document.defaultView.getComputedStyle &&
+                    !document.defaultView.getComputedStyle(document.body)) {
+            // we don't get dimensions for hidden frames in FF4, and our test
+            // runs in a hidden frame.
+            testAll = false;
+        }
+        
+        t.plan(testAll ? 12 : 2);
+
+        var map = new OpenLayers.Map('map');
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+        var handler = new OpenLayers.Handler.Box(control, {done: function(e) {
+            t.ok(e.equals(new OpenLayers.Bounds(5, 11, 11, 5)), "box result correct");
+        }});
+        handler.activate();
+        handler.dragHandler.start = {x: 5, y: 5};
+        handler.startBox({x: 5, y: 5});
+        var offset = handler.getBoxOffsets();
+        if (testAll) {
+            t.eq(parseInt(handler.zoomBox.style.left), 5 - offset.left, "x position of box correct");
+            t.eq(parseInt(handler.zoomBox.style.top), 5 - offset.top, "y position of box correct");
+        }
+        handler.moveBox({x: 10, y: 10});
+        if (testAll) {
+            t.eq(parseInt(handler.zoomBox.style.left), 5 - offset.left, "x position of box still correct");
+            t.eq(parseInt(handler.zoomBox.style.top), 5 - offset.top, "y position of box still correct");
+            t.eq(parseInt(handler.zoomBox.style.width), 5 + offset.width + 1, "x dimension of box correct");
+            t.eq(parseInt(handler.zoomBox.style.height), 5 + offset.height + 1, "y dimension of box correct");
+        }
+        handler.moveBox({x: 0, y: 0});
+        if (testAll) {
+            t.eq(parseInt(handler.zoomBox.style.left), 0 - offset.left, "new x position of box correct");
+            t.eq(parseInt(handler.zoomBox.style.top), 0 - offset.top, "new y position of box correct");
+            t.eq(parseInt(handler.zoomBox.style.width), 5 + offset.width + 1, "x dimension of box still correct");
+            t.eq(parseInt(handler.zoomBox.style.height), 5 + offset.height + 1, "y dimension of box still correct");
+        }
+        handler.endBox({x: 11, y: 11});
+        t.eq(handler.zoomBox, null, "box removed after endBox");
+    }
+    
     function test_Handler_Box_destroy(t) {
         t.plan(1);
         var map = new OpenLayers.Map('map');
@@ -21,6 +79,6 @@
   </script>
 </head>
 <body>
-    <div id="map" style="width: 300px; height: 150px;"/>
+    <div id="map" style="width: 300px; height: 150px;"></div>
 </body>
 </html>

Modified: sandbox/tschaub/canvas/tests/Handler/Click.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Click.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Handler/Click.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -2,6 +2,11 @@
 <head>
   <script src="../OLLoader.js"></script>
   <script type="text/javascript">
+  
+    function px(x, y) {
+        return new OpenLayers.Pixel(x, y);
+    }
+  
     function test_Handler_Click_constructor(t) {
         t.plan(3);
         var control = new OpenLayers.Control();
@@ -94,140 +99,314 @@
         handler.activate();
 
     }
+    
+    var callbackMap;
+    function callbackSetup(log, options) {
+        callbackMap = new OpenLayers.Map({
+            div: "map",
+            controls: [], // no controls here because these tests use a custom setTimeout and we only want setTimeout calls from a single handler 
+            layers: [new OpenLayers.Layer(null, {isBaseLayer: true})],
+            center: new OpenLayers.LonLat(0, 0),
+            zoom: 1
+        });
+        var control = new OpenLayers.Control();
+        callbackMap.addControl(control);
 
-    function test_Handler_Click_callbacks(t) {
-        t.plan(13);
-        
-        var map = new OpenLayers.Map('map', {controls: []});
-
-        var control = {
-            map: map
+        var callbacks = {
+            "click": function(evt) {
+                log.push({callback: "click", evt: evt});
+            },
+            "dblclick": function(evt) {
+                log.push({callback: "dblclick", evt: evt});
+            }
         };
-
-        var handler = new OpenLayers.Handler.Click(control, {});
+        var handler = new OpenLayers.Handler.Click(control, callbacks, options);
         handler.activate();
         
         
-        // set up for single click - three tests here
         var timers = {};
-        var sto = window.setTimeout;
+        window._setTimeout = window.setTimeout;
         window.setTimeout = function(func, delay) {
-            var key = Math.random();
+            log.push({method: "setTimeout", func: func, delay: delay});
+            var key = (new Date).getTime() + "-" + Math.random();
             timers[key] = true;
-            t.ok(typeof func == "function",
-                 "setTimeout called with a function");
-            t.eq(delay, handler.delay,
-                 "setTimeout called with proper delay");
             // execute function that is supposed to be delayed
             func();
             return key;
         }
-        var cto = window.clearTimeout;
+        window._clearTimeout = window.clearTimeout;
         window.clearTimeout = function(key) {
-            if(timers[key] === true) {
-                delete timers[key];
-            } else {
-                t.fail("clearTimeout called with non-existent timerId");
-            }
+            log.push({
+                method: "clearTimeout",
+                keyExists: (key in timers)
+            });
+            delete timers[key];
         }
+        return handler;    
+    }
+    
+    function callbackTeardown() {
+        window.setTimeout = window._setTimeout;
+        window.clearTimeout = window._clearTimeout;
+        callbackMap.destroy();
+        callbackMap = null;
+    }
+
+    function test_callbacks_click_default(t) {
+        t.plan(6);
+
+        var log = [];
+        var handler = callbackSetup(log);
+        
+        // set up for single click - three tests here
         var testEvt = {id: Math.random()};
-        handler.callbacks = {
-            "click": function(evt) {
-                t.eq(evt.id, testEvt.id,
-                     "(click w/ single true) click callback called with correct evt");
-            },
-            "dblclick": function(evt) {
-                t.fail("(click w/ single true) dblclick should not be called here");
-            }
+        handler.map.events.triggerEvent("click", testEvt);
+        t.eq(log.length, 2, "(click w/ single true) two items logged");
+
+        // first item logged is setTimeout call
+        t.eq(log[0].method, "setTimeout", "setTimeout called");
+        t.eq(typeof log[0].func, "function", "setTimeout called with a function");
+        t.eq(log[0].delay, handler.delay, "setTimeout called with proper delay");
+        
+        // second item logged is from click callback
+        t.eq(log[1].callback, "click", "click callback called");
+        t.eq(log[1].evt.id, testEvt.id, "got correct event");
+        
+        callbackTeardown();
+    }
+    
+    function test_callbacks_dblclick_default(t) {
+        t.plan(1);
+
+        var log = [];
+        var handler = callbackSetup(log);
+        var testEvt = {id: Math.random()};
+        handler.map.events.triggerEvent("dblclick", testEvt);
+        
+        t.eq(log.length, 0, "nothing happens by default with dblclick (double is false)");
+        
+        callbackTeardown();
+        
+    }
+
+    function test_callbacks_dblclick_double(t) {
+        t.plan(3);
+
+        var log = [];
+        var handler = callbackSetup(log, {"double": true});
+        var testEvt = {id: Math.random()};
+        handler.map.events.triggerEvent("dblclick", testEvt);
+        
+        t.eq(log.length, 1, "one item logged");
+        t.eq(log[0].callback, "dblclick", "dblclick callback called")
+        t.eq(log[0].evt.id, testEvt.id, "dblclick callback called with event");
+        
+        callbackTeardown();
+        
+    }
+
+    function test_callbacks_dblclick_sequence(t) {
+        t.plan(8);
+
+        var log = [];
+        var handler = callbackSetup(log, {"double": true});
+        var testEvt = {id: Math.random()};
+
+        // first click - set timer for next
+        handler.map.events.triggerEvent("click", testEvt);
+        t.ok(handler.timerId != null, "timer is set");
+        log.pop(); // because the test setTimeout is synchronous we get the click callback immediately
+        t.eq(log.length, 1, "one item logged (after pop due to synchronous setTimeout call in our tests");
+        t.eq(log[0].method, "setTimeout", "setTimeout called first");
+
+        // second click - timer cleared
+        handler.map.events.triggerEvent("click", testEvt);
+        t.ok(handler.timerId == null, "timer is cleared");
+        t.eq(log.length, 2, "two items logged after second click");
+        t.eq(log[1].method, "clearTimeout", "clearTimeout called second");
+
+        // dblclick event - callback called
+        handler.map.events.triggerEvent("dblclick", testEvt);        
+        t.eq(log.length, 3, "three items logged");
+        t.eq(log[2].callback, "dblclick", "dblclick callback called third");
+        
+        callbackTeardown();
+        
+    }
+
+    function test_callbacks_within_pixelTolerance(t) {
+        t.plan(1);
+
+        var log = [];
+        var handler = callbackSetup(log, {"double": true, pixelTolerance: 2});
+
+        var down = {
+            xy: px(0, 0)
         };
-        map.events.triggerEvent("click", testEvt);
+        var up = {
+            xy: px(0, 1)
+        };
+
+        handler.map.events.triggerEvent("mousedown", down);
+        handler.map.events.triggerEvent("mouseup", up);
+        handler.map.events.triggerEvent("click", up);
         
-        // set up for double click with double false - no tests here (only failures)
-        handler.callbacks = {
-            "click": function(evt) {
-                t.fail("(dblclick w/ double false) click should not be called here");
-            },
-            "dblclick": function(evt) {
-                t.fail("(dblclick w/ double false) dblclick should not be called here");
-            }
+        t.eq(log[log.length-1].callback, "click", "click callback called");
+        
+        callbackTeardown();
+        
+    }
+
+    function test_callbacks_outside_pixelTolerance(t) {
+        t.plan(2);
+
+        var log = [];
+        var handler = callbackSetup(log, {pixelTolerance: 2});
+
+        var down = {
+            xy: px(0, 0)
         };
-        testEvt = Math.random();
-        map.events.triggerEvent("dblclick", testEvt);
+        var up = {
+            xy: px(2, 3)
+        };
 
-        // set up for double click with double true - one test here
-        handler.double = true;
-        handler.callbacks = {
-            "click": function(evt) {
-                t.fail("(dblclick w/ double true) click should not be called here");
-            },
-            "dblclick": function(evt) {
-                t.eq(evt, testEvt,
-                     "(dblclick w/ double true) dblclick called with correct evt");
-            }
-        };
-        testEvt = Math.random();
-        map.events.triggerEvent("dblclick", testEvt);
+        handler.map.events.triggerEvent("mousedown", down);
+        t.ok(handler.down && handler.down.xy.equals(down.xy), "down position set");
+
+        handler.map.events.triggerEvent("mouseup", up);
+        handler.map.events.triggerEvent("click", up);
+        t.eq(log.length, 0, "nothing logged - event outside tolerance");
         
-        // set up for two clicks with double true - 6 tests here (with timeout ones from above)
-        handler.double = true;
-        handler.callbacks = {
-            "click": function(evt) {
-                t.ok(evt != null, "(two clicks w/ double true) click will not be called here if next three tests pass");
-            },
-            "dblclick": function(evt) {
-                t.eq(evt, testEvt,
-                     "(two clicks w/ double true) dblclick called with correct evt");
-            }
+        callbackTeardown();
+        
+    }
+
+    function test_callbacks_within_dblclickTolerance(t) {
+        t.plan(6);
+
+        var log = [];
+        var handler = callbackSetup(log, {single: false, "double": true, dblclickTolerance: 8});
+
+        var first = {
+            xy: px(0, 0)
         };
-        testEvt = Math.random();
-        map.events.triggerEvent("click", testEvt);
-        t.ok(handler.timerId != null,
-             "(two clicks w/ double true) timer is set to call click");
-        map.events.triggerEvent("click", testEvt);
-        t.ok(handler.timerId == null,
-             "(two clicks w/ double true) timer is cleared to call click");
-        map.events.triggerEvent("dblclick", testEvt);
-        handler.destroy();
+        var second = {
+            xy: px(0, 5)
+        };
+
+        handler.map.events.triggerEvent("mousedown", first);
+        handler.map.events.triggerEvent("mouseup", first);
+        handler.map.events.triggerEvent("click", first);
+        t.eq(log.length, 1, "one item logged");
+        t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called");
         
-        // set up to tests pixelTolerance - three tests here (2 from setTimeout above)
-        handler = new OpenLayers.Handler.Click(control, {}, {
-            pixelTolerance: 2
-        });
-        handler.activate();
-        var downEvt = {
-            xy: new OpenLayers.Pixel(0, 0)
+        handler.map.events.triggerEvent("mousedown", second);
+        handler.map.events.triggerEvent("mouseup", second);
+        handler.map.events.triggerEvent("click", second);
+        t.eq(log.length, 2, "two events logged");
+        t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called");
+        
+        handler.map.events.triggerEvent("dblclick", second);
+        t.eq(log.length, 3, "three items logged");
+        t.eq(log[2] && log[2].callback, "dblclick", "dblclick callback called");
+        
+        callbackTeardown();        
+    }
+
+    function test_callbacks_outside_dblclickTolerance(t) {
+        t.plan(5);
+
+        var log = [];
+        // default dblclickTolerance is 13
+        var handler = callbackSetup(log, {single: false, "double": true});
+
+        var first = {
+            xy: px(0, 0)
         };
-        map.events.triggerEvent("mousedown", downEvt);
-        var clickEvt = {
-            xy: new OpenLayers.Pixel(0, 1)
+        var second = {
+            xy: px(13.5, 0)
         };
-        // mouse moves one pixel, click should be called
-        handler.callbacks = {
-            "click": function(evt) {
-                t.ok(evt.xy == clickEvt.xy, "(pixelTolerance met) click called");
+
+        handler.map.events.triggerEvent("mousedown", first);
+        handler.map.events.triggerEvent("mouseup", first);
+        handler.map.events.triggerEvent("click", first);
+        t.eq(log.length, 1, "one item logged");
+        t.eq(log[0] && log[0].method, "setTimeout", "setTimeout called");
+
+        handler.map.events.triggerEvent("mousedown", second);
+        handler.map.events.triggerEvent("mouseup", second);
+        handler.map.events.triggerEvent("click", second);
+        t.eq(log.length, 2, "two items logged");
+        t.eq(log[1] && log[1].method, "clearTimeout", "clearTimeout called");
+
+        handler.map.events.triggerEvent("dblclick", second);
+        t.eq(log.length, 2, "still two items logged - dblclick callback is not called");
+        
+        callbackTeardown();
+    }
+
+    function test_callbacks_multitouch_single(t) {
+
+        t.plan(2);
+
+        var log = [];
+
+        var callbacks = {
+            click: function(evt) {
+                log.push({callback: "click", type: evt.type});
+            },
+            dblclick: function(evt) {
+                log.push({callback: "dblclick", type: evt.type});
             }
         };
-        map.events.triggerEvent("click", clickEvt);
-        handler.clearTimer();
+
+        var map = new OpenLayers.Map("map");
+        var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+        var handler = new OpenLayers.Handler.Click(
+            control, callbacks,
+            {"double": true, single: true, pixelTolerance: 2}
+        );
+
+        // we override here so we don't have to wait for the timeout
+        handler.queuePotentialClick = function(evt) {
+            log.push({potential: true, evt: evt});
+            OpenLayers.Handler.Click.prototype.queuePotentialClick.call(this, evt);
+        }
         
-        // mouse moves 3x3 pixels, click should not be called
-        map.events.triggerEvent("mousedown", downEvt);
-        var clickEvt = {
-            xy: new OpenLayers.Pixel(3, 3)
-        };
-        // mouse moves one pixel, click should be called
-        handler.callbacks = {
-            "click": function(evt) {
-                t.fail("(pixelTolerance not met) click should not be called");
+        handler.activate();
+        
+        function handle(o) {
+            var touches = [];
+            if (("x0" in o) && ("y0" in o)) {
+                touches.push({
+                    clientX: o.x0, clientY: o.y0
+                });
             }
-        };
-        map.events.triggerEvent("click", clickEvt); // no test run
-        handler.clearTimer();
+            if (("x1" in o) && ("y1" in o)) {
+                touches.push({
+                    clientX: o.x1, clientY: o.y1
+                });
+            }
+            handler.map.events.handleBrowserEvent({
+                type: o.type, touches: touches
+            });
+        }
+
+        // a typical multitouch sequence goes like this:
+        // touchstart, touchstart, touchend, touchend
+        handle({type: "touchstart", x0: 10, y0: 10});
+        handle({type: "touchstart", x0: 10, y0: 10, x1: 30, y1: 15});
+        handle({type: "touchend"});
+        handle({type: "touchend"});
         
-        window.setTimeout = sto;
-        window.clearTimeout = cto;
+        t.eq(log.length, 1, "one item logged");
+        t.eq(log[0] && log[0].potential, true, "click in queue - no dblclick called");
         
-
+        map.destroy();
     }
 
     function test_Handler_Click_deactivate(t) {
@@ -253,42 +432,60 @@
     }
 
     function test_Handler_Click_mouseup(t) {
-        t.plan(4);
-        g_Propagate = {};
-        g_evt = {};
-                
-      //no modifiers, no handlerightclicks, no isrightclick        
+        t.plan(11);
+
+        var map = new OpenLayers.Map("map");
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+        var handler = new OpenLayers.Handler.Click(control);
+
+        var testEvent = {id: Math.random()};
+        var propagate = true;
+        var log, got, modMatch, rightClick;
+
+        // override methods to log what is called
         var temp = OpenLayers.Event.isRightClick;
         OpenLayers.Event.isRightClick = function(e) { 
-            t.ok(e == g_evt, 'correct event passed in to checkModifiers');
-            return false; 
+            log.push({method: "isRightClick", evt: e});
+            return rightClick; 
         };
-
-        var h = {
-            'checkModifiers': function(e) {
-                t.ok(e == g_evt, 'correct event passed in to checkModifiers');
-                return false;
-            },
-            'control': {
-                'handleRightClicks': false
-            },
-            'rightclick': function(e) {
-                t.ok(e == g_evt, 'correct event passed in to checkModifiers');
-                return g_Propagate;
-            }
+        handler.checkModifiers = function(e) {
+            log.push({method: "checkModifiers", evt: e});
+            return modMatch;
         };
-        var propagate = OpenLayers.Handler.Click.prototype.mouseup.apply(h, [g_evt]);
-        t.ok(propagate, "default propagate is true when no modifiers, no handlerightclicks, no isrightclick")
+        handler.rightclick = function(e) {
+            log.push({method: "rightclick", evt: e});
+            return propagate;
+        };
 
-      //modifiers, handlerightclicks, and isrightclick
-        h.checkModifiers = function() { return true; };
-        h.control.handleRightClicks = true;        
-        OpenLayers.Event.isRightClick = function(e) { return true; };
-        propagate = OpenLayers.Handler.Click.prototype.mouseup.apply(h, [g_evt]);
+        
+        // simulate an event with non-matching modifiers
+        log = [];
+        modMatch = false;
+        rightClick = false;
+        got = handler.mouseup(testEvent);
+        t.eq(log.length, 1, "one item logged");
+        t.eq(log[0] && log[0].method, "checkModifiers", "a) checkModifiers called first");
+        t.eq(log[0] && log[0].evt, testEvent, "a) first method called with correct event");
 
-        t.ok(propagate == g_Propagate, "return from handler's rightClick() returned from mouseup");
 
+        // modifiers, handlerightclicks, and isrightclick
+        log = [];
+        rightClick = true;
+        modMatch = true;
+        handler.control.handleRightClicks = true;
+        got = handler.mouseup(testEvent);
+        t.eq(log.length, 3, "three items logged");
+        t.eq(log[0] && log[0].method, "checkModifiers", "b) checkModifiers called first");
+        t.eq(log[0] && log[0].evt, testEvent, "b) first method called with correct event");
+        t.eq(log[1] && log[1].method, "isRightClick", "b) isRightClick called second");
+        t.eq(log[1] && log[1].evt, testEvent, "b) second method called with correct event");
+        t.eq(log[2] && log[2].method, "rightclick", "b) rightclick called third");
+        t.eq(log[2] && log[2].evt, testEvent, "b) third method called with correct event");
+        t.eq(got, propagate, "b) return from handler's rightclick returned from mouseup");
+
         OpenLayers.Event.isRightClick = temp;
+        map.destroy();
     }
 
     function test_touch_click(t) {
@@ -315,7 +512,7 @@
         // test
 
         log = null;
-        handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo"]});
+        handler.touchstart({xy: px(1, 1), touches: ["foo"]});
         handler.touchend({touches: ["foo"]});
 
         t.delay_call(1, function() {
@@ -330,84 +527,125 @@
         });
     }
 
-    function test_touch_ignoresimulatedclick(t) {
-        t.plan(2);
+    function test_touch_within_dblclickTolerance(t) {
+        t.plan(4);
 
-        // set up
-
         var log;
 
-        var map = new OpenLayers.Map('map');
-        var control = {map: map};
-
         var callbacks = {
-            'dblclick': function(e) {
-                log.dblclick = {x: e.xy.x, y: e.xy.y,
-                   lastTouches: e.lastTouches};
+            click: function(evt) {
+                log.push({callback: "click", type: evt.type});
+            },
+            dblclick: function(evt) {
+                log.push({callback: "dblclick", type: evt.type});
             }
         };
 
+        var map = new OpenLayers.Map("map");
+        var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+        var control = new OpenLayers.Control();
+        map.addControl(control);
         var handler = new OpenLayers.Handler.Click(
-                control, callbacks,
-                {'double': true, pixelTolerance: null});
+            control, callbacks,
+            {"double": true, single: true, pixelTolerance: 2}
+        );
+        handler.activate();
+        
+        function handle(type, x, y) {
+            map.events.handleBrowserEvent({
+                type: type,
+                touches: [
+                    {clientX: x, clientY: y}
+                ]
+            });
+        }
 
         // test
+        log = [];
+        // sequence of two clicks on a touch device
+        // click 1
+        handle("touchstart", 10, 10);
+        handle("touchend", 11, 10);
+        handle("mousemove", 11, 10);
+        handle("mousedown", 10, 10);
+        handle("mouseup", 11, 10);
+        handle("click", 11, 10);
+        // click 2
+        handle("touchstart", 12, 10);
+        handle("touchend", 12, 10);
+        handle("mousedown", 12, 10);
+        handle("mouseup", 12, 10);
+        handle("click", 12, 10);
 
-        log = {};
-        handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo"]});
-        handler.touchend({});
-        handler.touchstart({xy: {x: 1, y: 1}, touches: ["foo"]});
-        handler.touchend({type: "click"});
+        t.eq(log.length, 1, "one callback called");
+        t.eq(log[0] && log[0].callback, "dblclick", "click callback called");
+        t.eq(log[0] && log[0].type, "touchend", "click callback called with touchend event");
+        t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click")
 
-        t.eq(!!handler.down.touches, true, "Handler down touches property should be truthy");
-        
-        t.ok(log.dblclick == undefined, "dblclick callback not called with simulated click");
-
         // tear down
         map.destroy();
     }
 
-    function test_touch_dblclick(t) {
-        t.plan(5);
+    function test_touch_outside_dblclickTolerance(t) {
+        t.plan(2);
 
-        // set up
-
         var log;
 
-        var map = new OpenLayers.Map('map');
-        var control = {map: map};
-
         var callbacks = {
-            'click': function(e) {
-                log.click = {x: e.xy.x, y: e.xy.y,
-                   lastTouches: e.lastTouches};
+            click: function(evt) {
+                log.push({callback: "click", type: evt.type});
             },
-            'dblclick': function(e) {
-                log.dblclick = {x: e.xy.x, y: e.xy.y,
-                   lastTouches: e.lastTouches};
+            dblclick: function(evt) {
+                log.push({callback: "dblclick", type: evt.type});
             }
         };
 
+        var map = new OpenLayers.Map("map");
+        var layer = new OpenLayers.Layer(null, {isBaseLayer: true});
+        map.addLayer(layer);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 1);
+        var control = new OpenLayers.Control();
+        map.addControl(control);
         var handler = new OpenLayers.Handler.Click(
-                control, callbacks,
-                {'double': true, pixelTolerance: null});
+            control, callbacks,
+            {"double": true, single: true, pixelTolerance: 2, dblclickTolerance: 8}
+        );
+        handler.activate();
+        
+        function handle(type, x, y) {
+            var touches = [];
+            if (x !== undefined && y !== undefined) {
+                touches.push({
+                    clientX: x, clientY: y
+                });
+            }
+            map.events.handleBrowserEvent({
+                type: type, touches: touches
+            });
+        }
 
         // test
+        log = [];
+        // sequence of two clicks on a touch device
+        // click 1
+        handle("touchstart", 10, 10);
+        handle("touchend");
+        handle("mousemove", 11, 10);
+        handle("mousedown", 10, 10);
+        handle("mouseup", 11, 10);
+        handle("click", 11, 10);
+        // click 2
+        handle("touchstart", 20, 10);
+        handle("touchend");
+        handle("mousedown", 20, 10);
+        handle("mouseup", 20, 10);
+        handle("click", 20, 10);
 
-        log = {};
-        handler.touchstart({xy: {x: 1, y: 1}, touches: [{clientX:0, clientY:10}]});
-        handler.touchend({});
-        handler.touchstart({xy: {x: 1, y: 1}, touches: [{clientX:0, clientY:10}]});
-        handler.touchend({});
+        t.eq(log.length, 0, "no callbacks called");
+        t.ok(!handler.timerId, "handler doesn't have a timerId waiting for click")
 
-        t.eq(log.click, undefined, "click callback not called");
-        t.ok(log.dblclick != undefined, "dblclick callback called");
-        if(log.dblclick != undefined) {
-            t.eq(log.dblclick.x, 1, "evt.xy.x as expected");
-            t.eq(log.dblclick.y, 1, "evt.xy.y as expected");
-            t.ok(log.dblclick.lastTouches, "evt.lastTouches on evt");
-        }
-
         // tear down
         map.destroy();
     }

Modified: sandbox/tschaub/canvas/tests/Handler/Drag.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Drag.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Handler/Drag.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -440,7 +440,159 @@
              "deactivate sets start to null");
     }
 
+    function test_interval_timer_after_mouseup(t) {
+        t.plan(5);
 
+        // set up
+
+        var map = new OpenLayers.Map('map');
+
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+
+        var moveCnt;
+
+        var handler = new OpenLayers.Handler.Drag(control, {}, {
+            interval: 1,
+            move: function() {
+                moveCnt++;
+            }
+        });
+        handler.activate();
+
+        handler.checkModifiers = function() { return true; };
+
+        var ilc = OpenLayers.Event.isLeftClick;
+        OpenLayers.Event.isLeftClick = function() { return true; };
+
+        // test
+
+        moveCnt = 0;
+
+        var xy1 = new OpenLayers.Pixel(1, 2);
+        handler.mousedown({xy: xy1});
+        t.ok(handler.last == xy1, "[mousedown] last is as expected");
+        var xy2 = new OpenLayers.Pixel(2, 3);
+        handler.mousemove({xy: xy2});
+        t.ok(handler.last == xy2, "[mousemove 1] last is as expected");
+        t.ok(handler.timeoutId != null, "[mousemove 1] timeoutId is set");
+        var xy3 = new OpenLayers.Pixel(3, 4);
+        handler.mousemove({xy: xy3});
+        t.ok(handler.last == xy2, "[mousemove 2] last is as expected");
+        var xy4 = new OpenLayers.Pixel(4, 5);
+        handler.mouseup({xy: xy4});
+
+        t.delay_call(3, function() {
+            // the timer should not cause a move
+            t.eq(moveCnt, 1, "move called once");
+            // tear down
+            OpenLayers.Event.isLeftClick = ilc;
+        });
+    }
+
+    function test_interval_timer_after_mousedown(t) {
+        t.plan(5);
+
+        // set up
+
+        var map = new OpenLayers.Map('map');
+
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+
+        var moveCnt;
+
+        var handler = new OpenLayers.Handler.Drag(control, {}, {
+            interval: 1,
+            move: function() {
+                moveCnt++;
+            }
+        });
+        handler.activate();
+
+        handler.checkModifiers = function() { return true; };
+
+        var ilc = OpenLayers.Event.isLeftClick;
+        OpenLayers.Event.isLeftClick = function() { return true; };
+
+        // test
+
+        moveCnt = 0;
+
+        var xy1 = new OpenLayers.Pixel(1, 2);
+        handler.mousedown({xy: xy1});
+        t.ok(handler.last == xy1, "[mousedown] last is as expected");
+        var xy2 = new OpenLayers.Pixel(2, 3);
+        handler.mousemove({xy: xy2});
+        t.ok(handler.last == xy2, "[mousemove 1] last is as expected");
+        t.ok(handler.timeoutId != null, "[mousemove 1] timeoutId is set");
+        var xy3 = new OpenLayers.Pixel(3, 4);
+        handler.mousemove({xy: xy3});
+        t.ok(handler.last == xy2, "[mousemove 2] last is as expected");
+        var xy4 = new OpenLayers.Pixel(4, 5);
+        handler.mouseup({xy: xy4});
+        var xy5 = new OpenLayers.Pixel(5, 6);
+        handler.mousedown({xy: xy4});
+
+        t.delay_call(3, function() {
+            // the timer should not cause a move
+            t.eq(moveCnt, 1, "move called once");
+            // tear down
+            OpenLayers.Event.isLeftClick = ilc;
+        });
+    }
+
+    function test_interval_timer_before_mouseup(t) {
+        t.plan(5);
+
+        // set up
+
+        var map = new OpenLayers.Map('map');
+
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+
+        var moveCnt;
+
+        var handler = new OpenLayers.Handler.Drag(control, {}, {
+            interval: 1,
+            move: function() {
+                moveCnt++;
+            }
+        });
+        handler.activate();
+
+        handler.checkModifiers = function() { return true; };
+
+        var ilc = OpenLayers.Event.isLeftClick;
+        OpenLayers.Event.isLeftClick = function() { return true; };
+
+        // test
+
+        moveCnt = 0;
+
+        var xy1 = new OpenLayers.Pixel(1, 2);
+        handler.mousedown({xy: xy1});
+        t.ok(handler.last == xy1, "[mousedown] last is as expected");
+        var xy2 = new OpenLayers.Pixel(2, 3);
+        handler.mousemove({xy: xy2});
+        t.ok(handler.last == xy2, "[mousemove 1] last is as expected");
+        t.ok(handler.timeoutId != null, "[mousemove 1] timeoutId is set");
+        var xy3 = new OpenLayers.Pixel(3, 4);
+        handler.mousemove({xy: xy3});
+        t.ok(handler.last == xy2, "[mousemove 2] last is as expected");
+
+        t.delay_call(3, function() {
+            // the timer should cause a move
+            t.eq(moveCnt, 2, "move called twice");
+            var xy4 = new OpenLayers.Pixel(4, 5);
+            handler.mouseup({xy: xy4});
+            // tear down
+            OpenLayers.Event.isLeftClick = ilc;
+        });
+    }
+
+
   </script>
 </head>
 <body>

Modified: sandbox/tschaub/canvas/tests/Handler/Path.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Path.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Handler/Path.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -67,7 +67,7 @@
              "activate adds the line feature to the layer");
         t.eq(log.length, 1,
              "activate calls \"create\" once");
-        t.geom_eq(log[0].geometry, handler.point.geometry,
+        t.ok(log[0].geometry == handler.point.geometry,
                   "\"create\" called with expected geometry");
         t.ok(log[0].feature == handler.line,
              "\"create\" called with expected feature");
@@ -78,6 +78,32 @@
         map.destroy();
     }
 
+    // See: http://trac.osgeo.org/openlayers/ticket/3179
+    function test_activate_before_map_is_centered(t) {
+        t.plan(1);
+        var map = new OpenLayers.Map('map', {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control();
+        var handler = new OpenLayers.Handler.Path(control, {});
+        control.handler = handler;
+        map.addControl(control);
+
+        var error;
+        try {
+            handler.activate();
+            error = false;
+        } catch(err) {
+            error = true;
+        }
+        t.ok(!error, "no error on activate");
+    }
+
     function test_bounds(t) {
         t.plan(2);
         var geometry;
@@ -145,6 +171,10 @@
             cancel: function() {
                 logs.push({type: "cancel", args: arguments});
             }
+        },
+        {
+            pixelTolerance: 0,
+            dblclickTolerance: 0
         });
         control.handler = handler;
         map.addControl(control);
@@ -155,8 +185,8 @@
         t.eq(logs.length, 1, "[activate] called back");
         log = logs.shift();
         t.eq(log.type, "create", "[activate] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[activate] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+                  "[activate] initial point");
         t.ok(log.args[1] == handler.line,
              "[activate] correct feature");
         // mouse move
@@ -256,8 +286,8 @@
         );
         log = logs.shift();
         t.eq(log.type, "create", "[dblclick] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[dblclick] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+                  "[dblclick] initial point");
         t.ok(log.args[1] == handler.line,
              "[dblclick] correct feature");
         // cancel
@@ -265,16 +295,13 @@
         t.eq(logs.length, 2, "[cancel] called back");
         log = logs.shift();
         t.eq(log.type, "cancel", "[cancel] canced called");
-        t.geom_eq(log.args[0],
-            new OpenLayers.Geometry.LineString([
-                new OpenLayers.Geometry.Point(-200, 125)
-            ]),
-            "[cancel] correct linestring"
+        t.ok(isNaN(log.args[0].components[0].x) && isNaN(log.args[0].components[0].y),
+            "[cancel] initial linestring"
         );
         log = logs.shift();
         t.eq(log.type, "create", "[cancel] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[cancel] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+                  "[cancel] initial point");
  
         map.destroy();
     }
@@ -642,7 +669,11 @@
         });
         map.addLayer(layer);
         var control = new OpenLayers.Control({});
-        var handler = new OpenLayers.Handler.Path(control, {});
+        var handler = new OpenLayers.Handler.Path(control, {},
+        {
+            pixelTolerance: 0,
+            dblclickTolerance: 0
+        });
         control.handler = handler;
         map.addControl(control);
         map.setCenter(new OpenLayers.LonLat(0, 0), 0);
@@ -741,6 +772,182 @@
             ]), "geometry is correct after mousemove");
     }
 
+ 
+    // a) tap
+    // b) tap
+    // c) doubletap
+    function test_touch_sequence1(t) {
+        t.plan(17);
+
+        // set up
+
+        var log;
+        var map = new OpenLayers.Map("map", {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control({});
+        var handler = new OpenLayers.Handler.Path(control, {
+            done: function(g, f) {
+                log = {type: 'done', geometry: g, feature: f};
+            },
+            modify: function(g, f) {
+                log = {type: 'modify', geometry: g, feature: f};
+            }
+        }, {
+            dblclickTolerance: 2
+        });
+        control.handler = handler;
+        map.addControl(control);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        handler.activate();
+
+        // test
+
+        var ret;
+
+        // tap on (1, 0)
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] feature not finalized or modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(1, 0)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] feature not finalized or modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.eq(log.type, 'modify', '[touchend] feature modified');
+        t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-149, 75),
+                  "[touchend] correct point");
+
+        // tap on (10, 10)
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] feature not finalized or modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(10, 10)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] feature not finalized or modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.eq(log.type, 'modify', '[touchend] feature modified');
+        t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-140, 65),
+                  "[touchend] correct point");
+
+        // tap on (11, 10) -> doubletap
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(11, 10)});
+        t.ok(!ret, '[touchstart] event does not propagate');
+        t.eq(log.type, 'done', '[touchend] feature finalized');
+        t.geom_eq(log.geometry,
+            new OpenLayers.Geometry.LineString([
+                new OpenLayers.Geometry.Point(-149, 75),  // (1, 0)
+                new OpenLayers.Geometry.Point(-140, 65)   // (10, 10)
+            ]), "[touchstart] final geometry is correct");
+
+        // tear down
+
+        map.destroy();
+    }
+
+    // a) tap
+    // b) tap-move
+    // c) tap
+    // d) doubletap
+    function test_touch_sequence2(t) {
+        t.plan(23);
+
+        // set up
+
+        var log;
+        var map = new OpenLayers.Map("map", {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control({});
+        var handler = new OpenLayers.Handler.Path(control, {
+            done: function(g, f) {
+                log = {type: 'done', geometry: g, feature: f};
+            },
+            modify: function(g, f) {
+                log = {type: 'modify', geometry: g, feature: f};
+            }
+        }, {
+            dblclickTolerance: 2
+        });
+        control.handler = handler;
+        map.addControl(control);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        handler.activate();
+
+        // test
+
+        var ret;
+
+        // tap on (1, 0)
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] feature not finalized or modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(1, 0)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] feature not finalized or modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.eq(log.type, 'modify', '[touchend] feature modified');
+        t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-149, 75),
+                  "[touchend] correct point");
+
+        // tap-move
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] feature not finalized or modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(20, 20)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] feature not finalized or modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.eq(log, null, '[touchend] feature not finalized or modified');
+
+        // tap on (10, 10)
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(9, 10)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] feature not finalized or modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(10, 10)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] feature not finalized or modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.eq(log.type, 'modify', '[touchend] feature modified');
+        t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-140, 65),
+                  "[touchend] correct point");
+
+        // tap on (11, 10) -> doubletap
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(11, 10)});
+        t.ok(!ret, '[touchstart] event does not propagate');
+        t.eq(log.type, 'done', '[touchend] feature finalized');
+        t.geom_eq(log.geometry,
+            new OpenLayers.Geometry.LineString([
+                new OpenLayers.Geometry.Point(-149, 75),  // (1, 0)
+                new OpenLayers.Geometry.Point(-140, 65)   // (10, 10)
+            ]), "[touchstart] final geometry is correct");
+
+        // tear down
+
+        map.destroy();
+    }
+
   </script>
 </head>
 <body>

Modified: sandbox/tschaub/canvas/tests/Handler/Point.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Point.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Handler/Point.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -25,7 +25,7 @@
     }
 
     function test_Handler_Point_activation(t) {
-        t.plan(10);
+        t.plan(11);
         var log = [];
         var map = new OpenLayers.Map("map", {
             resolutions: [1]
@@ -63,19 +63,53 @@
              "activate adds the feature to the layer");
         t.eq(log.length, 1,
              "activate calls \"create\" once");
-        t.geom_eq(log[0].geometry, handler.point.geometry,
+        t.ok(log[0].geometry == handler.point.geometry,
                   "\"create\" called with expected geometry");
         t.ok(log[0].feature == handler.point,
              "\"create\" called with expected feature");
         activated = handler.deactivate();
         t.ok(activated,
              "deactivate returns true if the handler was active already");
-
+        var failed = false;
+        try {
+            handler.finalize();
+            msg = "finalizing after deactivation does not throw an error";
+        } catch (err) {
+            failed = true;
+            msg = "finalizing after deactivation throws an error";
+        }
+        t.ok(!failed, msg);
         map.destroy();
     }
 
+    // http://trac.osgeo.org/openlayers/ticket/3179
+    function test_activate_before_map_is_centered(t) {
+        t.plan(1);
+        var map = new OpenLayers.Map('map', {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control();
+        var handler = new OpenLayers.Handler.Point(control, {});
+        control.handler = handler;
+        map.addControl(control);
+
+        var error;
+        try {
+            handler.activate();
+            error = false;
+        } catch(err) {
+            error = true;
+        }
+        t.ok(!error, "no error on activate");
+    }
+
     function test_Handler_Point_events(t) {
-        t.plan(34);
+        t.plan(49);
         var log = [];
         var map = new OpenLayers.Map("map", {
             resolutions: [1]
@@ -97,7 +131,7 @@
 
         // list below events that should be handled (events) and those
         // that should not be handled (nonevents) by the handler
-        var events = ["click", "dblclick", "mousedown", "mouseup", "mousemove", "mouseout"];
+        var events = ["click", "dblclick", "mousedown", "mouseup", "mousemove", "mouseout", "touchstart", "touchmove", "touchend"];
         var nonevents = ["resize", "focus", "blur"];
         map.events.registerPriority = function(type, obj, func) {
             var r = func();
@@ -166,6 +200,10 @@
             cancel: function() {
                 logs.push({type: "cancel", args: arguments});
             }
+        },
+        {
+            pixelTolerance: 0,
+            dblclickTolerance: 0
         });
         control.handler = handler;
         map.addControl(control);
@@ -176,8 +214,8 @@
         t.eq(logs.length, 1, "[activate] called back");
         log = logs.shift();
         t.eq(log.type, "create", "[activate] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[activate] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+             "[activate] initial point");
         // mouse down
         handler.mousedown(
             {type: "mousedown", xy: new OpenLayers.Pixel(0, 0)});
@@ -227,8 +265,8 @@
                   "[mouseup] correct point");
         log = logs.shift();
         t.eq(log.type, "create", "[mouseup] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[activate] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+             "[mouseup] initial point");
         // mouse up on same pixel
         handler.mouseup({type: "mouseup", xy: new OpenLayers.Pixel(2, 0)});
         t.eq(logs.length, 0, "[mouseup] not called back");
@@ -237,12 +275,12 @@
         t.eq(logs.length, 2, "[cancel] called back");
         log = logs.shift();
         t.eq(log.type, "cancel", "[cancel] canced called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[cancel] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+             "[cancel] initial point");
         log = logs.shift();
         t.eq(log.type, "create", "[cancel] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[cancel] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+             "[] initial point");
 
         map.destroy();
     }
@@ -378,9 +416,121 @@
         t.eq(handler.point, null,
              "handler.point is null after destroy");
     }
-    
 
+    //
+    // Sequence tests
+    // 
+    // Sequence tests basically involve executing a sequence of events
+    // and testing the resulting geometry.
+    //
+    // Below are tests for various drawing sequences. Tests can be
+    // added here each a non-working sequence is found.
+    //
 
+    // tap
+    function test_touch_sequence1(t) {
+        t.plan(8);
+
+        // set up
+
+        var log;
+        var map = new OpenLayers.Map("map", {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control({});
+        var handler = new OpenLayers.Handler.Point(control, {
+            done: function(g, f) {
+                log = {geometry: g, feature: f};
+            }
+        });
+        control.handler = handler;
+        map.addControl(control);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        handler.activate();
+
+        // test
+
+        var ret;
+
+        // tap on (1, 0)
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] no finalization');
+        t.ok(isNaN(handler.point.geometry.x) && isNaN(handler.point.geometry.y),
+             '[touchstart] feature not modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(1, 0)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] no finalization');
+        t.ok(isNaN(handler.point.geometry.x) && isNaN(handler.point.geometry.y),
+             '[touchmove] feature not modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.geom_eq(log.geometry, new OpenLayers.Geometry.Point(-149, 75),
+                  "[touchend] correct point");
+        // tear down
+
+        map.destroy();
+    }
+
+    // tap-move
+    function test_touch_sequence2(t) {
+        t.plan(9);
+
+        // set up
+
+        var log;
+        var map = new OpenLayers.Map("map", {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control({});
+        var handler = new OpenLayers.Handler.Point(control, {
+            done: function(g, f) {
+                log = {geometry: g, feature: f};
+            }
+        });
+        control.handler = handler;
+        map.addControl(control);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        handler.activate();
+
+        // test
+
+        var ret;
+
+        // tap-move (0, 0) -> (9, 0)
+        log = null;
+        ret = handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+        t.ok(ret, '[touchstart] event propagates');
+        t.eq(log, null, '[touchstart] no finalization');
+        t.ok(isNaN(handler.point.geometry.x) && isNaN(handler.point.geometry.y),
+             '[touchstart] feature not modified');
+        ret = handler.touchmove({xy: new OpenLayers.Pixel(9, 0)});
+        t.ok(ret, '[touchmove] event propagates');
+        t.eq(log, null, '[touchmove] no finalization');
+        t.ok(isNaN(handler.point.geometry.x) && isNaN(handler.point.geometry.y),
+             '[touchmove] feature not modified');
+        ret = handler.touchend({});
+        t.ok(ret, '[touchend] event propagates');
+        t.eq(log, null, '[touchend] no finalization');
+        t.ok(isNaN(handler.point.geometry.x) && isNaN(handler.point.geometry.y),
+             '[touchend] feature not modified');
+
+        // tear down
+
+        map.destroy();
+    }
+
   </script>
 </head>
 <body>

Modified: sandbox/tschaub/canvas/tests/Handler/Polygon.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Polygon.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Handler/Polygon.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -69,7 +69,7 @@
              "activate adds the polygin feature to the layer");
         t.eq(log.length, 1,
              "activate calls \"create\" once");
-        t.geom_eq(log[0].geometry, handler.point.geometry,
+        t.ok(log[0].geometry == handler.point.geometry,
                   "\"create\" called with expected geometry");
         t.ok(log[0].feature == handler.polygon,
              "\"create\" called with expected feature");
@@ -80,6 +80,32 @@
         map.destroy();
     }
 
+    // See: http://trac.osgeo.org/openlayers/ticket/3179
+    function test_activate_before_map_is_centered(t) {
+        t.plan(1);
+        var map = new OpenLayers.Map('map', {
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-10, -10, 10, 10),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control();
+        var handler = new OpenLayers.Handler.Polygon(control, {});
+        control.handler = handler;
+        map.addControl(control);
+
+        var error;
+        try {
+            handler.activate();
+            error = false;
+        } catch(err) {
+            error = true;
+        }
+        t.ok(!error, "no error on activate");
+    }
+
     function test_bounds_stopDown_true(t) {
         t.plan(2);
         var map = new OpenLayers.Map('map');
@@ -142,6 +168,10 @@
             cancel: function() {
                 logs.push({type: "cancel", args: arguments});
             }
+        },
+        {
+            pixelTolerance: 0,
+            dblclickTolerance: 0
         });
         control.handler = handler;
         map.addControl(control);
@@ -153,8 +183,8 @@
         t.eq(logs.length, 1, "[activate] called back");
         log = logs.shift();
         t.eq(log.type, "create", "[activate] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[activate] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+                  "[activate] initial point");
         t.ok(log.args[1] == handler.polygon,
              "[activate] correct feature");
         handler.mousemove(
@@ -275,8 +305,8 @@
         );
         log = logs.shift();
         t.eq(log.type, "create", "[dblclick] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[dblclick] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+                  "[dblclick] initial point");
         t.ok(log.args[1] == handler.polygon,
              "[dblclick] correct feature");
         // cancel
@@ -286,8 +316,8 @@
         t.eq(log.type, "cancel", "[cancel] canced called");
         log = logs.shift();
         t.eq(log.type, "create", "[cancel] create called");
-        t.geom_eq(log.args[0], new OpenLayers.Geometry.Point(-200, 125),
-                  "[cancel] correct point");
+        t.ok(isNaN(log.args[0].x) && isNaN(log.args[0].y),
+                  "[cancel] initial point");
 
         map.destroy();
     }        
@@ -531,7 +561,11 @@
         var draw = new OpenLayers.Control.DrawFeature(
             map.layers[0],
             OpenLayers.Handler.Polygon,
-            {handlerOptions: {holeModifier: "altKey"}}
+            {handlerOptions: {
+                holeModifier: "altKey",
+                pixelTolerance: 0,
+                dblclickTolerance: 0
+            }}
         );
         map.addControl(draw);
         draw.activate();
@@ -745,7 +779,8 @@
         var control = new OpenLayers.Control({});
         var handler = new OpenLayers.Handler.Polygon(control,
             {done: function(g) { log.geometry = g; }},
-            {stopDown: true, stopUp: true}
+            {stopDown: true, stopUp: true,
+            pixelTolerance: 0, dblclickTolerance: 0}
         );
         control.handler = handler;
         map.addControl(control);
@@ -816,7 +851,8 @@
         var control = new OpenLayers.Control({});
         var handler = new OpenLayers.Handler.Polygon(control,
             {done: function(g) { log.geometry = g; }},
-            {stopDown: false, stopUp: false}
+            {stopDown: false, stopUp: false,
+            pixelTolerance: 0, dblclickTolerance: 0}
         );
         control.handler = handler;
         map.addControl(control);
@@ -868,6 +904,105 @@
             ]), "geometry is correct");
     }
 
+
+    function test_sequence_touch_1(t) {
+        t.plan(19);
+        
+        log = [];
+        var map = new OpenLayers.Map("map", { // 300 x 150
+            resolutions: [1]
+        });
+        var layer = new OpenLayers.Layer.Vector("foo", {
+            maxExtent: new OpenLayers.Bounds(-100, -100, 100, 100),
+            isBaseLayer: true
+        });
+        map.addLayer(layer);
+        var control = new OpenLayers.Control({});
+        var handler = new OpenLayers.Handler.Polygon(control, {
+            "done": function(g, f) {
+                log.push({geometry: g, feature: f});
+            }
+        });
+        control.handler = handler;
+        control.layer = layer;
+        map.addControl(control);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+        handler.activate();
+        
+        handler.touchstart({type: "touchstart", xy: new OpenLayers.Pixel(49, 75)});
+        t.eq(log.length, 0, "touch start 1");
+        var expectedRing = new OpenLayers.Geometry.LinearRing([
+            new OpenLayers.Geometry.Point(-100, 0),
+            new OpenLayers.Geometry.Point(-100, 0)
+        ]);
+
+        handler.touchmove({type: "touchmove", xy: new OpenLayers.Pixel(50, 75)});
+        t.eq(log.length, 0, "touch move");
+        
+        handler.touchend({type: "touchend"});
+        t.eq(log.length, 0, "touch end");
+        expectedRing.addComponent(new OpenLayers.Geometry.Point(-100,0), 1);
+
+        t.geom_eq(handler.polygon.geometry.components[0], expectedRing, "geometry is correct");
+
+        handler.touchstart({type: "touchstart", xy: new OpenLayers.Pixel(100, 75)});
+        t.eq(log.length, 0, "touch start 2");
+        var expectedRing = new OpenLayers.Geometry.LinearRing([
+            new OpenLayers.Geometry.Point(-100, 0),
+            new OpenLayers.Geometry.Point(-100, 0)
+        ]);
+
+        handler.touchmove({type: "touchmove", xy: new OpenLayers.Pixel(250, 75)});
+        t.eq(log.length, 0, "touch move");
+
+        handler.touchend({type: "touchend"});
+        t.eq(log.length, 0, "touch end");
+        expectedRing.addComponent(new OpenLayers.Geometry.Point(-100,0), 1);
+        
+        t.geom_eq(handler.polygon.geometry.components[0], expectedRing, "geometry is correct");
+            
+        handler.touchstart({type: "touchstart", xy: new OpenLayers.Pixel(100, 75)});
+        t.eq(log.length, 0, "touch start 3");
+
+        handler.touchmove({type: "touchmove", xy: new OpenLayers.Pixel(100, 75)});
+        t.eq(log.length, 0, "touch move");
+
+        handler.touchend({type: "touchend"});
+        t.eq(log.length, 0, "touch end");
+        t.geom_eq(handler.polygon.geometry,
+            new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([
+                new OpenLayers.Geometry.Point(-100, 0),
+                new OpenLayers.Geometry.Point(-50, 0),
+                new OpenLayers.Geometry.Point(-50, 0),
+                new OpenLayers.Geometry.Point(-100, 0)
+            ])]), "geometry is correct");
+            
+        handler.touchstart({type: "touchstart", xy: new OpenLayers.Pixel(252, 100)});
+        t.eq(log.length, 0, "touch start 4");
+
+        handler.touchmove({type: "touchmove", xy: new OpenLayers.Pixel(252, 100)});
+        t.eq(log.length, 0, "touch move");
+
+        handler.touchend({type: "touchend"});
+        t.eq(log.length, 0, "touch end");
+            
+        handler.touchstart({type: "touchstart", xy: new OpenLayers.Pixel(250, 100)});
+        t.eq(log.length, 1, "touch start");
+
+        handler.touchmove({type: "touchmove", xy: new OpenLayers.Pixel(250, 100)});
+        t.eq(log.length, 1, "touch move");
+
+        handler.touchend({type: "touchend"});
+        t.eq(log.length, 1, "touch end");
+        t.geom_eq(log[0].geometry,
+            new OpenLayers.Geometry.Polygon([new OpenLayers.Geometry.LinearRing([
+                new OpenLayers.Geometry.Point(-100, 0),
+                new OpenLayers.Geometry.Point(-50, 0),
+                new OpenLayers.Geometry.Point(102, -25),
+                new OpenLayers.Geometry.Point(-100, 0)
+            ])]), "geometry is correct");
+    }
+
   </script>
 </head>
 <body>

Copied: sandbox/tschaub/canvas/tests/Layer/ArcGISCache.html (from rev 11762, trunk/openlayers/tests/Layer/ArcGISCache.html)
===================================================================
--- sandbox/tschaub/canvas/tests/Layer/ArcGISCache.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/Layer/ArcGISCache.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,227 @@
+<html>
+<head>
+  <script src="../../lib/OpenLayers.js"></script>
+  <script src="../../lib/OpenLayers/Layer/ArcGISCache.js" type="text/javascript"></script>
+  <script src="ArcGISCache.json" type="text/javascript"></script>
+  <script type="text/javascript">
+    var isMozilla = (navigator.userAgent.indexOf("compatible") == -1);
+    var layer; 
+
+    var name = 'Test Layer';
+    var url = "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer";
+    var options = { }; 
+
+    function test_Layer_ARCGISCACHE_constructor (t) {
+        t.plan( 1 );
+                       
+        var layer = new OpenLayers.Layer.ArcGISCache(name, url, options);
+        t.ok( layer instanceof OpenLayers.Layer.ArcGISCache, "returns OpenLayers.Layer.ArcGISCache object" );
+    }
+    
+    function test_Layer_ARCGISCACHE_autoConfigure (t) {
+        t.plan( 5 );
+        var layerInfo = capabilitiesObject;
+        
+        //initialize the layer using the JSON object from an arcgis server
+        //SEE: ArcGISCache.json
+        var layer = new OpenLayers.Layer.ArcGISCache(name, url, {
+            layerInfo: layerInfo
+        });
+        t.ok( layer instanceof OpenLayers.Layer.ArcGISCache, "returns OpenLayers.Layer.ArcGISCache object" );        
+        t.ok( layer.projection = 'EPSG:' + layerInfo.spatialReference.wkid, "projection is set correctly");
+        t.ok( layer.units = 'm', "map units are set correctly");
+        t.ok( layer.resolutions && layer.resolutions.length == 20, "resolutions are initialized from LOD objects properly");
+        
+        if (layerInfo.tileInfo) {
+            if (layerInfo.tileInfo.width && layerInfo.tileInfo.height) {
+                var tileSize = new OpenLayers.Size(layerInfo.tileInfo.width, layerInfo.tileInfo.height);
+                t.ok((layer.tileSize.width == tileSize.width) && (layer.tileSize.height == tileSize.height), "tile size is set properly");
+            }
+            else {
+                var tileSize = new OpenLayers.Size(layerInfo.tileInfo.cols, layerInfo.tileInfo.rows);
+                t.ok((layer.tileSize.width == tileSize.width) && (layer.tileSize.height == tileSize.height), "tile size is set properly");
+            }
+        }        
+    }
+    
+    /**
+     * lets make sure we're getting the correct urls back with a basic auto-configure setup 
+     */
+    function test_Layer_ARCGISCACHE_autoConfigure_URLS(t) {
+        var layerInfo = capabilitiesObject;
+        
+        //initialize the layer using the JSON object from an arcgis server
+        //SEE: ArcGISCache.json
+        var layer = new OpenLayers.Layer.ArcGISCache(name, url, {
+            layerInfo: layerInfo
+        });
+        var map = new OpenLayers.Map('map', { 
+            maxExtent: layer.maxExtent,
+            units: layer.units,
+            resolutions: layer.resolutions,
+            numZoomLevels: layer.numZoomLevels,
+            tileSize: layer.tileSize,
+            projection: layer.displayProjection,
+            StartBounds: layer.initialExtent    
+        });
+        map.addLayers([layer]);
+    
+        //this set represents a few edge cases, and some more specific cases, it is by no means exhaustive,
+        var urlSets = [
+            { 
+                bounds: new OpenLayers.Bounds(-36787612.973083,-22463925.368666, 43362420.398053,17611091.316902),
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/0/0/0" 
+            },            
+            { 
+                bounds: new OpenLayers.Bounds(-31793889.951914,4589319.785415, 8281126.733654,24626828.128199),
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/1/0/0"
+            },            
+            { 
+                bounds: new OpenLayers.Bounds(-24639873.181971,12676071.933457, -4602364.839187,22694826.104849),
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/2/0/0" 
+            },
+            { 
+                bounds: new OpenLayers.Bounds(-15521241.455665,11580270.695961, 4516266.887119,21599024.867353),
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/2/0/1" 
+            },                    
+            { 
+                bounds: new OpenLayers.Bounds(-9265879.5435993,2870892.9335638, -8639707.4078873,3183979.0014198) ,
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/7/54/35" 
+            },
+            { 
+                bounds: new OpenLayers.Bounds(-10741909.131798,4684560.1640365, -10585366.09787,4762831.6810005),
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/9/195/119" 
+            },
+            { 
+                bounds: new OpenLayers.Bounds(-13668958.106938,4456961.2611504, -13512415.07301,4535232.7781144),
+                url: "http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/9/198/82" 
+            }
+        ];
+        
+        t.plan( urlSets.length );        
+        for(var i=0;i<urlSets.length;i++) 
+        {
+            var o = urlSets[i];            
+            map.zoomToExtent(o.bounds, true);
+            
+            var resultUrl = layer.getURL(o.bounds);            
+            t.ok( resultUrl == o.url, "correct tile returned for " + o.bounds);        
+        }
+    }    
+    
+    /**
+     * Test the formatting for the 'direct' urls, especially when not auto-configuring the layer
+     */
+    function test_Layer_ARCGISCACHE_direct(t) {
+        var roadsUrl = 'http://serverx.esri.com/arcgiscache/DG_County_roads_yesA_backgroundDark/Layers/_alllayers';
+        var urlSets = [
+            { 
+                bounds: new OpenLayers.Bounds(289244.67443386,4317153.7421985, 306178.04163392,4325620.4257985),
+                url: roadsUrl + "/L00/R0000029e/C0000027f.png" 
+            },            
+            { 
+                bounds: new OpenLayers.Bounds(308658.51534463,4303230.0164352, 325591.88254469,4311696.7000352),
+                url: roadsUrl + "/L00/R000002a0/C00000282.png"
+            },            
+            { 
+                bounds: new OpenLayers.Bounds(311136.39626998,4318933.8711555, 311678.26402038,4319204.8050307) ,
+                url: roadsUrl + "/L05/R000051e0/C00004e52.png" 
+            }
+        ];
+        t.plan( urlSets.length );
+    
+    
+        //perform the exact setup from the arcgiscache_direct example
+            
+        // First 4 variables extracted from conf.xml file        
+        // Tile layers & map MUST have same projection 
+        var proj='EPSG:26915';
+            
+        // Layer can also accept serverResolutions array
+        // to deal with situation in which layer resolution array & map resolution
+        // array are out of sync
+        var mapResolutions = [33.0729828126323,16.9333672000677,8.46668360003387,4.23334180001693,2.11667090000847,1.05833545000423];
+
+        // For this example this next line is not really needed, 256x256 is default.
+        // However, you would need to change this if your layer had different tile sizes 
+        var tileSize = new OpenLayers.Size(256,256);
+        
+        // Tile Origin is required unless it is the same as the implicit map origin
+        // which can be effected by several variables including maxExtent for map or base layer 
+        var agsTileOrigin = new OpenLayers.LonLat(-5120900,9998100);
+        
+        // This can really be any valid bounds that the map would reasonably be within 
+        var mapExtent = new OpenLayers.Bounds(289310.8204,4300021.937,314710.8712,4325421.988);
+        
+
+        var map = new OpenLayers.Map('map', {
+            maxExtent:mapExtent,
+            controls: [
+                new OpenLayers.Control.Navigation(),
+                new OpenLayers.Control.LayerSwitcher(), 
+                new OpenLayers.Control.PanZoomBar(),
+                new OpenLayers.Control.MousePosition()]
+        });
+
+        var layer = new OpenLayers.Layer.ArcGISCache('Roads', roadsUrl, {
+            tileOrigin: agsTileOrigin,
+            resolutions: mapResolutions,
+            sphericalMercator: true,
+            maxExtent: mapExtent,
+            useArcGISServer: false,
+            isBaseLayer: true,
+            projection: proj
+        });
+        
+        map.addLayers([layer]);
+        map.zoomToExtent(new OpenLayers.Bounds(-8341644, 4711236, -8339198, 4712459));
+
+        for(var i=0;i<urlSets.length;i++) 
+        {
+            var o = urlSets[i];            
+            map.zoomToExtent(o.bounds, true);
+            var resultUrl = layer.getURL(o.bounds);
+            t.ok( resultUrl == o.url, "correct tile returned for " + o.bounds);        
+        }
+    }
+        
+    /**
+     * Check our utility function for generating tile indexes against a file cache
+     */
+    function test_Layer_ARCGISCACHE_zeroPad(t) {
+        t.plan(4);
+    
+        var layer = new OpenLayers.Layer.ArcGISCache('test', null, { });
+
+        //some tile examples
+        t.ok('00000001' == layer.zeroPad(1, 8, 16), 'zeroPad should generate tile indexes properly ');
+        t.ok('00000020' == layer.zeroPad(32, 8, 16), 'zeroPad should generate tile indexes properly ');
+        t.ok('00000100' == layer.zeroPad(256, 8, 16), 'zeroPad should generate tile indexes properly ');
+        t.ok('00001000' == layer.zeroPad(4096, 8, 16), 'zeroPad should generate tile indexes properly ');        
+    }
+    
+    /**
+     * Check to ensure our LOD calculation will correctly avoid returning tile indexes less than zero
+     * (see http://trac.osgeo.org/openlayers/ticket/3169)
+     */
+    function test_Layer_ARCGISCACHE_tileBounds(t) {
+        t.plan(1);
+    
+        var layer = new OpenLayers.Layer.ArcGISCache('test', null, { });
+        var res = 264.583862501058;
+        layer.tileOrigin = new OpenLayers.LonLat(0.0, 650000.0);
+        layer.tileSize = new OpenLayers.Size(512, 512);
+        
+        // pick a point off the left of our tile origin  (would be a negative tile index)
+        var point = new OpenLayers.Geometry.Point(-123308.94829, 393128.85817);
+        
+        var tile = layer.getContainingTileCoords(point, res);
+        t.ok((tile.x >= 0 && tile.y >= 0), 'layer should not generate negative tile ranges for level of detail');
+    }
+
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;"></div>
+</body>
+</html>

Copied: sandbox/tschaub/canvas/tests/Layer/ArcGISCache.json (from rev 11762, trunk/openlayers/tests/Layer/ArcGISCache.json)
===================================================================
--- sandbox/tschaub/canvas/tests/Layer/ArcGISCache.json	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/Layer/ArcGISCache.json	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,334 @@
+var capabilitiesObject = {
+  "currentVersion" : 10.01, 
+  "serviceDescription" : "This map is designed to be used as a base map by GIS professionals and as a reference map by anyone.  The base map includes administrative boundaries, cities, water features, physiographic features, parks, landmarks, highways, roads, railways, airports, and buildings overlaid on land cover and shaded relief imagery for added context. The map was compiled from a variety of best available sources from several data providers, including the U.S. Geological Survey, Food and Agriculture Organization of the United Nations, National Park Service, Tele Atlas, AND, and ESRI. The base map currently provides coverage for the world down to a scale of ~1:1m and coverage for the continental United States and Hawaii to a scale of ~1:20k.  The base map also includes detailed maps for selected cities in the United States including Portland, Oregon and Philadephia, Pennsylvania. The base map was designed and developed by ESRI based on the topographic map templates tha
 t are available through the ArcGIS Resource Centers. For more information on this map, visit us \u003ca href=\"http://goto.arcgisonline.com/maps/World_Topo_Map \" target=\"_new\"\u003eonline\u003c/a\u003e.", 
+  "mapName" : "Layers", 
+  "description" : "This map is designed to be used as a base map by GIS professionals and as a reference map by anyone.  The base map includes administrative boundaries, cities, water features, physiographic features, parks, landmarks, highways, roads, railways, airports, and buildings overlaid on land cover and shaded relief imagery for added context. The map was compiled from a variety of best available sources from several data providers, including the U.S. Geological Survey, Food and Agriculture Organization of the United Nations, National Park Service, Tele Atlas, AND, and ESRI. The base map currently provides coverage for the world down to a scale of ~1:1m and coverage for the continental United States and Hawaii to a scale of ~1:20k.  The base map also includes detailed maps for selected cities in the United States including Portland, Oregon and Philadephia, Pennsylvania. The base map was designed and developed by ESRI based on the topographic map templates that are a
 vailable through the ArcGIS Resource Centers. For more information on this map, visit us online at http://goto.arcgisonline.com/maps/World_Topo_Map", 
+  "copyrightText" : "Sources: USGS, FAO, NPS, EPA, ESRI, DeLorme, TANA, other suppliers", 
+  "layers" : [
+    {
+      "id" : 0, 
+      "name" : "Topographic Info", 
+      "parentLayerId" : -1, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : [1, 2, 3, 4], 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 1, 
+      "name" : "Elevation (m)", 
+      "parentLayerId" : 0, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 2, 
+      "name" : "Elevation (ft)", 
+      "parentLayerId" : 0, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 3, 
+      "name" : "Slope", 
+      "parentLayerId" : 0, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 4, 
+      "name" : "Aspect", 
+      "parentLayerId" : 0, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 5, 
+      "name" : "Places Info", 
+      "parentLayerId" : -1, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : [6, 7, 8, 9], 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 6, 
+      "name" : "Place Names (Country Level)", 
+      "parentLayerId" : 5, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 80000000
+    }, 
+    {
+      "id" : 7, 
+      "name" : "Place Names (State Level)", 
+      "parentLayerId" : 5, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 80000001, 
+      "maxScale" : 1500000
+    }, 
+    {
+      "id" : 8, 
+      "name" : "Place Names (County Level)", 
+      "parentLayerId" : 5, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 1500001, 
+      "maxScale" : 400000
+    }, 
+    {
+      "id" : 9, 
+      "name" : "Place Names (City Level)", 
+      "parentLayerId" : 5, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 399999, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 10, 
+      "name" : "Scale Descriptions", 
+      "parentLayerId" : -1, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }, 
+    {
+      "id" : 11, 
+      "name" : "Level 15  ~1:18K", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 25000, 
+      "maxScale" : 15001
+    }, 
+    {
+      "id" : 12, 
+      "name" : "Level 14  ~1:36K", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 50000, 
+      "maxScale" : 25001
+    }, 
+    {
+      "id" : 13, 
+      "name" : "Level 13  ~1:72K", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 100000, 
+      "maxScale" : 50001
+    }, 
+    {
+      "id" : 14, 
+      "name" : "Level 12  ~1:144K", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 288000, 
+      "maxScale" : 100000
+    }, 
+    {
+      "id" : 15, 
+      "name" : "Level 11  ~1:288K", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 575000, 
+      "maxScale" : 288000
+    }, 
+    {
+      "id" : 16, 
+      "name" : "Level 10  ~1:577K", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 1150000, 
+      "maxScale" : 575000
+    }, 
+    {
+      "id" : 17, 
+      "name" : "Level 9    ~1:1.15M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 2200000, 
+      "maxScale" : 1150000
+    }, 
+    {
+      "id" : 18, 
+      "name" : "Level 8    ~1:2.3M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 4500000, 
+      "maxScale" : 2200000
+    }, 
+    {
+      "id" : 19, 
+      "name" : "Level 7    ~1:4.5M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 9000000, 
+      "maxScale" : 4500000
+    }, 
+    {
+      "id" : 20, 
+      "name" : "Level 6    ~1:9.2M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 18000000, 
+      "maxScale" : 9000000
+    }, 
+    {
+      "id" : 21, 
+      "name" : "Level 5    ~1:18M ", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 36000000, 
+      "maxScale" : 18000000
+    }, 
+    {
+      "id" : 22, 
+      "name" : "Level 4    ~1:36M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 72000000, 
+      "maxScale" : 36000000
+    }, 
+    {
+      "id" : 23, 
+      "name" : "Level 3    ~1:72M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 75500000, 
+      "maxScale" : 70000000
+    }, 
+    {
+      "id" : 24, 
+      "name" : "Level 2    ~1:147M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 290000000, 
+      "maxScale" : 147000000
+    }, 
+    {
+      "id" : 25, 
+      "name" : "Level 1    ~1:292M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 295000000, 
+      "maxScale" : 150000000
+    }, 
+    {
+      "id" : 26, 
+      "name" : "Level 0     ~1:584M", 
+      "parentLayerId" : 10, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 295000000
+    }, 
+    {
+      "id" : 27, 
+      "name" : "Citations", 
+      "parentLayerId" : -1, 
+      "defaultVisibility" : true, 
+      "subLayerIds" : null, 
+      "minScale" : 0, 
+      "maxScale" : 0
+    }
+  ], 
+  "tables" : [
+    
+  ], 
+  "spatialReference" : {
+    "wkid" : 102100
+  }, 
+  "singleFusedMapCache" : true, 
+  "tileInfo" : {
+    "rows" : 256, 
+    "cols" : 256, 
+    "dpi" : 96, 
+    "format" : "JPEG", 
+    "compressionQuality" : 90, 
+    "origin" : {
+      "x" : -20037508.342787, 
+      "y" : 20037508.342787
+    }, 
+    "spatialReference" : {
+      "wkid" : 102100
+    }, 
+    "lods" : [
+      {"level" : 0, "resolution" : 156543.033928, "scale" : 591657527.591555}, 
+      {"level" : 1, "resolution" : 78271.5169639999, "scale" : 295828763.795777}, 
+      {"level" : 2, "resolution" : 39135.7584820001, "scale" : 147914381.897889}, 
+      {"level" : 3, "resolution" : 19567.8792409999, "scale" : 73957190.948944}, 
+      {"level" : 4, "resolution" : 9783.93962049996, "scale" : 36978595.474472}, 
+      {"level" : 5, "resolution" : 4891.96981024998, "scale" : 18489297.737236}, 
+      {"level" : 6, "resolution" : 2445.98490512499, "scale" : 9244648.868618}, 
+      {"level" : 7, "resolution" : 1222.99245256249, "scale" : 4622324.434309}, 
+      {"level" : 8, "resolution" : 611.49622628138, "scale" : 2311162.217155}, 
+      {"level" : 9, "resolution" : 305.748113140558, "scale" : 1155581.108577}, 
+      {"level" : 10, "resolution" : 152.874056570411, "scale" : 577790.554289}, 
+      {"level" : 11, "resolution" : 76.4370282850732, "scale" : 288895.277144}, 
+      {"level" : 12, "resolution" : 38.2185141425366, "scale" : 144447.638572}, 
+      {"level" : 13, "resolution" : 19.1092570712683, "scale" : 72223.819286}, 
+      {"level" : 14, "resolution" : 9.55462853563415, "scale" : 36111.909643}, 
+      {"level" : 15, "resolution" : 4.77731426794937, "scale" : 18055.954822}, 
+      {"level" : 16, "resolution" : 2.38865713397468, "scale" : 9027.977411}, 
+      {"level" : 17, "resolution" : 1.19432856685505, "scale" : 4513.988705}, 
+      {"level" : 18, "resolution" : 0.597164283559817, "scale" : 2256.994353}, 
+      {"level" : 19, "resolution" : 0.298582141647617, "scale" : 1128.497176}
+    ]
+  }, 
+  "initialExtent" : {
+    "xmin" : -45223792.233066, 
+    "ymin" : -22882589.2065154, 
+    "xmax" : 45223792.233066, 
+    "ymax" : 22882589.2065155, 
+    "spatialReference" : {
+      "wkid" : 102100
+    }
+  }, 
+  "fullExtent" : {
+    "xmin" : -20037507.0671618, 
+    "ymin" : -19971868.8804086, 
+    "xmax" : 20037507.0671618, 
+    "ymax" : 19971868.8804086, 
+    "spatialReference" : {
+      "wkid" : 102100
+    }
+  }, 
+  "units" : "esriMeters", 
+  "supportedImageFormatTypes" : "PNG24,PNG,JPG,DIB,TIFF,EMF,PS,PDF,GIF,SVG,SVGZ,AI,BMP", 
+  "documentInfo" : {
+    "Title" : "World Topo Map", 
+    "Author" : "ESRI", 
+    "Comments" : "", 
+    "Subject" : "", 
+    "Category" : "", 
+    "Keywords" : "", 
+    "Credits" : ""
+  }, 
+  "capabilities" : "Map,Query,Data"
+};
\ No newline at end of file

Modified: sandbox/tschaub/canvas/tests/Layer/GML.html
===================================================================
--- sandbox/tschaub/canvas/tests/Layer/GML.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Layer/GML.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -37,8 +37,8 @@
         var map = new OpenLayers.Map("map");
         map.addLayer(layer);
         map.zoomToMaxExtent();
-        t.delay_call(1, function() { 
-            t.ok(true, "waited for 1s"); 
+        t.delay_call(3, function() { 
+            t.ok(true, "waited for 3s"); 
         });
 
     }

Modified: sandbox/tschaub/canvas/tests/Layer/Vector.html
===================================================================
--- sandbox/tschaub/canvas/tests/Layer/Vector.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Layer/Vector.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -458,9 +458,7 @@
         t.plan(9);
         
         var map = new OpenLayers.Map("map");
-        var layer = new OpenLayers.Layer.Vector(null, {
-            drawn: true
-        });
+        var layer = new OpenLayers.Layer.Vector();
         map.addLayer(layer);
         var feature = new OpenLayers.Feature.Vector(
             new OpenLayers.Geometry.Point(10, 10)
@@ -477,6 +475,7 @@
         };
         
         // draw feature with no state
+        layer.drawn = true;
         layer.drawFeature(feature);
         t.ok(log.feature === feature, "[no state] drawFeature called with correct feature");
         t.ok(log.style.display !== "none", "[no state] drawFeature called with style display not none");
@@ -707,6 +706,54 @@
                         (-y + customStyle6.graphicYOffset).toFixed().toString(),
                         "graphicYOffset correctly set");
         }
+        if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.SVG2') {
+                feature.style = customStyle1;
+                layer.drawFeature(feature);
+                var resolution = map.getResolution();
+                t.eq(root.firstChild.getAttributeNS(null, 'width'),
+                             (2*customStyle1.pointRadius*resolution).toString(),
+                             "given a pointRadius, width equals 2*pointRadius");
+                t.eq(root.firstChild.getAttributeNS(null, 'height'),
+                             (2*customStyle1.pointRadius*resolution).toString(),
+                             "given a pointRadius, height equals 2*pointRadius");
+                feature.style = customStyle2;
+                layer.drawFeature(feature);
+                t.eq(root.firstChild.getAttributeNS(null, 'width'),
+                             root.firstChild.getAttributeNS(null, 'height'),
+                             "given a graphicWidth, width equals height");
+                t.eq(root.firstChild.getAttributeNS(null, 'width'),
+                             (customStyle2.graphicWidth*resolution).toString(),
+                             "width is set correctly");
+                feature.style = customStyle3;
+                layer.drawFeature(feature);
+                t.eq(root.firstChild.getAttributeNS(null, 'height'),
+                             root.firstChild.getAttributeNS(null, 'width'),
+                             "given a graphicHeight, height equals width");
+                t.eq(root.firstChild.getAttributeNS(null, 'height'),
+                             (customStyle3.graphicHeight*resolution).toString(),
+                             "height is set correctly");
+                feature.style = customStyle4;
+                layer.drawFeature(feature);
+                t.eq(root.firstChild.getAttributeNS(null, 'height'),
+                             (customStyle4.graphicHeight*resolution).toString(),
+                             "given graphicHeight and graphicWidth, both are set: height");
+                t.eq(root.firstChild.getAttributeNS(null, 'width'),
+                             (customStyle4.graphicWidth*resolution).toString(),
+                             "given graphicHeight and graphicWidth, both are set: width");
+                feature.style = customStyle5;
+                layer.drawFeature(feature);
+                t.eq(root.firstChild.getAttributeNS(null, 'style'),
+                             'opacity: '+customStyle5.graphicOpacity.toString()+((OpenLayers.Util.getBrowserName() == "opera" || OpenLayers.Util.getBrowserName() == "safari") ? "" : ';'),
+                             "graphicOpacity correctly set");
+                feature.style = customStyle6;
+                layer.drawFeature(feature);
+                t.eq(root.firstChild.getAttributeNS(null, 'x'),
+                        (geometryX + customStyle6.graphicXOffset*resolution).toString(),
+                        "graphicXOffset correctly set");
+                t.eq(root.firstChild.getAttributeNS(null, 'y'),
+                        (-geometryY + customStyle6.graphicYOffset*resolution).toString(),
+                        "graphicYOffset correctly set");
+        }
         if (layer.renderer.CLASS_NAME == 'OpenLayers.Renderer.VML') {
                 feature.style = customStyle1;
                 layer.drawFeature(feature);

Modified: sandbox/tschaub/canvas/tests/Map.html
===================================================================
--- sandbox/tschaub/canvas/tests/Map.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Map.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -163,8 +163,15 @@
     }
 
     function test_Map_center(t) {
-        t.plan(8);
-        map = new OpenLayers.Map('map');
+        t.plan(11);
+        var log = [];
+        map = new OpenLayers.Map('map', {
+            eventListeners: {
+                "movestart": function() {log.push("movestart");},
+                "move": function() {log.push("move");},
+                "moveend": function() {log.push("moveend");}
+            }            
+        });
         var baseLayer = new OpenLayers.Layer.WMS("Test Layer", 
             "http://octo.metacarta.com/cgi-bin/mapserv?",
             {map: "/mapdata/vmap_wms.map", layers: "basic"} );
@@ -180,7 +187,11 @@
         map.zoomOut();
         t.eq( map.getZoom(), 0, "map.zoom is correct after calling setCenter,zoom in, zoom out");
 
+        log = [];
         map.zoomTo(5);
+        t.eq(log[0], "movestart", "zoomTo fires movestart event");
+        t.eq(log[1], "move", "zoomTo fires move event");
+        t.eq(log[2], "moveend", "zoomTo fires moveend event");
         t.eq( map.getZoom(), 5, "map.zoom is correct after calling zoomTo" );
 
     /**
@@ -1052,7 +1063,7 @@
     }
 
     function test_Map_restrictedExtent(t) {
-        t.plan(24);
+        t.plan(25);
         var extent = new OpenLayers.Bounds(-180, -90, 180, 90);
         var options = {
             maxResolution: "auto"
@@ -1147,8 +1158,28 @@
              "map extent not restricted with null restrictedExtent for se");
 
         map.destroy();
+
+        extent = new OpenLayers.Bounds(8, 44.5, 19, 50);
+        var options = {
+            restrictedExtent: extent
+        };
+        map = new OpenLayers.Map('map', options);
+
+        var wms = new OpenLayers.Layer.WMS(
+            "OpenLayers WMS", 
+            "http://vmap0.tiles.osgeo.org/wms/vmap0?",
+            {layers: 'basic'}
+        ); 
+
+        map.addLayers([wms]);
+        map.zoomToExtent(extent);
+        map.zoomIn();
+        map.setOptions({restrictedExtent: null});
+        map.pan(-250, -250);
+        t.ok((map.getExtent().bottom == 48.3486328125 && map.getExtent().left == 7.45751953125), "Expected extent when toggling restrictedExtent");
+        map.destroy();
     }
-    
+
     function test_Map_getResolutionForZoom(t) {
         t.plan(2);
         var map = new OpenLayers.Map("map");
@@ -1507,24 +1538,76 @@
     
     function test_panTo(t) {
         
-        t.plan(2);
+        t.plan(6);
         
-        var map = new OpenLayers.Map("map");
+        var log = [];
+        var map = new OpenLayers.Map("map", {
+            eventListeners: {
+                "movestart": function() {log.push("movestart");},
+                "move": function() {log.push("move");},
+                "moveend": function() {log.push("moveend");}
+            }            
+        });
         map.addLayer(
             new OpenLayers.Layer(null, {isBaseLayer: true})
         );
         map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        t.eq(log[log.length-1], "moveend", "moveend fired when map center is set");
+        log = [];
         
         map.panTo(new OpenLayers.LonLat(1, 0));
         t.eq(map.panTween.playing, true, "the map pan tween is playing before destroy");
         
-        map.destroy();
-        t.ok(!map.panTween || !map.panTween.playing, "the map pan tween is not playing after destroy");
+        t.delay_call(2, function() {
+            t.eq(log[0], "movestart", "panTo starts with movestart event");
+            t.eq(log[1], "move", "move events fired while panning");
+            t.eq(log[log.length-1], "moveend", "panTo finishes with moveend event");
+            map.destroy();
+            t.ok(!map.panTween || !map.panTween.playing, "the map pan tween is not playing after destroy");
+        });
     }
+    
+    function test_pan(t) {
+        t.plan(4);
+        
+        var map = new OpenLayers.Map("map");
+        map.addLayer(
+            new OpenLayers.Layer(null, {isBaseLayer: true})
+        );
+        map.setCenter(new OpenLayers.LonLat(0, 0), 5);
+        var log = [];
+        map.events.on({
+            "movestart": function() {log.push("movestart");},
+            "move": function() {log.push("move");},
+            "moveend": function() {log.push("moveend");}
+        });
+        
+        // simulate the drag sequence of the DragPan control;
+        map.pan(5,5, {animate: false, dragging: true});
+        map.pan(1,1, {animate: false, dragging: false});
+        
+        t.eq(log[0], "movestart", "pan sequence starts with movestart");
+        t.eq(log[1], "move", "followed by move,");
+        t.eq(log[log.length-2], "move", "move again before we stop panning,");
+        t.eq(log[log.length-1], "moveend", "and moveend when we're done.");
+        
+    }
 
+    // test if we can call updateSize before document.body is ready. updateOk
+    // is tested in the test_updateSize function below
+    var earlyMap = new OpenLayers.Map();
+    var updateOk;
+    try {
+      earlyMap.updateSize();
+      updateOk = true;
+    } catch(e) {}
+    earlyMap.destroy();
     function test_updateSize(t) {
-        t.plan(2);
+        t.plan(3);
 
+        // checking updateSize from outside this test function (see above)
+        t.ok(updateOk, "updateSize works before document.body is ready");
+
         var map, moveToCnt, size;
 
         map = new OpenLayers.Map({div: "map"});
@@ -1554,7 +1637,7 @@
         map.updateSize();
         t.eq(moveToCnt, 1, "updateSize move the map if it has a center");
 
-        map.destroy();
+        map.destroy();        
     }
     
     function test_invisible_map(t) {

Modified: sandbox/tschaub/canvas/tests/Projection.html
===================================================================
--- sandbox/tschaub/canvas/tests/Projection.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Projection.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -58,7 +58,7 @@
          t.eq(proj2.equals(proj5), false, "Projection.equals() returns false for unknown projections with proj4js");
          
          if (!hasProj) {
-             delete window.Proj4js
+             window.Proj4js = undefined;
          }
          
      }

Modified: sandbox/tschaub/canvas/tests/Protocol/HTTP.html
===================================================================
--- sandbox/tschaub/canvas/tests/Protocol/HTTP.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Protocol/HTTP.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -287,291 +287,6 @@
         t.eq(ret, null, 'parseFeatures returns expected value');
     }
 
-    function test_filterToParams(t) {
-        t.plan(30);
-
-        // setup
-
-        var protocol, filter, params;
-
-        protocol = new OpenLayers.Protocol.HTTP();
-
-        // 1 test
-        var filter = new OpenLayers.Filter.Spatial({
-            type: OpenLayers.Filter.Spatial.BBOX,
-            value: new OpenLayers.Bounds(0, 1, 2, 3)
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.bbox, [0, 1, 2, 3],
-             "filterToParams sets correct bbox param if passed a BBOX filter");
-
-        // 3 tests
-        var lon = 100, lat = 200, tolerance = 10;
-        filter = new OpenLayers.Filter.Spatial({
-            type: OpenLayers.Filter.Spatial.DWITHIN,
-            value: new OpenLayers.Geometry.Point(lon, lat),
-            distance: tolerance
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.lon, lon,
-             "filterToParams sets correct lon param if passed a DWITHIN filter");
-        t.eq(params.lat, lat,
-             "filterToParams sets correct lat param if passed a DWITHIN filter");
-        t.eq(params.tolerance, tolerance,
-             "filterToParams sets correct tolerance param if passed a DWITHIN filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Spatial({
-            type: OpenLayers.Filter.Spatial.WITHIN,
-            value: new OpenLayers.Geometry.Point(lon, lat)
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.lon, lon,
-             "filterToParams sets correct lon param if passed a WITHIN filter");
-        t.eq(params.lat, lat,
-             "filterToParams sets correct lat param if passed a WITHIN filter");
-
-        // Some bbox filters used in the next tests.
-
-        var bboxFilter1 = new OpenLayers.Filter.Spatial({
-            type: OpenLayers.Filter.Spatial.BBOX,
-            value:  new OpenLayers.Bounds(0, 0, 10, 10)
-        });
-
-        var bboxFilter2 = new OpenLayers.Filter.Spatial({
-            type: OpenLayers.Filter.Spatial.BBOX,
-            value:  new OpenLayers.Bounds(0, 0, 20, 20)
-        });
-
-        // 1 test
-        filter = new OpenLayers.Filter.Logical({
-            type: OpenLayers.Filter.Logical.AND,
-            filters: []
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params, {},
-             "filterToParams returns empty object if given empty AND Logical filter");
-
-        // 1 test
-        filter = new OpenLayers.Filter.Logical({
-            type: OpenLayers.Filter.Logical.OR,
-            filters: [
-                bboxFilter1
-            ]
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params, {},
-             "filterToParams does not support OR Logical filter");
-
-        // 1 test
-        filter = new OpenLayers.Filter.Logical({
-            type: OpenLayers.Filter.Logical.AND,
-            filters: [
-                bboxFilter1
-            ]
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.bbox, [0, 0, 10, 10],
-             "filterToParams sets correct bbox param if passed " +
-             "a Logical filter containing a BBOX");
-
-        // 1 test
-        filter = new OpenLayers.Filter.Logical({
-            type: OpenLayers.Filter.Logical.AND,
-            filters: [
-                bboxFilter1, bboxFilter2
-            ]
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.bbox, [0, 0, 20, 20],
-             "filterToParams sets correct bbox param if passed " +
-             "multiple BBOX filter in a Logical filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.EQUAL_TO,
-            property: "foo",
-            value: "bar"
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an EQUAL_TO filter");
-        t.eq(params["foo__eq"], "bar",
-             "filterToParams sets correct param key and value if passed an EQUAL_TO filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.NOT_EQUAL_TO,
-            property: "foo",
-            value: "bar"
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an NOT_EQUAL_TO filter");
-        t.eq(params["foo__ne"], "bar",
-             "filterToParams sets correct param key and value if passed an NOT_EQUAL_TO filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.LESS_THAN,
-            property: "foo",
-            value: "bar"
-        });
-        var params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an LESS_THAN filter");
-        t.eq(params["foo__lt"], "bar",
-             "filterToParams sets correct param key and value if passed an LESS_THAN filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.LESS_THAN_OR_EQUAL_TO,
-            property: "foo",
-            value: "bar"
-        });
-        var params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an LESS_THAN_OR_EQUAL_TO filter");
-        t.eq(params["foo__lte"], "bar",
-             "filterToParams sets correct param key and value if passed an LESS_THAN_OR_EQUAL_TO filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.GREATER_THAN,
-            property: "foo",
-            value: "bar"
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an GREATER_THAN filter");
-        t.eq(params["foo__gt"], "bar",
-             "filterToParams sets correct param key and value if passed an GREATER_THAN filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.GREATER_THAN_OR_EQUAL_TO,
-            property: "foo",
-            value: "bar"
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an GREATER_THAN_OR_EQUAL_TO filter");
-        t.eq(params["foo__gte"], "bar",
-             "filterToParams sets correct param key and value if passed an GREATER_THAN_OR_EQUAL_TO filter");
-
-        // 2 tests
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.LIKE,
-            property: "foo",
-            value: "bar"
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed a LIKE filter");
-        t.eq(params["foo__ilike"], "bar",
-             "filterToParams sets correct param key and value if passed an LIKE filter");
-
-        // 4 tests
-        filter = new OpenLayers.Filter.Logical({
-            type: OpenLayers.Filter.Logical.AND,
-            filters: [
-                new OpenLayers.Filter.Comparison({
-                    type: OpenLayers.Filter.Comparison.EQUAL_TO,
-                    property: "foo",
-                    value: "bar"
-                }),
-                new OpenLayers.Filter.Comparison({
-                    type: OpenLayers.Filter.Comparison.LESS_THAN,
-                    property: "foo2",
-                    value: "baz"
-                })
-            ]
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed an EQUAL_TO filter within a AND filter");
-        t.eq(params["foo__eq"], "bar",
-             "filterToParams sets correct param key and value if passed an EQUAL_TO filter within a AND filter");
-        t.eq(params.queryable[1], "foo2",
-             "filterToParams sets correct queryable param if passed a LESS_THAN filter within a AND filter");
-        t.eq(params["foo2__lt"], "baz",
-             "filterToParams sets correct param key and value if passed a LESS_THAN filter within a AND filter");
-
-        // 2 tests
-        protocol = new OpenLayers.Protocol.HTTP({wildcarded: true});
-        filter = new OpenLayers.Filter.Comparison({
-            type: OpenLayers.Filter.Comparison.LIKE,
-            property: "foo",
-            value: "bar"
-        });
-        params = protocol.filterToParams(filter);
-        t.eq(params.queryable[0], "foo",
-             "filterToParams sets correct queryable param if passed a LIKE filter (wildcarded true)");
-        t.eq(params["foo__ilike"], "%bar%",
-             "filterToParams sets correct param key and value if passed an LIKE filter (wildcarded true)");
-    }
-
-    function test_regex2value(t) {
-        t.plan(16);
-
-        // setup
-
-        var protocol = new OpenLayers.Protocol.HTTP();
-        var value;
-
-        // test
-
-        value = protocol.regex2value("foo");
-        t.eq(value, "foo", 'regex2value converts "foo" to "foo"');
-
-        value = protocol.regex2value("foo%");
-        t.eq(value, "foo\\%", 'regex2value converts "foo%" to "foo\\%"');
-
-        value = protocol.regex2value("foo.*");
-        t.eq(value, "foo%", 'regex2value converts "foo.*" to "foo%"');
-
-        value = protocol.regex2value("f.*oo.*");
-        t.eq(value, "f%oo%", 'regex2value converts "f.*oo.*" to "f%oo%"');
-
-        value = protocol.regex2value("foo.");
-        t.eq(value, "foo_", 'regex2value converts "foo." to "foo_"');
-
-        value = protocol.regex2value("f.oo.");
-        t.eq(value, "f_oo_", 'regex2value converts "f.oo." to "f_oo_"');
-
-        value = protocol.regex2value("f.oo.*");
-        t.eq(value, "f_oo%", 'regex2value converts "f.oo.*" to "f_oo%"');
-
-        value = protocol.regex2value("foo\\\\");
-        t.eq(value, "foo\\\\", 'regex2value converts "foo\\\\" to "foo\\\\"');
-
-        value = protocol.regex2value("foo\\.");
-        t.eq(value, "foo.", 'regex2value converts "foo\\." to "foo."');
-
-        value = protocol.regex2value("foo\\\\.");
-        t.eq(value, "foo\\\\_", 'regex2value converts "foo\\\\." to "foo\\\\_"');
-
-        value = protocol.regex2value("foo\\*");
-        t.eq(value, "foo*", 'regex2value converts "foo\\*" to "foo*"');
-
-        value = protocol.regex2value("foo\\\\*");
-        t.eq(value, "foo\\\\*", 'regex2value converts "foo\\\\*" to "foo\\\\*"');
-
-        value = protocol.regex2value("foo\\\\.*");
-        t.eq(value, "foo\\\\%", 'regex2value converts "foo\\\\.*" to "foo\\\\%"');
-
-        value = protocol.regex2value("fo\\.o.*");
-        t.eq(value, "fo.o%", 'regex2value converts from "fo\\.o.*" to "fo.o%"');
-
-        value = protocol.regex2value("fo.*o\\.");
-        t.eq(value, "fo%o.", 'regex2value converts from "fo.*o\\." to "to%o."');
-
-        value = protocol.regex2value("\\*\\..*.\\\\.*\\\\.%");
-        t.eq(value, "*.%_\\\\%\\\\_\\%",
-             'regex2value converts from "\\*\\..*.\\\\.*\\\\.%" ' +
-             'to "*.%_\\\\%\\\\_\\%"');
-   }
-
     function test_create(t) {
         t.plan(10);
         var protocol = new OpenLayers.Protocol.HTTP({

Copied: sandbox/tschaub/canvas/tests/Protocol/Script.html (from rev 11762, trunk/openlayers/tests/Protocol/Script.html)
===================================================================
--- sandbox/tschaub/canvas/tests/Protocol/Script.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/Protocol/Script.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,271 @@
+<html>
+<head>
+  <script src="../../lib/OpenLayers.js"></script>
+  <script type="text/javascript">
+
+    function test_constructor(t) {
+        t.plan(11);
+        var a = new OpenLayers.Protocol.Script({
+            url: "foo"
+        });
+
+        // 7 tests
+        t.eq(a.url, "foo", "constructor sets url");
+        t.eq(a.options.url, a.url, "constructor copies url to options.url");
+        t.eq(a.params, {}, "constructor sets params");
+        t.eq(a.options.params, undefined, "constructor does not copy params to options.params");
+        t.ok(a.format instanceof OpenLayers.Format.GeoJSON,
+                "constructor sets a GeoJSON format by default");
+        t.eq(a.callbackKey, 'callback',
+                "callbackKey is set to 'callback' by default");
+        t.eq(a.callbackPrefix, '',
+                "callbackPrefix is set to '' by default");
+
+        var params = {hello: "world"};
+        var b = new OpenLayers.Protocol.Script({
+            url: "bar",
+            params: params,
+            callbackKey: 'cb_key',
+            callbackPrefix: 'cb_prefix'
+        });
+
+        // 6 tests
+        t.eq(b.params, params, "constructor sets params");
+        t.eq(b.options.params, b.params, "constructor copies params to options.params");
+        t.eq(b.callbackKey, 'cb_key',
+                "callbackKey is set to 'cb_key'");
+        t.eq(b.callbackPrefix, 'cb_prefix',
+                "callbackPrefix is set to 'cb_prefix'");
+    }
+
+    function test_destroy(t) {
+        t.plan(3);
+        var aborted = false;
+        var protocol = new OpenLayers.Protocol.Script({
+            url: "bar",
+            params: {hello: "world"},
+            abort: function() {
+                aborted = true;
+            }
+        });
+        protocol.destroy();
+        t.ok(aborted, "destroy aborts request");
+        t.eq(protocol.params, null, "destroy nullifies params");
+        t.eq(protocol.format, null, "destroy nullifies format");
+    }
+
+    function test_read(t) {
+        t.plan(5);
+        var protocol = new OpenLayers.Protocol.Script({
+            'url': 'foo_url',
+            'params': {'k': 'foo_param'}
+        });
+
+        // fake XHR request object
+        var request = {'status': 200};
+
+        // options to pass to read
+        var readOptions = {
+            'url': 'bar_url',
+            'params': {'k': 'bar_param'}
+        };
+
+        var response;
+
+        protocol.createRequest = function(url, params, callback) {
+            // 4 tests
+            t.ok(this == protocol,
+                'createRequest called with correct scope');
+            t.ok(url == readOptions.url,
+                'createRequest called with correct url');
+            t.ok(params == readOptions.params,
+                'createRequest called with correct params');
+            t.ok(callback instanceof Function,
+                'createRequest called with a function as callback');
+
+            return 'foo_request';
+       };
+
+        var resp = protocol.read(readOptions);
+
+        t.eq(resp.priv, 'foo_request',
+            'response priv property set to what the createRequest method returns');
+    }
+
+    function test_read_bbox(t) {
+        t.plan(6);
+
+        var _createRequest = OpenLayers.Protocol.Script.prototype.createRequest;
+
+        var bounds = new OpenLayers.Bounds(1, 2, 3, 4);
+        var filter = new OpenLayers.Filter.Spatial({
+            type: OpenLayers.Filter.Spatial.BBOX,
+            value: bounds,
+            projection: new OpenLayers.Projection("foo")
+        });
+
+        // log requests
+        var log, exp;
+        OpenLayers.Protocol.Script.prototype.createRequest = function(url, params,
+                                                               callback) {
+            log.push(params.bbox);
+            return null;
+        };
+
+        // 1) issue request with default protocol
+        log = [];
+        new OpenLayers.Protocol.Script().read({filter: filter});
+
+        t.eq(log.length, 1, "1) createRequest called once");
+        t.ok(log[0] instanceof Array, "1) bbox param is array");
+        exp = bounds.toArray();
+        t.eq(log[0], exp, "1) bbox param doesn't include SRS id by default");
+
+        // 2) issue request with default protocol
+        log = [];
+        new OpenLayers.Protocol.Script({srsInBBOX: true}).read({filter: filter});
+
+        t.eq(log.length, 1, "2) createRequest called once");
+        t.ok(log[0] instanceof Array, "2) bbox param is array");
+        exp = bounds.toArray();
+        exp.push("foo");
+        t.eq(log[0], exp, "2) bbox param includes SRS id if srsInBBOX is true");
+
+        OpenLayers.Protocol.Script.prototype.createRequest = _createRequest;
+    }
+
+    function test_createRequest(t) {
+        t.plan(3);
+        var protocol = new OpenLayers.Protocol.Script({
+            callbackKey: 'cb_key',
+            callbackPrefix: 'cb_prefix:'
+        });
+
+        var _register = OpenLayers.Protocol.Script.register;
+        OpenLayers.Protocol.Script.register = function() {
+            return 'bar';
+        };
+
+        var script = protocol.createRequest('http://bar_url/', {'k': 'bar_param'}, 'bar_callback');
+
+        t.eq(script.type, 'text/javascript',
+            'created script has a correct type');
+        t.eq(script.src, 'http://bar_url/?k=bar_param&cb_key=cb_prefix%3AOpenLayers.Protocol.Script.getCallback(bar)',
+            'created script has a correct url');
+        t.eq(script.id, 'OpenLayers_Protocol_Script_bar',
+            'created script has a correct id');
+
+        OpenLayers.Protocol.Script.register = _register;
+    }
+
+    function test_destroyRequest(t) {
+        t.plan(2);
+
+        var protocol = new OpenLayers.Protocol.Script({});
+
+        var _unregister = OpenLayers.Protocol.Script.unregister;
+        OpenLayers.Protocol.Script.unregister = function(id) {
+            t.eq(id, 'foo', "destroyRequest calls unregister with correct id");
+        };
+        var script = {
+            id: 'script_foo'
+        };
+        protocol.destroyRequest(script);
+        t.eq(protocol.pendingRequests[script.id], null, 
+            "destroyRequest nullifies the pending request");
+
+        OpenLayers.Protocol.Script.unregister = _unregister;
+    }
+
+    function test_handleResponse(t) {
+        t.plan(8);
+
+        var protocol = new OpenLayers.Protocol.Script();
+
+        // 2 tests (should be called only twive)
+        protocol.destroyRequest = function(priv) {
+            t.eq(priv, 'foo_priv', 'destroyRequest called with correct argument');
+        }
+
+        // 1 test (should be called only once)
+        protocol.parseFeatures = function(data) {
+            t.eq(data, 'foo_data', 'parseFeatures called with correct argument');
+            return 'foo_features';
+        }
+
+        var response = {
+            priv: 'foo_priv',
+            data: 'foo_data'
+        }
+        var options = {
+            // 2 tests (should be called twice)
+            scope: 'foo_scope',
+            callback: function(resp) {
+                t.eq(this, 'foo_scope', 'callback called with correct scope');
+            }
+        }
+        protocol.handleResponse(response, options);
+        // 2 tests
+        t.eq(response.code, OpenLayers.Protocol.Response.SUCCESS,
+                'response code correctly set');
+        t.eq(response.features, 'foo_features', 
+                'response features takes a correct value');
+
+        response = {
+            priv: 'foo_priv'
+        }
+        protocol.handleResponse(response, options);
+        // 1 test
+        t.eq(response.code, OpenLayers.Protocol.Response.FAILURE,
+                'response code correctly set');
+    }
+
+    function test_parseFeatures(t) {
+        t.plan(1);
+
+        var protocol = new OpenLayers.Protocol.Script();
+
+        protocol.format = {
+            'read': function(data) {
+                t.ok(true, 'format.read called');
+            }
+        };
+
+        var ret = protocol.parseFeatures({foo: 'bar'});
+    }
+
+    function test_abort(t) {
+        t.plan(2);
+
+        var protocol = new OpenLayers.Protocol.Script();
+
+        // 1 test
+        protocol.destroyRequest = function(priv) {
+            t.eq(priv, 'foo_priv', 'destroyRequest called with correct argument');
+        }
+
+        var response = {
+            priv: 'foo_priv'
+        }
+
+        protocol.abort(response);
+
+        var calls = [];
+        protocol.pendingRequests = {
+            'foo': 'foo_request',
+            'bar': 'bar_request'
+        }
+        protocol.destroyRequest = function(priv) {
+            calls.push(priv);
+        }
+        protocol.abort();
+        // 1 test
+        t.eq(calls, ['foo_request', 'bar_request'],
+                'destroyRequest called for each pending requests');
+    }
+
+  </script>
+</head>
+<body>
+</body>
+</html>

Modified: sandbox/tschaub/canvas/tests/Protocol/WFS.html
===================================================================
--- sandbox/tschaub/canvas/tests/Protocol/WFS.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Protocol/WFS.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -24,6 +24,39 @@
              "initialize returns instance of custom versioned protocol")
     }
 
+    function test_setGeometryName(t) {
+        t.plan(4);
+        var protocol = new OpenLayers.Protocol.WFS({
+            url: "http://some.url.org",
+            featureNS: "http://namespace.org",
+            featureType: "type",
+            geometryName: "geom"
+        });
+        t.eq(protocol.geometryName, "geom", "geometryName set correctly by constructor");
+        t.eq(protocol.format.geometryName, "geom", "geometryName correctly set on format by constructor");
+        // change the geometryName on the fly
+        protocol.setGeometryName("SHAPE");
+        t.eq(protocol.geometryName, "SHAPE", "geometryName changed correctly by setGeometryName");
+        t.eq(protocol.format.geometryName, "SHAPE", "geometryName correctly changed on format by setGeometryName");
+        protocol.destroy();
+    }
+
+    function test_setFeatureType(t) {
+        t.plan(4);
+        var protocol = new OpenLayers.Protocol.WFS({
+            url: "http://some.url.org",
+            featureNS: "http://namespace.org",
+            featureType: "type"
+        });
+        t.eq(protocol.featureType, "type", "featureType set correctly by constructor");
+        t.eq(protocol.format.featureType, "type", "featureType correctly set on format by constructor");
+        // change the feature type on the fly
+        protocol.setFeatureType("foo");
+        t.eq(protocol.featureType, "foo", "featureType changed correctly by setFeatureType");
+        t.eq(protocol.format.featureType, "foo", "featureType correctly changed on format by setFeatureType");
+        protocol.destroy();
+    }
+
     function test_read(t) {
         t.plan(7);
 

Copied: sandbox/tschaub/canvas/tests/Renderer/SVG2.html (from rev 11762, trunk/openlayers/tests/Renderer/SVG2.html)
===================================================================
--- sandbox/tschaub/canvas/tests/Renderer/SVG2.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/Renderer/SVG2.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,424 @@
+<html>
+<head>
+<script src="../OLLoader.js"></script>
+  <script type="text/javascript">
+
+    var geometry = null, node = null;
+    
+    function test_SVG_constructor(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(1);
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        t.ok(r instanceof OpenLayers.Renderer.SVG2, "new OpenLayers.Renderer.SVG2 returns SVG object" );
+    }
+    
+    function test_SVG_destroy(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(1);
+        
+        var g_Destroy = false;
+        
+        OpenLayers.Renderer.Elements.prototype._destroy = 
+            OpenLayers.Renderer.Elements.prototype.destroy;
+            
+        OpenLayers.Renderer.prototype.destroy = function() {
+            g_Destroy = true;
+        }
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        r.destroy();
+        
+        t.eq(g_Destroy, true, "OpenLayers.Renderer.Elements.destroy() called");
+        
+        OpenLayers.Renderer.prototype.destroy = 
+            OpenLayers.Renderer.prototype._destroy;
+    }
+    
+    function test_SVG_updateDimensions(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(5);
+        
+        OpenLayers.Renderer.SVG2.prototype._setExtent =
+            OpenLayers.Renderer.SVG2.prototype.setExtent;
+        
+        var g_SetExtent = false;
+        OpenLayers.Renderer.SVG2.prototype.setExtent = function() {
+            g_SetExtent = true;
+            OpenLayers.Renderer.SVG2.prototype._setExtent.apply(this, arguments);
+        }
+                
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        var extent = new OpenLayers.Bounds(1,2,3,4);
+        r.map = {
+            getResolution: function() {
+                return 0.5;
+            },
+            getExtent: function() {
+                return extent;
+            },
+            getMaxExtent: function() {
+                return extent;
+            }
+        }
+        r.updateDimensions();
+        
+        t.eq(g_SetExtent, true, "Elements.setExtent() called");
+        
+        t.eq(r.rendererRoot.getAttributeNS(null, "width"), "4", "width is correct");
+        t.eq(r.rendererRoot.getAttributeNS(null, "height"), "4", "height is correct");
+        t.eq(r.rendererRoot.getAttributeNS(null, "viewBox"), "1 -4 2 2", "rendererRoot viewBox is correct");
+        
+        // test extent changes
+        extent = new OpenLayers.Bounds(2,3,5,6);
+        r.updateDimensions();
+        t.eq(r.rendererRoot.getAttributeNS(null, "viewBox"), "2 -6 3 3", "rendererRoot viewBox is correct after a new setExtent");
+
+        OpenLayers.Renderer.SVG2.prototype.setExtent =
+            OpenLayers.Renderer.SVG2.prototype._setExtent;
+    }
+    
+    function test_SVG_drawpoint(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+    
+        t.plan(1);
+
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        
+        var properDraw = false;
+        var g_Radius = null;
+        r.drawCircle = function(n, g, r) {
+            properDraw = true;
+            g_Radius = 1;
+        }
+        r.drawPoint();
+        
+        t.ok(properDraw && g_Radius == 1, "drawPoint called drawCircle with radius set to 1");
+    }
+    
+    function test_SVG_drawcircle(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(5);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        r.resolution = 0.5;
+        r.left = 0;
+        r.top = 0;
+        
+        var node = document.createElement('div');
+        
+        var geometry = {
+            x: 1,
+            y: 2
+        }
+        
+        r.drawCircle(node, geometry, 3);
+        
+        t.eq(node.getAttributeNS(null, 'cx'), '1', "cx is correct");
+        t.eq(node.getAttributeNS(null, 'cy'), '-2', "cy is correct");
+        t.eq(node._radius, 3, "radius preset is correct");
+       
+        // #1274: out of bound node fails when first added
+        var geometry = {
+            x: 10000000,
+            y: 200000000,
+            CLASS_NAME: "OpenLayers.Geometry.Point",
+            id: "foo",
+            getBounds: function() {return {bottom: 0}}
+        }
+        node.id = geometry.id;
+        r.root.appendChild(node);
+
+        var drawCircleCalled = false;
+        r.drawCircle = function() {
+            drawCircleCalled = true;
+            return OpenLayers.Renderer.SVG2.prototype.drawCircle.apply(r, arguments);
+        }
+        
+        r.drawGeometry(geometry, {pointRadius: 3}, "blah_4000");
+        t.eq(drawCircleCalled, true, "drawCircle called on drawGeometry for a point geometry.")
+        t.ok(node.parentNode != r.root, "circle will not be drawn when coordinates are outside the valid range");
+    }
+    
+    function test_SVG_drawlinestring(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(2);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        
+        var node = document.createElement('div');
+        
+        var geometry = {
+            components: "foo"
+        }
+        g_GetString = false;
+        g_Components = null;
+        r.getComponentsString = function(c) {
+            g_GetString = true;
+            g_Components = c;
+            return "bar";
+        }
+        
+        r.drawLineString(node, geometry);
+        
+        t.ok(g_GetString && g_Components == "foo", "getComponentString is called with valid arguments");
+        t.eq(node.getAttributeNS(null, "points"), "bar", "points attribute is correct");
+    }
+    
+    function test_SVG_drawlinearring(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(2);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        
+        var node = document.createElement('div');
+        
+        var geometry = {
+            components: "foo"
+        }
+        g_GetString = false;
+        g_Components = null;
+        r.getComponentsString = function(c) {
+            g_GetString = true;
+            g_Components = c;
+            return "bar";
+        }
+        
+        r.drawLinearRing(node, geometry);
+        
+        t.ok(g_GetString, "getComponentString is called with valid arguments");
+        t.eq(node.getAttributeNS(null, "points"), "bar", "points attribute is correct");
+    }
+
+    function test_SVG_drawpolygon(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(3);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        
+        var node = document.createElement('div');
+        
+        var linearRings = [{
+            components: ["foo"]
+        },{
+            components: ["bar"]
+        }]
+        
+        var geometry = {
+            components: linearRings
+        }
+        g_GetString = false;
+        r.getShortString = function(c) {
+            g_GetString = true;
+            return c;
+        }
+        
+        r.drawPolygon(node, geometry);
+        
+        t.ok(g_GetString, "getShortString is called");
+        t.eq(node.getAttributeNS(null, "d"), "M foo M bar z", "d attribute is correctly set");
+        t.eq(node.getAttributeNS(null, "fill-rule"), "evenodd", "fill-rule attribute is correctly set");
+    }
+
+    function test_SVG_drawrectangle(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(4);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        r.resolution = 0.5;
+        r.left = 0;
+        r.top = 0;
+        
+        var node = document.createElement('div');
+        
+        var geometry = {
+            x: 1,
+            y: 2,
+            width: 3,
+            height: 4
+        }
+        
+        r.drawRectangle(node, geometry);
+        
+        t.eq(node.getAttributeNS(null, "x"), "1", "x attribute is correctly set");
+        t.eq(node.getAttributeNS(null, "y"), "-2", "y attribute is correctly set");
+        t.eq(node.getAttributeNS(null, "width"), "3", "width attribute is correctly set");
+        t.eq(node.getAttributeNS(null, "height"), "4", "height attribute is correctly set");
+    }
+    
+    function test_SVG_drawsurface(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(2);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        
+        var node = document.createElement('div');
+        
+        var geometry = {
+            components: ['foo', 'bar', 'dude']
+        }
+        g_GetString = false;
+        r.getShortString = function(c) {
+            g_GetString = true;
+            return c;
+        }
+        
+        r.drawSurface(node, geometry);
+        
+        t.ok(g_GetString, "getShortString is called");
+        
+        t.eq(node.getAttributeNS(null, "d"), "M foo C bar dude Z", "d attribute is correctly set");
+    }
+    
+    function test_SVG_getcomponentsstring(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(1);
+        
+        var components = ['foo', 'bar'];
+        
+        OpenLayers.Renderer.SVG2.prototype._getShortString = 
+            OpenLayers.Renderer.SVG2.prototype.getShortString;
+            
+        OpenLayers.Renderer.SVG2.prototype.getShortString = function(p) {
+            return p;
+        };
+        
+        var string = OpenLayers.Renderer.SVG2.prototype.getComponentsString(components);
+        t.eq(string, "foo,bar", "returned string is correct");
+        
+        OpenLayers.Renderer.SVG2.prototype.getShortString = 
+            OpenLayers.Renderer.SVG2.prototype._getShortString;
+    }
+    
+    
+    
+    function test_SVG_getshortstring(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+        
+        t.plan(1);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+        r.resolution = 0.5;
+        r.left = 0;
+        r.top = 0;
+        
+        var point = {
+            x: 1,
+            y: 2
+        };
+        
+        var string = r.getShortString(point);
+        t.eq(string, "1,-2", "returned string is correct");
+    }
+    
+    function test_svg_getnodetype(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+
+        t.plan(1);
+         
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+
+        var g = {CLASS_NAME: "OpenLayers.Geometry.Point"}
+        var s = {graphicName: "square"};
+        
+        t.eq(r.getNodeType(g, s), "svg", "Correct node type for well known symbols");
+    }
+        
+    function test_svg_importsymbol(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+
+        t.plan(2);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+
+        r.importSymbol("square");
+
+        var polygon = document.getElementById(r.container.id + "_defs").firstChild.firstChild;
+        
+        var pass = false;
+        for (var i = 0; i < polygon.points.numberOfItems; i++) {
+            var p = polygon.points.getItem(i);
+            pass = p.x === OpenLayers.Renderer.symbol.square[2*i] && 
+                   p.y === OpenLayers.Renderer.symbol.square[2*i+1];
+            if (!pass) {
+                break;
+            }
+        }
+        t.ok(pass, "Square symbol rendered correctly");
+        t.ok(r.symbolMetrics["-square"], "Symbol metrics cached correctly.");
+    }
+        
+    function test_svg_dashstyle(t) {
+        if (!OpenLayers.Renderer.SVG2.prototype.supported()) {
+            t.plan(0);
+            return;
+        }
+
+        t.plan(5);
+        
+        var r = new OpenLayers.Renderer.SVG2(document.body);
+
+        t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dot"}, 1), "1,4", "dot dasharray created correctly");
+        t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dash"}, 1), "4,4", "dash dasharray created correctly");
+        t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdash"}, 1), "8,4", "longdash dasharray created correctly");
+        t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "dashdot"}, 1), "4,4,1,4", "dashdot dasharray created correctly");
+        t.eq(r.dashStyle({strokeWidth: 1, strokeDashstyle: "longdashdot"}, 1), "8,4,1,4", "dashdot dasharray created correctly");
+    }
+
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px"></div>
+</body>
+</html>

Modified: sandbox/tschaub/canvas/tests/Strategy/BBOX.html
===================================================================
--- sandbox/tschaub/canvas/tests/Strategy/BBOX.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/Strategy/BBOX.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -87,7 +87,7 @@
     
     function test_events(t) {
         
-        t.plan(2);
+        t.plan(3);
         var log = {
             loadstart: 0,
             loadend: 0
@@ -117,6 +117,14 @@
         t.eq(log.loadstart, 1, "loadstart triggered");
         t.eq(log.loadend, 1, "loadend triggered");
         
+        log = {};
+        layer.protocol.read = function(obj) {
+            log.obj = obj;
+        }
+        layer.refresh({force: true, whee: 'chicken'});
+
+        t.eq(log.obj && log.obj.whee, "chicken", "properties passed to read on refresh correctly.");
+
         map.destroy();
         
     }
@@ -270,6 +278,38 @@
         
     }
 
+    // Test fix for Ticket #3142
+    function test_layerLoadedAfterBeingAdded(t) {
+        t.plan(1);
+
+        var dummy = new OpenLayers.Layer(null, {isBaseLayer: true});
+
+        var strategy = new OpenLayers.Strategy.BBOX({
+            ratio: 1 // makes for easier comparison to map bounds
+        });
+        var layer = new OpenLayers.Layer.Vector(null, {
+            protocol: new OpenLayers.Protocol(),
+            strategies: [strategy]
+        });
+
+        // Make sure to test the case of a vector layer needing to be 
+        // reprojected while the map is not yet centered
+        var layerReproject = new OpenLayers.Layer.Vector(null, {
+            protocol: new OpenLayers.Protocol(),
+            strategies: [new OpenLayers.Strategy.BBOX()],
+            projection: 'EPSG:900913'
+        });
+
+        var map = new OpenLayers.Map("map");
+        map.addLayer(dummy);
+        map.addLayer(layerReproject);
+        map.setCenter(new OpenLayers.LonLat(0, 0));
+        map.addLayer(layer);
+
+        // test that the strategy bounds were set
+        t.ok(map.getExtent().equals(strategy.bounds), "[set center] bounds set to map extent");
+    }
+
   </script>
 </head>
 <body>

Modified: sandbox/tschaub/canvas/tests/list-tests.html
===================================================================
--- sandbox/tschaub/canvas/tests/list-tests.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/list-tests.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -74,6 +74,7 @@
     <li>Format/Filter/v1.html</li>
     <li>Format/Filter/v1_0_0.html</li>
     <li>Format/Filter/v1_1_0.html</li>
+    <li>Format/QueryStringFilter.html</li>
     <li>Format/WFS.html</li>
     <li>Format/WFSCapabilities.html</li>
     <li>Format/WFSCapabilities/v1.html</li>
@@ -133,6 +134,7 @@
     <li>Layer.html</li>
     <li>Layer/ArcIMS.html</li> 
     <li>Layer/ArcGIS93Rest.html</li>
+    <li>Layer/ArcGISCache.html</li>
     <li>Layer/Bing.html</li>
     <li>Layer/EventPane.html</li>
     <li>Layer/FixedZoomLevels.html</li>
@@ -177,6 +179,7 @@
     <li>Projection.html</li>
     <li>Protocol.html</li>
     <li>Protocol/HTTP.html</li>
+    <li>Protocol/Script.html</li>
     <li>Protocol/SQL.html</li>
     <li>Protocol/SQL/Gears.html</li>
     <li>Protocol/WFS.html</li>
@@ -185,6 +188,7 @@
     <li>Renderer/Canvas.html</li>
     <li>Renderer/Elements.html</li>
     <li>Renderer/SVG.html</li>
+    <li>Renderer/SVG2.html</li>
     <li>Renderer/VML.html</li>
     <li>Request.html</li>
     <li>Request/XMLHttpRequest.html</li>

Copied: sandbox/tschaub/canvas/tests/manual/box-quirks.html (from rev 11762, trunk/openlayers/tests/manual/box-quirks.html)
===================================================================
--- sandbox/tschaub/canvas/tests/manual/box-quirks.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/manual/box-quirks.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,52 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>Box Handler Quirks Mode Test</title>
+    <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+    <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+    <style type="text/css">
+    /* simulate quirks mode (traditional box model) in browsers other than IE */
+    div {
+        box-sizing: border-box;
+        -moz-box-sizing: border-box;
+        -ms-box-sizing: border-box;
+        -webkit-box-sizing: border-box;
+    }
+    
+    .olHandlerBoxZoomBox {
+        border: 20px solid red;
+        border-left-width: 10px;
+        border-bottom-width: 30px;
+    }
+    </style>
+    <script src="../../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        var map, layer;
+        function init(){
+            map = new OpenLayers.Map( 'map' );
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+                    "http://vmap0.tiles.osgeo.org/wms/vmap0",
+                    {layers: 'basic'} );
+            map.addLayer(layer);
+            map.zoomToMaxExtent();
+        }
+    </script>
+  </head>
+  <body onload="init()">
+    <h1 id="title">Box handler Quirks Mode Test</h1>
+
+    <div id="shortdesc">Test the correct appearance of the ZoomBox in quirks mode</div>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        <p>For the box to be positioned correctly, we need to know the
+            width of the borders.</p>
+        <p>Shift-click on the map. A red box should be visible around the mouse
+            cursor position, with 20 pixels to the top and right, 10 pixels to
+            the left and 30 pixels to the bottom edge of the box.</p>
+        <p>Drag the box both to the top-left and the bottom-right. The cursor
+            should always be at the top-left or bottom-right inner corner of
+            the box.</p>
+    </div>
+  </body>
+</html>

Copied: sandbox/tschaub/canvas/tests/manual/box-strict.html (from rev 11762, trunk/openlayers/tests/manual/box-strict.html)
===================================================================
--- sandbox/tschaub/canvas/tests/manual/box-strict.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/manual/box-strict.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" 
+    "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+  <head>
+    <title>Box Handler Strict Mode Test</title>
+    <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+    <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+    <style type="text/css">
+    .olHandlerBoxZoomBox {
+        border: 20px solid red;
+        border-left-width: 10px;
+        border-bottom-width: 30px;
+    }
+    </style>
+    <script src="../../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        var map, layer;
+        function init(){
+            map = new OpenLayers.Map( 'map' );
+            layer = new OpenLayers.Layer.WMS( "OpenLayers WMS",
+                    "http://vmap0.tiles.osgeo.org/wms/vmap0",
+                    {layers: 'basic'} );
+            map.addLayer(layer);
+            map.zoomToMaxExtent();
+        }
+    </script>
+  </head>
+  <body onload="init()">
+    <h1 id="title">Box Handler Strict Mode Test</h1>
+
+    <div id="shortdesc">Test the correct appearance of the ZoomBox in strict mode</div>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        <p>For the box to be positioned correctly, we need to know the
+            width of the borders.</p>
+        <p>Shift-click on the map. A red box should be visible around the mouse
+            cursor position, with 20 pixels to the top and right, 10 pixels to
+            the left and 30 pixels to the bottom edge of the box.</p>
+        <p>Drag the box both to the top-left and the bottom-right. The cursor
+            should always be at the top-left or bottom-right inner corner of
+            the box.</p>
+    </div>
+  </body>
+</html>

Modified: sandbox/tschaub/canvas/tests/manual/vector-features-performance.html
===================================================================
--- sandbox/tschaub/canvas/tests/manual/vector-features-performance.html	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tests/manual/vector-features-performance.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -81,9 +81,16 @@
         }
 
         function init(){
+            // allow testing of specific renderers via "?renderer=Canvas", etc
+            var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+            renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
             map = new OpenLayers.Map('map');
             
-            vectorLayer = new OpenLayers.Layer.Vector("Vector Layer", {isBaseLayer: true});
+            vectorLayer = new OpenLayers.Layer.Vector("Vector Layer", {
+                isBaseLayer: true,
+                renderers: renderer
+            });
 
             map.addLayers([vectorLayer]);
             map.addControl(new OpenLayers.Control.MousePosition());

Copied: sandbox/tschaub/canvas/tests/speed/vector-renderers.html (from rev 11762, trunk/openlayers/tests/speed/vector-renderers.html)
===================================================================
--- sandbox/tschaub/canvas/tests/speed/vector-renderers.html	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/speed/vector-renderers.html	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Vector Features Performance Test</title>
+    <script type="text/javascript" src="https://getfirebug.com/firebug-lite.js#startOpened=true"></script>
+    <link rel="stylesheet" href="../../theme/default/style.css" type="text/css" />
+    <link rel="stylesheet" href="../../examples/style.css" type="text/css" />
+  </head>
+  <body>
+    <h1 id="title">Vector Rendering Performance</h1>
+    <div id="map" class="smallmap"></div>
+    <p>
+    This is a benchmark for vector rendering performance. Test results are
+    written to the debug console.
+    Select a renderer here:
+    <br/>
+    <select id="renderers"></select>
+    </p><p>
+    The benchmark shows the time needed to render the features, and how long a
+    move (drag or zoom) takes. Drag and zoom around to produce move results.
+    </p>
+    <script src="../../lib/OpenLayers.js"></script>
+    <script src="vector-renderers.js"></script>
+  </body>
+</html>
\ No newline at end of file

Copied: sandbox/tschaub/canvas/tests/speed/vector-renderers.js (from rev 11762, trunk/openlayers/tests/speed/vector-renderers.js)
===================================================================
--- sandbox/tschaub/canvas/tests/speed/vector-renderers.js	                        (rev 0)
+++ sandbox/tschaub/canvas/tests/speed/vector-renderers.js	2011-03-30 05:03:20 UTC (rev 11765)
@@ -0,0 +1,70 @@
+var map, vectorLayer, drawFeature, features
+
+map = new OpenLayers.Map('map', {
+    eventListeners: {
+        movestart: function() {
+            console.time("move");
+        },
+        moveend: function() {
+            console.timeEnd("move");
+        }
+    }
+});
+
+// allow testing of specific renderers via "?renderer=Canvas", etc
+var renderer = OpenLayers.Util.getParameters(window.location.href).renderer;
+renderer = (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
+
+vectorLayer = new OpenLayers.Layer.Vector("Vector Layer", {
+    isBaseLayer: true,
+    renderers: renderer,
+    eventListeners: {
+        beforefeaturesadded: function() {
+            console.time("addFeatures");
+        },
+        featuresadded: function() {
+            console.timeEnd("addFeatures");
+        }
+    }
+});
+
+map.addLayers([vectorLayer]);
+map.addControl(new OpenLayers.Control.MousePosition());
+map.setCenter(new OpenLayers.LonLat(0, 0), 2);
+
+features = new Array(500);
+var x, y, points
+for (var i = 0; i < 500; i++) {
+    x = 90-Math.random()*180;
+    y = 45-Math.random()*90;
+    var pointList = [];
+    for(var p=0; p<19; ++p) {
+        var a = p * (2 * Math.PI) / 20;
+        var r = Math.random() * 3 + 1;
+        var newPoint = new OpenLayers.Geometry.Point(x + (r * Math.cos(a)),
+                                                     y + (r * Math.sin(a)));
+        pointList.push(newPoint);
+    }
+    pointList.push(pointList[0]);
+    features[i] = new OpenLayers.Feature.Vector(
+        new OpenLayers.Geometry.LinearRing(pointList));
+        
+}
+vectorLayer.addFeatures(features);
+
+var select = document.getElementById("renderers");
+var renderers = OpenLayers.Layer.Vector.prototype.renderers;
+var option;
+for (var i=0, len=renderers.length; i<len; i++) {
+    if (OpenLayers.Renderer[renderers[i]].prototype.supported()) {
+        option = document.createElement("option");
+        option.textContent = renderers[i];
+        option.value = renderers[i];
+        option.selected = renderers[i] == vectorLayer.renderer.CLASS_NAME.split(".").pop();
+        select.appendChild(option);
+    }
+}
+select.onchange = function() {
+    window.location.href = window.location.href.split("?")[0] +
+        "?renderer=" + select.options[select.selectedIndex].value;
+}

Modified: sandbox/tschaub/canvas/theme/default/img/editing_tool_bar.png
===================================================================
(Binary files differ)

Modified: sandbox/tschaub/canvas/theme/default/style.css
===================================================================
--- sandbox/tschaub/canvas/theme/default/style.css	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/theme/default/style.css	2011-03-30 05:03:20 UTC (rev 11765)
@@ -1,7 +1,7 @@
 div.olMap {
     z-index: 0;
-    padding: 0px!important;
-    margin: 0px!important;
+    padding: 0 !important;
+    margin: 0 !important;
     cursor: default;
 }
 
@@ -80,8 +80,8 @@
 
 .olControlOverviewMapContainer {
     position: absolute;
-    bottom: 0px;
-    right: 0px;
+    bottom: 0;
+    right: 0;
 }
 
 .olControlOverviewMapElement {
@@ -91,13 +91,13 @@
 }
 
 .olControlOverviewMapMinimizeButton {
-    right: 0px;
+    right: 0;
     bottom: 80px;
     cursor: pointer;
 }    
 
 .olControlOverviewMapMaximizeButton {
-    right: 0px;
+    right: 0;
     bottom: 80px;
     cursor: pointer;
 }
@@ -137,18 +137,6 @@
     padding:5px;
     overflow: auto;
 }    
-.olControlNavToolbar { 
-    width:0px;
-    height:0px;
-}    
-.olControlNavToolbar div { 
-  display:block;
-  width:  28px;
-  height: 28px;
-  top: 300px;
-  left: 6px;
-  position: relative;
-}
 
 .olControlNavigationHistory {
    background-image: url("img/navigation_history.png");
@@ -158,82 +146,27 @@
 
 }
 .olControlNavigationHistoryPreviousItemActive { 
-  background-position: 0px 0px;
+  background-position: 0 0;
 }
 .olControlNavigationHistoryPreviousItemInactive { 
-   background-position: 0px -24px;
+   background-position: 0 -24px;
 }
 .olControlNavigationHistoryNextItemActive { 
-   background-position: -24px 0px;
+   background-position: -24px 0;
 }
 .olControlNavigationHistoryNextItemInactive { 
    background-position: -24px -24px;
 }
 
-.olControlNavToolbar .olControlNavigationItemActive { 
-  background-image: url("img/panning-hand-on.png");
-  background-repeat: no-repeat;
-}
-.olControlNavToolbar .olControlNavigationItemInactive { 
-  background-image: url("img/panning-hand-off.png");
-  background-repeat: no-repeat;
-}
-.olControlNavToolbar .olControlZoomBoxItemActive { 
-  background-image: url("img/drag-rectangle-on.png");
-  background-color: orange;
-  background-repeat: no-repeat;
-}
-.olControlNavToolbar .olControlZoomBoxItemInactive { 
-  background-image: url("img/drag-rectangle-off.png");
-  background-repeat: no-repeat;
-}
-.olControlEditingToolbar  {
-    float:right;
-    right: 0px;
-    height: 30px; 
-    width: 200px;
-}
-.olControlEditingToolbar div { 
-  background-image: url("img/editing_tool_bar.png");
-  background-repeat: no-repeat;
-  float:right;
-  width:  24px;
-  height: 24px;
-  margin: 5px;
-}
-.olControlEditingToolbar .olControlNavigationItemActive { 
-  background-position: -103px -23px; 
-}
-.olControlEditingToolbar .olControlNavigationItemInactive { 
-  background-position: -103px -0px; 
-}
-.olControlEditingToolbar .olControlDrawFeaturePointItemActive { 
-  background-position: -77px -23px; 
-}
-.olControlEditingToolbar .olControlDrawFeaturePointItemInactive { 
-  background-position: -77px -0px; 
-}
-.olControlEditingToolbar .olControlDrawFeaturePathItemInactive { 
-  background-position: -51px 0px; 
-}
-.olControlEditingToolbar .olControlDrawFeaturePathItemActive { 
-  background-position: -51px -23px; 
-}
-.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive { 
-  background-position: -26px 0px; 
-}
-.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive { 
-  background-position: -26px -23px ;                                                                   
-}
 div.olControlSaveFeaturesItemActive { 
     background-image: url(img/save_features_on.png);
     background-repeat: no-repeat;
-    background-position: 0px 1px;
+    background-position: 0 1px;
 }
 div.olControlSaveFeaturesItemInactive { 
     background-image: url(img/save_features_off.png);
     background-repeat: no-repeat;
-    background-position: 0px 1px;
+    background-position: 0 1px;
 }
 
 .olHandlerBoxZoomBox {
@@ -267,20 +200,20 @@
 }
 
 .olControlPanPanel .olControlPanNorthItemInactive {
-    top: 0px;
+    top: 0;
     left: 9px;
-    background-position: 0px 0px;
+    background-position: 0 0;
 }
 .olControlPanPanel .olControlPanSouthItemInactive {
     top: 36px;
     left: 9px;
-    background-position: 18px 0px;
+    background-position: 18px 0;
 }
 .olControlPanPanel .olControlPanWestItemInactive {
     position: absolute;
     top: 18px;
-    left: 0px;
-    background-position: 0px 18px;
+    left: 0;
+    background-position: 0 18px;
 }
 .olControlPanPanel .olControlPanEastItemInactive {
     top: 18px;
@@ -302,23 +235,31 @@
 }
 
 .olControlZoomPanel .olControlZoomInItemInactive {
-    top: 0px;
-    left: 0px;
-    background-position: 0px 0px;
+    top: 0;
+    left: 0;
+    background-position: 0 0;
 }
 
 .olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
     top: 18px;
-    left: 0px;
-    background-position: 0px -18px;
+    left: 0;
+    background-position: 0 -18px;
 }
 
 .olControlZoomPanel .olControlZoomOutItemInactive {
     top: 36px;
-    left: 0px;
-    background-position: 0px 18px;
+    left: 0;
+    background-position: 0 18px;
 }
 
+/* 
+ * When a potential text is bigger than the image it move the image
+ * with some headers (closes #3154) 
+ */
+.olControlPanZoomBar div {
+    font-size: 1px;
+}
+
 .olPopupCloseBox {
   background: url("img/close.gif") no-repeat;
   cursor: pointer;
@@ -366,7 +307,7 @@
 .olControlLayerSwitcher {
     position: absolute;
     top: 25px;
-    right: 0px;
+    right: 0;
     width: 20em;
     font-family: sans-serif;
     font-weight: bold;
@@ -403,7 +344,7 @@
 .olControlLayerSwitcher .maximizeDiv,
 .olControlLayerSwitcher .minimizeDiv {
     top: 5px;
-    right: 0px;
+    right: 0;
     cursor: pointer;
 }
 
@@ -413,3 +354,68 @@
 .olBingAttribution.road {
     color: #333;
 }
+
+/**
+ * Editing and navigation icons.
+ * (using the editing_tool_bar.png sprint image)
+ */
+.olControlNavToolbar ,
+.olControlEditingToolbar {
+    margin: 5px 5px 0 0;
+}
+.olControlNavToolbar div,
+.olControlEditingToolbar div {
+    background-image: url("img/editing_tool_bar.png");
+    background-repeat: no-repeat;
+    margin: 0 0 5px 5px;
+    width: 24px;
+    height: 22px;
+    cursor: pointer
+}
+/* positions */
+.olControlEditingToolbar {
+    right: 0;
+    top: 0;
+    width: 200px; /* Only for IE6 or IE without DOCTYPE */
+}
+.olControlNavToolbar {
+    top: 295px;
+    left: 9px;
+}
+/* layouts */
+.olControlEditingToolbar div {
+    float: right;
+}
+/* individual controls */
+.olControlNavToolbar .olControlNavigationItemInactive,
+.olControlEditingToolbar .olControlNavigationItemInactive {
+    background-position: -103px -1px;
+}
+.olControlNavToolbar .olControlNavigationItemActive ,
+.olControlEditingToolbar .olControlNavigationItemActive  {
+    background-position: -103px -24px;
+}
+.olControlNavToolbar .olControlZoomBoxItemInactive {
+    background-position: -128px -1px;
+}
+.olControlNavToolbar .olControlZoomBoxItemActive  {
+    background-position: -128px -24px;
+}
+.olControlEditingToolbar .olControlDrawFeaturePointItemInactive {
+    background-position: -77px -1px;
+}
+.olControlEditingToolbar .olControlDrawFeaturePointItemActive {
+    background-position: -77px -24px;
+}
+.olControlEditingToolbar .olControlDrawFeaturePathItemInactive {
+    background-position: -51px -1px;
+}
+.olControlEditingToolbar .olControlDrawFeaturePathItemActive {
+    background-position: -51px -24px;
+}
+.olControlEditingToolbar .olControlDrawFeaturePolygonItemInactive{
+    background-position: -26px -1px;
+}
+.olControlEditingToolbar .olControlDrawFeaturePolygonItemActive {
+    background-position: -26px -24px;
+}

Modified: sandbox/tschaub/canvas/tools/mergejs.py
===================================================================
--- sandbox/tschaub/canvas/tools/mergejs.py	2011-03-30 03:40:22 UTC (rev 11764)
+++ sandbox/tschaub/canvas/tools/mergejs.py	2011-03-30 05:03:20 UTC (rev 11765)
@@ -43,7 +43,7 @@
 
 SUFFIX_JAVASCRIPT = ".js"
 
-RE_REQUIRE = "@requires:? (.*)\n" # TODO: Ensure in comment?
+RE_REQUIRE = "@requires?:? (.*)\n" # TODO: Ensure in comment?
 
 class MissingImport(Exception):
     """Exception raised when a listed import is not found in the lib."""



More information about the Commits mailing list