[OpenLayers-Commits] r11857 - in sandbox/tschaub/canvas: . build examples lib/OpenLayers lib/OpenLayers/Control lib/OpenLayers/Feature lib/OpenLayers/Format/Filter lib/OpenLayers/Geometry lib/OpenLayers/Handler lib/OpenLayers/Layer lib/OpenLayers/Renderer tests/Control tests/Geometry tests/Handler tests/Renderer

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Fri Apr 1 18:40:37 EDT 2011


Author: tschaub
Date: 2011-04-01 15:40:36 -0700 (Fri, 01 Apr 2011)
New Revision: 11857

Added:
   sandbox/tschaub/canvas/examples/mobile-layers.html
   sandbox/tschaub/canvas/examples/mobile-layers.js
Modified:
   sandbox/tschaub/canvas/
   sandbox/tschaub/canvas/build/mobile.cfg
   sandbox/tschaub/canvas/examples/donut.js
   sandbox/tschaub/canvas/examples/mobile-base.js
   sandbox/tschaub/canvas/examples/mobile-drawing.html
   sandbox/tschaub/canvas/examples/mobile-drawing.js
   sandbox/tschaub/canvas/examples/mobile-jq.js
   sandbox/tschaub/canvas/examples/mobile-sencha.html
   sandbox/tschaub/canvas/examples/vector-features-with-text.html
   sandbox/tschaub/canvas/lib/OpenLayers/Control/DragFeature.js
   sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js
   sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js
   sandbox/tschaub/canvas/lib/OpenLayers/Geometry/LinearRing.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Feature.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js
   sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js
   sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js
   sandbox/tschaub/canvas/lib/OpenLayers/Map.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js
   sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js
   sandbox/tschaub/canvas/lib/OpenLayers/Util.js
   sandbox/tschaub/canvas/tests/Control/DragFeature.html
   sandbox/tschaub/canvas/tests/Control/Measure.html
   sandbox/tschaub/canvas/tests/Geometry/LinearRing.html
   sandbox/tschaub/canvas/tests/Handler/Feature.html
   sandbox/tschaub/canvas/tests/Handler/Path.html
   sandbox/tschaub/canvas/tests/Handler/Point.html
   sandbox/tschaub/canvas/tests/Renderer/Canvas.html
Log:
Merge -r11819:11856 from trunk.


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

Modified: sandbox/tschaub/canvas/build/mobile.cfg
===================================================================
--- sandbox/tschaub/canvas/build/mobile.cfg	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/build/mobile.cfg	2011-04-01 22:40:36 UTC (rev 11857)
@@ -9,12 +9,14 @@
 OpenLayers/Layer/SphericalMercator.js
 OpenLayers/Layer/XYZ.js
 OpenLayers/Layer/Bing.js
+OpenLayers/Layer/WMS.js
 OpenLayers/Control/TouchNavigation.js
 OpenLayers/Control/Geolocate.js
 OpenLayers/Control/ZoomPanel.js
 OpenLayers/Control/Attribution.js
 OpenLayers/Control/SelectFeature.js
 OpenLayers/Control/DrawFeature.js
+OpenLayers/Control/ModifyFeature.js
 OpenLayers/Control/Panel.js
 OpenLayers/Handler/Point.js
 OpenLayers/Handler/Path.js
@@ -23,6 +25,11 @@
 OpenLayers/Renderer/SVG.js
 OpenLayers/Renderer/Canvas.js
 OpenLayers/Format/GeoJSON.js
+OpenLayers/Format/KML.js
+OpenLayers/Protocol/HTTP.js
+OpenLayers/Protocol/WFS.js
+OpenLayers/Protocol/WFS/v1_0_0.js
+OpenLayers/Strategy/Fixed.js
 
 [exclude]
 

Modified: sandbox/tschaub/canvas/examples/donut.js
===================================================================
--- sandbox/tschaub/canvas/examples/donut.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/donut.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -1,3 +1,7 @@
+// 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 map = new OpenLayers.Map({
     div: "map",
     layers: [
@@ -2,3 +6,5 @@
         new OpenLayers.Layer.OSM(),
-        new OpenLayers.Layer.Vector()
+        new OpenLayers.Layer.Vector("Vector Layer", {
+            renderers: renderer
+        })
     ],
@@ -35,4 +41,4 @@
         draw.deactivate();
     }
 }
-document.getElementById("noneToggle").checked = true;
\ No newline at end of file
+document.getElementById("noneToggle").checked = true;

Modified: sandbox/tschaub/canvas/examples/mobile-base.js
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-base.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/mobile-base.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -7,10 +7,27 @@
 var gg = new OpenLayers.Projection("EPSG:4326");
 var sm = new OpenLayers.Projection("EPSG:900913");
 
-var init = function () {
+var init = function (onSelectFeatureFunction) {
 
     var vector = new OpenLayers.Layer.Vector("Vector Layer", {});
 
+    var sprintersLayer = new OpenLayers.Layer.Vector("Sprinters", {
+        styleMap: new OpenLayers.StyleMap({
+            externalGraphic: "img/mobile-loc.png",
+            graphicOpacity: 1.0,
+            graphicWith: 16,
+            graphicHeight: 26,
+            graphicYOffset: -26
+        })
+    });
+
+    var sprinters = getFeatures();
+    sprintersLayer.addFeatures(sprinters);
+
+    var selectControl = new OpenLayers.Control.SelectFeature(sprintersLayer, {
+        autoActivate:true,
+        onSelect: onSelectFeatureFunction});
+
     var geolocate = new OpenLayers.Control.Geolocate({
         id: 'locate-control',
         geolocationOptions: {
@@ -38,7 +55,8 @@
                     enableKinetic: true
                 }
             }),
-            geolocate
+            geolocate,
+            selectControl
         ],
         layers: [
             new OpenLayers.Layer.OSM("OpenStreetMap", null, {
@@ -66,8 +84,9 @@
                 type: "AerialWithLabels",
                 name: "Bing Aerial + Labels",
                 transitionEffect: 'resize'
-            }), 
-            vector
+            }),
+            vector,
+            sprintersLayer
         ],
         center: new OpenLayers.LonLat(0, 0),
         zoom: 1
@@ -79,7 +98,7 @@
         strokeColor: '#f00',
         strokeOpacity: 0.6
     };
-    geolocate.events.register("locationupdated",this,function(e) {
+    geolocate.events.register("locationupdated", this, function(e) {
         vector.removeAllFeatures();
         vector.addFeatures([
             new OpenLayers.Feature.Vector(
@@ -96,7 +115,7 @@
             new OpenLayers.Feature.Vector(
                 OpenLayers.Geometry.Polygon.createRegularPolygon(
                     new OpenLayers.Geometry.Point(e.point.x, e.point.y),
-                    e.position.coords.accuracy/2,
+                    e.position.coords.accuracy / 2,
                     50,
                     0
                 ),
@@ -106,4 +125,49 @@
         ]);
         map.zoomToExtent(vector.getDataExtent());
     });
+
+    function getFeatures() {
+        var features = {
+            "type": "FeatureCollection",
+            "features": [
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [1332700, 7906300]},
+                    "properties": {"Name": "Igor Tihonov", "Country":"Sweden", "City":"Gothenburg"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [790300, 6573900]},
+                    "properties": {"Name": "Marc Jansen", "Country":"Germany", "City":"Bonn"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [568600, 6817300]},
+                    "properties": {"Name": "Bart van den Eijnden", "Country":"Netherlands", "City":"Utrecht"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-7909900, 5215100]},
+                    "properties": {"Name": "Christopher Schmidt", "Country":"United States of America", "City":"Boston"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-937400, 5093200]},
+                    "properties": {"Name": "Jorge Gustavo Rocha", "Country":"Portugal", "City":"Braga"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-355300, 7547800]},
+                    "properties": {"Name": "Jennie Fletcher ", "Country":"Scotland", "City":"Edinburgh"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [657068.53608487, 5712321.2472725]},
+                    "properties": {"Name": "Bruno Binet ", "Country":"France", "City":"Chambéry"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [667250.8958124, 5668048.6072737]},
+                    "properties": {"Name": "Eric Lemoine", "Country":"France", "City":"Theys"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [653518.03606319, 5721118.5122914]},
+                    "properties": {"Name": "Antoine Abt", "Country":"France", "City":"La Motte Servolex"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [657985.78042416, 5711862.6251028]},
+                    "properties": {"Name": "Pierre Giraud", "Country":"France", "City":"Chambéry"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [742941.93818208, 5861818.9477535]},
+                    "properties": {"Name": "Stéphane Brunner", "Country":"Switzerland", "City":"Paudex"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [736082.61064069, 5908165.4649505]},
+                    "properties": {"Name": "Frédéric Junod", "Country":"Switzerland", "City":"Montagny-près-Yverdon"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [771595.97057525, 5912284.7041793]},
+                    "properties": {"Name": "Cédric Moullet", "Country":"Switzerland", "City":"Payerne"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [744205.23922364, 5861277.319748]},
+                    "properties": {"Name": "Benoit Quartier", "Country":"Switzerland", "City":"Lutry"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [1717430.147101, 5954568.7127565]},
+                    "properties": {"Name": "Andreas Hocevar", "Country":"Austria", "City":"Graz"}},
+                { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-12362007.067301,5729082.2365672]},
+                    "properties": {"Name": "Tim Schaub", "Country":"United States of America", "City":"Bozeman"}}
+            ]
+        };
+
+        var reader = new OpenLayers.Format.GeoJSON();
+
+        return reader.read(features);
+    }
+
 };

Modified: sandbox/tschaub/canvas/examples/mobile-drawing.html
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-drawing.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/mobile-drawing.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -55,6 +55,12 @@
             div.olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
                 display: none;
             }
+            .olControlEditingToolbar .olControlModifyFeatureItemInactive {
+                background-position: -1px -1px;
+            }
+            .olControlEditingToolbar .olControlModifyFeatureItemActive {
+                background-position: -1px -24px;
+            }
             #title, #tags, #shortdesc {
                 display: none;
             }

Modified: sandbox/tschaub/canvas/examples/mobile-drawing.js
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-drawing.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/mobile-drawing.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -1,7 +1,13 @@
 function init() {
 
     // create a vector layer for drawing
-    var vector = new OpenLayers.Layer.Vector();
+    var vector = new OpenLayers.Layer.Vector('Vector Layer', {
+        styleMap: new OpenLayers.StyleMap({
+            temporary: OpenLayers.Util.applyDefaults({
+                pointRadius: 16
+            }, OpenLayers.Feature.Vector.style.temporary)
+        })
+    });
 
     // OpenLayers' EditingToolbar internally creates a Navigation control, we
     // want a TouchNavigation control here so we create our own editing toolbar
@@ -14,6 +20,10 @@
         new OpenLayers.Control({
             displayClass: 'olControlNavigation'
         }),
+        new OpenLayers.Control.ModifyFeature(vector, {
+            vertexRenderIntent: 'temporary',
+            displayClass: 'olControlModifyFeature'
+        }),
         new OpenLayers.Control.DrawFeature(vector, OpenLayers.Handler.Point, {
             displayClass: 'olControlDrawFeaturePoint'
         }),
@@ -52,4 +62,4 @@
     // 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-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/mobile-jq.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -22,7 +22,10 @@
             map.updateSize();
         } else {
             // initialize map
-            init();
+            init(function(feature) { 
+                selectedFeature = feature; 
+                $.mobile.changePage($("#popup"), "pop"); 
+            });
         }
     }
     $(window).bind("orientationchange resize pageshow", fixContentHeight);
@@ -44,30 +47,7 @@
             control.activate();
         }
     });
-
-    var sprintersLayer = new OpenLayers.Layer.Vector("Sprinters", {
-        styleMap: new OpenLayers.StyleMap({
-            externalGraphic: "img/mobile-loc.png",
-            graphicOpacity: 1.0,
-            graphicWith: 16,
-            graphicHeight: 26,
-            graphicYOffset: -26
-        })
-    });
     
-    var sprinters = getFeatures();
-    sprintersLayer.addFeatures(sprinters);
-    
-    map.addLayer(sprintersLayer);
-    
-    var selectControl = new OpenLayers.Control.SelectFeature(sprintersLayer, {onSelect: function(feature){
-        selectedFeature = feature;
-        $.mobile.changePage($("#popup"), "pop");
-    }});
-    
-    map.addControl(selectControl);
-    selectControl.activate();
-    
     $('div#popup').live('pageshow',function(event, ui){
         var li = "";
         for(var attr in selectedFeature.attributes){
@@ -168,47 +148,3 @@
         }
     });
 }
-
-function getFeatures(){
-    var features = {
-      "type": "FeatureCollection", 
-      "features": [
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [1332700, 7906300]}, 
-            "properties": {"Name": "Igor Tihonov", "Country":"Sweden", "City":"Gothenburg"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [790300, 6573900]}, 
-            "properties": {"Name": "Marc Jansen", "Country":"Germany", "City":"Bonn"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [568600, 6817300]}, 
-            "properties": {"Name": "Bart van den Eijnden", "Country":"Netherlands", "City":"Utrecht"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-7909900, 5215100]}, 
-            "properties": {"Name": "Christopher Schmidt", "Country":"United States of America", "City":"Boston"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-937400, 5093200]}, 
-            "properties": {"Name": "Jorge Gustavo Rocha", "Country":"Portugal", "City":"Braga"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-355300, 7547800]}, 
-            "properties": {"Name": "Jennie Fletcher ", "Country":"Scotland", "City":"Edinburgh"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [657068.53608487, 5712321.2472725]}, 
-            "properties": {"Name": "Bruno Binet ", "Country":"France", "City":"Chambéry"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [667250.8958124, 5668048.6072737]}, 
-            "properties": {"Name": "Eric Lemoine", "Country":"France", "City":"Theys"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [653518.03606319, 5721118.5122914]}, 
-            "properties": {"Name": "Antoine Abt", "Country":"France", "City":"La Motte Servolex"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [657985.78042416, 5711862.6251028]}, 
-            "properties": {"Name": "Pierre Giraud", "Country":"France", "City":"Chambéry"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [742941.93818208, 5861818.9477535]}, 
-            "properties": {"Name": "Stéphane Brunner", "Country":"Switzerland", "City":"Paudex"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [736082.61064069, 5908165.4649505]},
-            "properties": {"Name": "Frédéric Junod", "Country":"Switzerland", "City":"Montagny-près-Yverdon"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [771595.97057525, 5912284.7041793]},
-            "properties": {"Name": "Cédric Moullet", "Country":"Switzerland", "City":"Payerne"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [744205.23922364, 5861277.319748]},
-            "properties": {"Name": "Benoit Quartier", "Country":"Switzerland", "City":"Lutry"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [1717430.147101, 5954568.7127565]}, 
-            "properties": {"Name": "Andreas Hocevar", "Country":"Austria", "City":"Graz"}},
-            { "type": "Feature", "geometry": {"type": "Point", "coordinates": [-12362007.067301,5729082.2365672]}, 
-            "properties": {"Name": "Tim Schaub", "Country":"United States of America", "City":"Bozeman"}}
-       ]
-    };
-
-    var reader = new OpenLayers.Format.GeoJSON();
-    
-    return reader.read(features);
-}

Copied: sandbox/tschaub/canvas/examples/mobile-layers.html (from rev 11856, trunk/openlayers/examples/mobile-layers.html)
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-layers.html	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/mobile-layers.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <title>OpenLayers Mobile Layers</title>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <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">
+    <script src="../lib/OpenLayers.js?mobile"></script>
+    <script src="mobile-layers.js"></script>
+    <style>
+        html, body {
+            margin: 0;
+            padding: 0;
+            height: 100%;
+            width: 100%;
+        }
+
+        @media only screen and (max-width: 600px) {
+            html, body {
+                height: 117%;
+            }
+        }
+
+        #map {
+            width: 100%;
+            position: relative;
+            height: 100%;
+        }
+
+        .olControlAttribution {
+            position: absolute;
+            font-size: 10px;
+            bottom: 0 !important;
+            right: 0 !important;
+            background: rgba(0, 0, 0, 0.1);
+            font-family: Arial;
+            padding: 2px 4px;
+            border-radius: 5px 0 0 0;
+        }
+
+        div.olControlZoomPanel .olControlZoomInItemInactive,
+        div.olControlZoomPanel .olControlZoomOutItemInactive {
+            background: rgba(0, 0, 0, 0.2);
+            position: absolute;
+        }
+
+        div.olControlZoomPanel .olControlZoomInItemInactive {
+            border-radius: 5px 5px 0 0;
+        }
+
+        div.olControlZoomPanel .olControlZoomOutItemInactive {
+            border-radius: 0 0 5px 5px;
+            top: 37px;
+        }
+
+        div.olControlZoomPanel .olControlZoomOutItemInactive:after,
+        div.olControlZoomPanel .olControlZoomInItemInactive:after {
+            font-weight: bold;
+            content: '+';
+            font-size: 36px;
+            padding: 7px;
+            z-index: 2000;
+            color: #fff;
+            line-height: 1em;
+        }
+
+        div.olControlZoomPanel .olControlZoomOutItemInactive:after {
+            content: '–';
+            line-height: 0.9em;
+            padding: 0 8px;
+        }
+
+        div.olControlZoomPanel .olControlZoomToMaxExtentItemInactive {
+            display: none;
+        }
+
+        #title, #tags, #shortdesc {
+            display: none;
+        }
+    </style>
+</head>
+<body>
+<h1 id="title">Mobile example with various layer types</h1>
+
+<div id="tags">
+    mobile, WMS, WFS, KML
+</div>
+<p id="shortdesc">
+    A mobile example displaying various layer types: WMS, WFS, KML.
+</p>
+
+<div id="map"></div>
+<script>
+    init();
+</script>
+</body>
+</html>

Copied: sandbox/tschaub/canvas/examples/mobile-layers.js (from rev 11856, trunk/openlayers/examples/mobile-layers.js)
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-layers.js	                        (rev 0)
+++ sandbox/tschaub/canvas/examples/mobile-layers.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -0,0 +1,72 @@
+// initialize map when page ready
+var map;
+
+// Get rid of address bar on iphone/ipod
+var fixSize = function() {
+    window.scrollTo(0, 0);
+    document.body.style.height = '100%';
+    if (!(/(iphone|ipod)/.test(navigator.userAgent.toLowerCase()))) {
+        if (document.body.parentNode) {
+            document.body.parentNode.style.height = '100%';
+        }
+    }
+};
+setTimeout(fixSize, 700);
+setTimeout(fixSize, 1500);
+
+// 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;
+
+OpenLayers.ProxyHost = "proxy.cgi?url=";
+
+function init() {
+
+    map = new OpenLayers.Map({
+        div: "map",
+        theme: null,
+        controls: [
+            new OpenLayers.Control.Attribution(),
+            new OpenLayers.Control.TouchNavigation({
+                dragPanOptions: {
+                    interval: 100,
+                    enableKinetic: true
+                }
+            }),
+            new OpenLayers.Control.ZoomPanel()
+        ]
+    });
+
+    var wms = new OpenLayers.Layer.WMS("OpenLayers WMS",
+        "http://vmap0.tiles.osgeo.org/wms/vmap0",
+        {layers: 'basic'},
+        {isBaseLayer: true, transitionEffect: 'resize'}
+    )
+
+    var kml = new OpenLayers.Layer.Vector("KML", {
+        projection: map.displayProjection,
+        strategies: [new OpenLayers.Strategy.Fixed()],
+        protocol: new OpenLayers.Protocol.HTTP({
+            url: "kml/sundials.kml",
+            format: new OpenLayers.Format.KML({
+                extractStyles: true,
+                extractAttributes: true
+            })
+        }),
+        renderers: renderer
+    });
+
+    var wfs = new OpenLayers.Layer.Vector("States", {
+        strategies: [new OpenLayers.Strategy.Fixed()],
+        protocol: new OpenLayers.Protocol.WFS({
+            url: "http://demo.opengeo.org/geoserver/wfs",
+            featureType: "states",
+            featureNS: "http://www.openplans.org/topp"
+        }),
+        renderers: renderer
+    });
+
+    map.addLayers([wms, wfs, kml]);
+
+    map.setCenter(new OpenLayers.LonLat(-104, 42), 3);
+};

Modified: sandbox/tschaub/canvas/examples/mobile-sencha.html
===================================================================
--- sandbox/tschaub/canvas/examples/mobile-sencha.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/mobile-sencha.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -1,8 +1,8 @@
 <!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" />
+        <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"/>
         <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
         <title>OpenLayers with Sencha Touch</title>
         <script src="../lib/OpenLayers.js?mobile"></script>
@@ -15,29 +15,36 @@
             .searchList {
                 min-height: 150px;
             }
+
             .close-btn {
                 position: absolute;
                 right: 10px;
                 top: 10px;
             }
+
             img.minus {
                 -webkit-mask-image: url(img/minus1.png);
             }
+
             img.layers {
                 -webkit-mask-image: url(img/list.png);
             }
+
             .gx-layer-item {
                 margin-left: 10px;
             }
+
             #map {
                 width: 100%;
                 height: 100%;
             }
+
             .olControlAttribution {
                 font-size: 10px;
                 bottom: 5px;
                 right: 5px;
             }
+
             #title, #tags, #shortdesc {
                 display: none;
             }
@@ -59,7 +66,7 @@
                         items: [{
                             iconCls: "search",
                             iconMask: true,
-                            handler: function(){
+                            handler: function() {
                                 // this is the app
                                 if (!app.searchFormPopupPanel) {
                                     app.searchFormPopupPanel = new App.SearchFormPopupPanel({
@@ -124,11 +131,37 @@
                             monitorResize: true,
                             id: "map",
                             listeners: {
-                                render: init,
+                                render: function() {
+                                    var self = this;
+                                    init(function(feature) {
+                                        var htmlContent = "";
+                                        for (var property in feature.data) {
+                                            if (feature.data[property] != 'undefined') {
+                                                htmlContent = htmlContent + feature.data[property] + "<br>";
+                                            }
+                                        }
+                                        if (self.featurePopup) {
+                                            self.featurePopup.destroy();
+                                        }
+                                        self.featurePopup = new Ext.Panel({
+                                            floating: true,
+                                            modal: true,
+                                            centered: true,
+                                            hideOnMaskTap: true,
+                                            width: 240,
+                                            html: htmlContent,
+                                            scroll: 'vertical'
+                                        });
+                                        self.featurePopup.show();
+                                    })
+                                },
                                 resize: function() {
                                     if (window.map) {
                                         map.updateSize();
                                     }
+                                },
+                                scope: {
+                                    featurePopup: null
                                 }
                             }
                         }
@@ -140,6 +173,7 @@
     </head>
     <body>
         <h1 id="title">OpenLayers with Sencha Touch</h1>
+
         <div id="tags">
             mobile, sencha touch
         </div>

Modified: sandbox/tschaub/canvas/examples/vector-features-with-text.html
===================================================================
--- sandbox/tschaub/canvas/examples/vector-features-with-text.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/examples/vector-features-with-text.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -29,7 +29,8 @@
                     fillOpacity: 0.5,
                     pointRadius: 6,
                     pointerEvents: "visiblePainted",
-                    label : "name: ${name}, age: ${age}",
+                    // label with \n linebreaks
+                    label : "name: ${name}\n\nage: ${age}",
                     
                     fontColor: "${favColor}",
                     fontSize: "12px",
@@ -113,7 +114,7 @@
             
             map.addLayer(vectorLayer);
             vectorLayer.drawFeature(multiFeature);
-            map.setCenter(new OpenLayers.LonLat(point.x, point.y), 3);
+            map.setCenter(new OpenLayers.LonLat(-109.370078125, 43.39484375), 4);
             vectorLayer.addFeatures([pointFeature, polygonFeature, multiFeature, labelOffsetFeature, nullFeature ]);
         }
     </script>

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Control/DragFeature.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Control/DragFeature.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Control/DragFeature.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -151,6 +151,10 @@
             ),
             feature: new OpenLayers.Handler.Feature(
                 this, this.layer, OpenLayers.Util.extend({
+                    // 'click' and 'clickout' callback are for the mobile
+                    // support: no 'over' or 'out' in touch based browsers.
+                    click: this.clickFeature,
+                    clickout: this.clickoutFeature,
                     over: this.overFeature,
                     out: this.outFeature
                 }, this.featureCallbacks),
@@ -158,8 +162,35 @@
             )
         };
     },
-    
+
     /**
+     * Method: clickFeature
+     * Called when the feature handler detects a click-in on a feature.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>}
+     */
+    clickFeature: function(feature) {
+        if (this.overFeature(feature)) {
+            this.handlers.drag.dragstart(this.handlers.feature.evt);
+            // to let the events propagate to the feature handler (click callback)
+            this.handlers.drag.stopDown = false;
+        }
+    },
+
+    /**
+     * Method: clickoutFeature
+     * Called when the feature handler detects a click-out on a feature.
+     *
+     * Parameters:
+     * feature - {<OpenLayers.Feature.Vector>}
+     */
+    clickoutFeature: function(feature) {
+        this.outFeature(feature);
+        this.handlers.drag.stopDown = true;
+    },
+
+    /**
      * APIMethod: destroy
      * Take care of things that are not handled in superclass
      */
@@ -207,11 +238,16 @@
      *
      * Parameters:
      * feature - {<OpenLayers.Feature.Vector>} The selected feature.
+     *
+     * Returns:
+     * {Boolean} Successfully activated the drag handler.
      */
     overFeature: function(feature) {
+        var activated = false;
         if(!this.handlers.drag.dragging) {
             this.feature = feature;
             this.handlers.drag.activate();
+            activated = true;
             this.over = true;
             OpenLayers.Element.addClass(this.map.viewPortDiv, this.displayClass + "Over");
             this.onEnter(feature);
@@ -222,6 +258,7 @@
                 this.over = false;
             }
         }
+        return activated;
     },
 
     /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Feature/Vector.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -365,7 +365,7 @@
  * graphicZIndex - {Number} The integer z-index value to use in rendering.
  * graphicName - {String} Named graphic to use when rendering points.  Supported values include "circle" (default),
  *     "square", "star", "x", "cross", "triangle".
- * graphicTitle - {String} Tooltip for an external graphic. Only supported in Firefox and Internet Explorer.
+ * graphicTitle - {String} Tooltip for an external graphic.
  * backgroundGraphic - {String} Url to a graphic to be used as the background under an externalGraphic.
  * backgroundGraphicZIndex - {Number} The integer z-index value to use in rendering the background graphic.
  * backgroundXOffset - {Number} The x offset (in pixels) for the background graphic.
@@ -377,10 +377,9 @@
  * labelAlign - {String} Label alignment. This specifies the insertion point relative to the text. It is a string
  *     composed of two characters. The first character is for the horizontal alignment, the second for the vertical
  *     alignment. Valid values for horizontal alignment: "l"=left, "c"=center, "r"=right. Valid values for vertical
- *     alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb". The canvas renderer does not
- *     support vertical alignment, it will always use "b".
- * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label.
- * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label.
+ *     alignment: "t"=top, "m"=middle, "b"=bottom. Example values: "lt", "cm", "rb".
+ * labelXOffset - {Number} Pixel offset along the positive x axis for displacing the label. Not supported by the canvas renderer.
+ * labelYOffset - {Number} Pixel offset along the positive y axis for displacing the label. Not supported by the canvas renderer.
  * labelSelect - {Boolean} If set to true, labels will be selectable using SelectFeature or similar controls.
  *     Default is false.
  * fontColor - {String} The font color for the label, to be provided like CSS.

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Format/Filter/v1.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -5,6 +5,7 @@
 /**
  * @requires OpenLayers/Format/Filter.js
  * @requires OpenLayers/Format/XML.js
+ * @requires OpenLayers/Filter/Function.js
  */
 
 /**

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Geometry/LinearRing.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Geometry/LinearRing.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Geometry/LinearRing.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -93,7 +93,7 @@
      * point - {<OpenLayers.Geometry.Point>}
      */
     removeComponent: function(point) {
-        if (this.components.length > 4) {
+        if (this.components.length > 3) {
 
             //remove last point
             this.components.pop();

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Feature.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Feature.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Feature.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -56,6 +56,13 @@
      * {<OpenLayers.Pixel>} The location of the last mouseup.
      */
     up: 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: clickTolerance
@@ -129,10 +136,34 @@
      * {Boolean} Let the event propagate.
      */
     touchstart: function(evt) {
+        if(!this.touch) {
+            this.touch =  true;
+            this.map.events.un({
+                mousedown: this.mousedown,
+                mouseup: this.mouseup,
+                mousemove: this.mousemove,
+                click: this.click,
+                dblclick: this.dblclick,
+                scope: this
+            });
+        }
         return this.mousedown(evt);
     },
 
     /**
+     * Method: touchmove
+     * Handle touchmove events. We just prevent the browser default behavior,
+     *    for Android Webkit not to select text when moving the finger after
+     *    selecting a feature.
+     *
+     * Parameters:
+     * evt - {Event}
+     */
+    touchmove: function(evt) {
+        OpenLayers.Event.stop(evt);
+    },
+
+    /**
      * Method: mousedown
      * Handle mouse down.  Stop propagation if a feature is targeted by this
      *     event (stops map dragging during feature selection).
@@ -251,6 +282,11 @@
             this.lastFeature = null;
         }
         if(this.feature) {
+            if(evt.type === "touchstart") {
+                // stop the event to prevent Android Webkit from
+                // "flashing" the map div
+                OpenLayers.Event.stop(evt);
+            }
             var inNew = (this.feature != this.lastFeature);
             if(this.geometryTypeMatches(this.feature)) {
                 // in to a feature
@@ -349,6 +385,7 @@
             this.lastFeature = null;
             this.down = null;
             this.up = null;
+            this.touch = false;
             this.map.events.un({
                 "removelayer": this.handleMapEvents,
                 "changelayer": this.handleMapEvents,

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Path.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -157,6 +157,7 @@
         this.line.geometry.addComponent(
             this.point.geometry, this.line.geometry.components.length
         );
+        this.layer.addFeatures([this.point]);
         this.callback("point", [this.point.geometry, this.getGeometry()]);
         this.callback("modify", [this.point.geometry, this.getSketch()]);
         this.drawFeature();
@@ -261,8 +262,8 @@
     },
 
     /**
-     * Method: mousedown
-     * Handle mouse down.  Add a new point to the geometry and
+     * Method: down
+     * Handle mousedown and touchstart.  Add a new point to the geometry and
      * render it. Return determines whether to propagate the event on the map.
      * 
      * Parameters:
@@ -286,8 +287,8 @@
     },
 
     /**
-     * Method: mousemove
-     * Handle mouse move.  Adjust the geometry and redraw.
+     * Method: move
+     * Handle mousemove and touchmove.  Adjust the geometry and redraw.
      * Return determines whether to propagate the event on the map.
      * 
      * Parameters:
@@ -311,8 +312,8 @@
     },
     
     /**
-     * Method: mouseup
-     * Handle mouse up.  Send the latest point in the geometry to
+     * Method: up
+     * Handle mouseup and touchend.  Send the latest point in the geometry to
      * the control. Return determines whether to propagate the event on the map.
      * 
      * Parameters:

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Handler/Point.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -219,6 +219,7 @@
             this.layer.destroy(false);
         }
         this.layer = null;
+        this.touch = false;
         return true;
     },
     
@@ -385,9 +386,6 @@
      * {Boolean} Allow event propagation
      */
     mousedown: function(evt) {
-        if (this.touch) {
-            return;
-        }
         return this.down(evt);
     },
 
@@ -402,7 +400,18 @@
      * {Boolean} Allow event propagation
      */
     touchstart: function(evt) {
-        this.touch = true;
+        if (!this.touch) {
+            this.touch = true;
+            // unregister mouse listeners
+            this.map.events.un({
+                mousedown: this.mousedown,
+                mouseup: this.mouseup,
+                mousemove: this.mousemove,
+                click: this.click,
+                dblclick: this.dblclick,
+                scope: this
+            });
+        }
         this.lastTouchPx = evt.xy;
         return this.down(evt);
     },
@@ -418,9 +427,6 @@
      * {Boolean} Allow event propagation
      */
     mousemove: function(evt) {
-        if (this.touch) {
-            return;
-        }
         return this.move(evt);
     },
 
@@ -450,9 +456,6 @@
      * {Boolean} Allow event propagation
      */
     mouseup: function(evt) {
-        if (this.touch) {
-            return;
-        }
         return this.up(evt);
     },
 
@@ -472,8 +475,8 @@
     },
   
     /**
-     * Method: mousedown
-     * Handle mouse down.  Adjust the geometry and redraw.
+     * Method: down
+     * Handle mousedown and touchstart.  Adjust the geometry and redraw.
      * Return determines whether to propagate the event on the map.
      * 
      * Parameters:
@@ -493,8 +496,8 @@
     },
 
     /**
-     * Method: mousemove
-     * Handle mouse move.  Adjust the geometry and redraw.
+     * Method: move
+     * Handle mousemove and touchmove.  Adjust the geometry and redraw.
      * Return determines whether to propagate the event on the map.
      * 
      * Parameters:
@@ -512,8 +515,8 @@
     },
 
     /**
-     * Method: mouseup
-     * Handle mouse up.  Send the latest point in the geometry to the control.
+     * Method: up
+     * Handle mouseup and touchend.  Send the latest point in the geometry to the control.
      * Return determines whether to propagate the event on the map.
      *
      * Parameters:

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Layer/Vector.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -810,7 +810,9 @@
             }
         }
         
-        if (!this.renderer.drawFeature(feature, style)) {
+        var drawn = this.renderer.drawFeature(feature, style);
+        //TODO remove the check for null when we get rid of Renderer.SVG
+        if (drawn === false || drawn === null) {
             this.unrenderedFeatures[feature.id] = feature;
         } else {
             delete this.unrenderedFeatures[feature.id];

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Map.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Map.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Map.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -1849,7 +1849,7 @@
             
             bounds = this.baseLayer.getExtent();
             
-            for (var i=0, len=this.layers.length; i<len; i++) {
+            for (var i=this.layers.length-1; i>=0; --i) {
                 var layer = this.layers[i];
                 if (layer !== this.baseLayer && !layer.isBaseLayer) {
                     var inRange = layer.calculateInRange();

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/Canvas.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -9,7 +9,7 @@
 
 /**
  * Class: OpenLayers.Renderer.Canvas 
- * A renderer based on the 2D 'canvas' drawing element.element
+ * A renderer based on the 2D 'canvas' drawing element.
  * 
  * Inherits:
  *  - <OpenLayers.Renderer>
@@ -18,7 +18,7 @@
     
     /**
      * APIProperty: hitDetection
-     * {Boolean} Allow for hit detection of features.  Default is false.
+     * {Boolean} Allow for hit detection of features.  Default is true.
      */
     hitDetection: true,
     
@@ -129,16 +129,25 @@
      * Parameters:
      * feature - {<OpenLayers.Feature.Vector>} 
      * style - {<Object>} 
+     *
+     * Returns:
+     * {Boolean} The feature has been drawn completely.  If the feature has no
+     *     geometry, undefined will be returned.  If the feature is not rendered
+     *     for other reasons, false will be returned.
      */
     drawFeature: function(feature, style) {
-        style = style || feature.style;
-        style = this.applyDefaultSymbolizer(style);  
-        
-        this.features[feature.id] = [feature, style]; 
-        this.redraw();
+        var rendered;
+        if (feature.geometry) {
+            style = style || feature.style;
+            style = this.applyDefaultSymbolizer(style);  
+
+            this.features[feature.id] = [feature, style]; 
+            this.redraw();
+            rendered = true;
+        }
+        return rendered;
     },
 
-
     /** 
      * Method: drawGeometry
      * Used when looping (in redraw) over the features; draws
@@ -515,42 +524,61 @@
             labelAlign: "cm"
         }, style);
         var pt = this.getLocalXY(location);
-        
+
         this.setCanvasStyle("reset");
         this.canvas.fillStyle = style.fontColor;
         this.canvas.globalAlpha = style.fontOpacity || 1.0;
         var fontStyle = [style.fontStyle ? style.fontStyle : "normal",
                          "normal", // "font-variant" not supported
                          style.fontWeight ? style.fontWeight : "normal",
-                         style.fontSize ? style.fontSize : "10px",
+                         style.fontSize ? style.fontSize : "1em",
                          style.fontFamily ? style.fontFamily : "sans-serif"].join(" ");
+        var labelRows = style.label.split('\n');
+        var numRows = labelRows.length;
         if (this.canvas.fillText) {
             // HTML5
-            var labelAlign =
+            this.canvas.font = fontStyle;
+            this.canvas.textAlign =
                 OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[0]] ||
                 "center";
-            this.canvas.font = fontStyle;
-            this.canvas.textAlign = labelAlign;
-            this.canvas.fillText(style.label, pt[0], pt[1]);
+            this.canvas.textBaseline =
+                OpenLayers.Renderer.Canvas.LABEL_ALIGN[style.labelAlign[1]] ||
+                "middle";
+            var vfactor =
+                OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
+            if (vfactor == null) {
+                vfactor = -.5;
+            }
+            var lineHeight =
+                this.canvas.measureText('Mg').height ||
+                this.canvas.measureText('xx').width;
+            pt[1] += lineHeight*vfactor*(numRows-1);
+            for (var i = 0; i < numRows; i++) {
+                this.canvas.fillText(labelRows[i], pt[0], pt[1] + (lineHeight*i));
+            }
         } else if (this.canvas.mozDrawText) {
             // Mozilla pre-Gecko1.9.1 (<FF3.1)
             this.canvas.mozTextStyle = fontStyle;
             // No built-in text alignment, so we measure and adjust the position
-            var len = this.canvas.mozMeasureText(style.label);
-            switch(style.labelAlign[0]) {
-                case "l":
-                    break;
-                case "r":
-                    pt[0] -= len;
-                    break;
-                case "c":
-                default:
-                    pt[0] -= len / 2;
+            var hfactor =
+                OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[0]];
+            if (hfactor == null) {
+                hfactor = -.5;
             }
-            this.canvas.translate(pt[0], pt[1]);
-            
-            this.canvas.mozDrawText(style.label);
-            this.canvas.translate(-1*pt[0], -1*pt[1]);
+            var vfactor =
+                OpenLayers.Renderer.Canvas.LABEL_FACTOR[style.labelAlign[1]];
+            if (vfactor == null) {
+                vfactor = -.5;
+            }
+            var lineHeight = this.canvas.mozMeasureText('xx');
+            pt[1] += lineHeight*(1 + (vfactor*numRows));
+            for (var i = 0; i < numRows; i++) {
+                var x = pt[0] + (hfactor*this.canvas.mozMeasureText(labelRows[i]));
+                var y = pt[1] + (i*lineHeight);
+                this.canvas.translate(x, y);
+                this.canvas.mozDrawText(labelRows[i]);
+                this.canvas.translate(-x, -y);
+            }
         }
         this.setCanvasStyle("reset");
     },
@@ -592,7 +620,9 @@
      * evt - {<OpenLayers.Event>} 
      *
      * Returns:
-     * {String} A feature id or null.
+     * {<OpenLayers.Feature.Vector} A feature or null.  This method returns a 
+     *     feature instead of a feature id to avoid an unnecessary lookup on the
+     *     layer.
      */
     getFeatureIdFromEvent: function(evt) {
         var feature = null;
@@ -677,5 +707,18 @@
  */
 OpenLayers.Renderer.Canvas.LABEL_ALIGN = {
     "l": "left",
-    "r": "right"
+    "r": "right",
+    "t": "top",
+    "b": "bottom"
 };
+
+/**
+ * Constant: OpenLayers.Renderer.Canvas.LABEL_FACTOR
+ * {Object}
+ */
+OpenLayers.Renderer.Canvas.LABEL_FACTOR = {
+    "l": 0,
+    "r": -1,
+    "t": 0,
+    "b": -1
+};

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -679,24 +679,23 @@
     /**
      * Method: drawText
      * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
+     *
+     * Parameters:
      * featureId - {String}
      * style -
      * location - {<OpenLayers.Geometry.Point>}
      */
     drawText: function(featureId, style, location) {
         var resolution = this.getResolution();
-        
+
         var x = (location.x / resolution + this.left);
         var y = (location.y / resolution - this.top);
-        
+
         var label = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX, "text");
-        var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan", "tspan");
 
         label.setAttributeNS(null, "x", x);
         label.setAttributeNS(null, "y", -y);
-        
+
         if (style.fontColor) {
             label.setAttributeNS(null, "fill", style.fontColor);
         }
@@ -715,12 +714,9 @@
         if (style.fontStyle) {
             label.setAttributeNS(null, "font-style", style.fontStyle);
         }
-        if(style.labelSelect === true) {
+        if (style.labelSelect === true) {
             label.setAttributeNS(null, "pointer-events", "visible");
             label._featureId = featureId;
-            tspan._featureId = featureId;
-            tspan._geometry = location;
-            tspan._geometryClass = location.CLASS_NAME;
         } else {
             label.setAttributeNS(null, "pointer-events", "none");
         }
@@ -731,17 +727,43 @@
         if (OpenLayers.IS_GECKO === true) {
             label.setAttributeNS(null, "dominant-baseline",
                 OpenLayers.Renderer.SVG.LABEL_ALIGN[align[1]] || "central");
-        } else {
-            tspan.setAttributeNS(null, "baseline-shift",
-                OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
         }
 
-        tspan.textContent = style.label;
-        
-        if(!label.parentNode) {
-            label.appendChild(tspan);
+        var labelRows = style.label.split('\n');
+        var numRows = labelRows.length;
+        while (label.childNodes.length > numRows) {
+            label.removeChild(label.lastChild);
+        }
+        for (var i = 0; i < numRows; i++) {
+            var tspan = this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
+            if (style.labelSelect === true) {
+                tspan._featureId = featureId;
+                tspan._geometry = location;
+                tspan._geometryClass = location.CLASS_NAME;
+            }
+            if (OpenLayers.IS_GECKO === false) {
+                tspan.setAttributeNS(null, "baseline-shift",
+                    OpenLayers.Renderer.SVG.LABEL_VSHIFT[align[1]] || "-35%");
+            }
+            tspan.setAttribute("x", x);
+            if (i == 0) {
+                var vfactor = OpenLayers.Renderer.SVG.LABEL_VFACTOR[align[1]];
+                if (vfactor == null) {
+                     vfactor = -.5;
+                }
+                tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
+            } else {
+                tspan.setAttribute("dy", "1em");
+            }
+            tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
+            if (!tspan.parentNode) {
+                label.appendChild(tspan);
+            }
+        }
+
+        if (!label.parentNode) {
             this.textRoot.appendChild(label);
-        }   
+        }
     },
     
     /** 
@@ -989,6 +1011,15 @@
 };
 
 /**
+ * Constant: OpenLayers.Renderer.SVG.LABEL_VFACTOR
+ * {Object}
+ */
+OpenLayers.Renderer.SVG.LABEL_VFACTOR = {
+    "t": 0,
+    "b": -1
+};
+
+/**
  * 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

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Renderer/SVG2.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -570,8 +570,8 @@
      * Method: drawText
      * Function for drawing text labels.
      * This method is only called by the renderer itself.
-     * 
-     * Parameters: 
+     *
+     * Parameters:
      * featureId - {String|DOMElement}
      * style - {Object}
      * location - {<OpenLayers.Geometry.Point>}, will be modified inline
@@ -583,14 +583,12 @@
         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);
         }
@@ -609,10 +607,9 @@
         if (style.fontStyle) {
             text.setAttributeNS(null, "font-style", style.fontStyle);
         }
-        if(style.labelSelect === true) {
+        if (style.labelSelect === true) {
             text.setAttributeNS(null, "pointer-events", "visible");
             text._featureId = featureId;
-            tspan._featureId = featureId;
         } else {
             text.setAttributeNS(null, "pointer-events", "none");
         }
@@ -623,18 +620,43 @@
         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);
+        var labelRows = style.label.split('\n');
+        var numRows = labelRows.length;
+        while (text.childNodes.length > numRows) {
+            text.removeChild(text.lastChild);
+        }
+        for (var i = 0; i < numRows; i++) {
+            var tspan = text.childNodes[i] ||
+                this.nodeFactory(featureId + this.LABEL_ID_SUFFIX + "_tspan_" + i, "tspan");
+            if (style.labelSelect === true) {
+                tspan._featureId = featureId;
+            }
+            if (OpenLayers.IS_GECKO === false) {
+                tspan.setAttributeNS(null, "baseline-shift",
+                    OpenLayers.Renderer.SVG2.LABEL_VSHIFT[align[1]] || "-35%");
+            }
+            tspan.setAttribute("x", location.x / res);
+            if (i == 0) {
+                var vfactor = OpenLayers.Renderer.SVG2.LABEL_VFACTOR[align[1]];
+                if (vfactor == null) {
+                    vfactor = -.5;
+                }
+                tspan.setAttribute("dy", (vfactor*(numRows-1)) + "em");
+            } else {
+                tspan.setAttribute("dy", "1em");
+            }
+            tspan.textContent = (labelRows[i] === '') ? ' ' : labelRows[i];
+            if (!tspan.parentNode) {
+                text.appendChild(tspan);
+            }
+        }
+
+        if (!text.parentNode) {
             g.appendChild(text);
         }
-        
+
         return g;
     },
     
@@ -786,6 +808,15 @@
     "b": "0"    
 };
 
+/**
+ * Constant: OpenLayers.Renderer.SVG2.LABEL_VFACTOR
+ * {Object}
+ */
+OpenLayers.Renderer.SVG2.LABEL_VFACTOR = {
+    "t": 0,
+    "b": -1
+};
+
 /** 
  * Function: OpenLayers.Renderer.SVG2.preventDefault 
  * Used to prevent default events (especially opening images in a new tab on 

Modified: sandbox/tschaub/canvas/lib/OpenLayers/Util.js
===================================================================
--- sandbox/tschaub/canvas/lib/OpenLayers/Util.js	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/lib/OpenLayers/Util.js	2011-04-01 22:40:36 UTC (rev 11857)
@@ -1614,7 +1614,7 @@
  *     * "opera" -- Opera
  *     * "msie"  -- Internet Explorer
  *     * "safari" -- Safari
- *     * "firefox" -- FireFox
+ *     * "firefox" -- Firefox
  *     * "mozilla" -- Mozilla
  */
 OpenLayers.BROWSER_NAME = (function() {
@@ -1647,7 +1647,7 @@
  *           * 'opera' -- Opera
  *           * 'msie'  -- Internet Explorer
  *           * 'safari' -- Safari
- *           * 'firefox' -- FireFox
+ *           * 'firefox' -- Firefox
  *           * 'mozilla' -- Mozilla
  * 
  *          If we are unable to property identify the browser, we 

Modified: sandbox/tschaub/canvas/tests/Control/DragFeature.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/DragFeature.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Control/DragFeature.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -103,6 +103,41 @@
              "onEnter called with expected feature");
     }
 
+    function test_Control_DragFeature_over_touch(t) {
+        t.plan(7);
+        var log = [];
+        var map = new OpenLayers.Map("map");
+        var layer = new OpenLayers.Layer.Vector();
+        map.addLayer(layer);
+        var control = new OpenLayers.Control.DragFeature(layer, {
+            onEnter: function(f) { log.push({feature: f}); }
+        });
+        map.addControl(control);
+
+        control.activate();
+        t.ok(!control.handlers.drag.active,
+             "drag handler is not active before touch on a feature");
+
+        // simulate a touch on a feature
+        var feature = new OpenLayers.Feature.Vector();
+        feature.layer = layer;
+        layer.getFeatureFromEvent = function(evt) {
+            return feature;
+        }
+        map.events.triggerEvent("touchstart", {type: "touchstart", touches: ['foo']});
+
+        t.eq(control.feature.id, feature.id,
+             "control gets the proper feature from the feature handler");
+        t.ok(control.handlers.drag.active,
+             "drag handler activated when touch on a feature");
+        t.ok(control.handlers.drag.started, "drag handler has started");
+        t.ok(!control.handlers.drag.stopDown, "drag handler is not stopping down");
+        t.eq(log.length, 1,
+             "onEnter called exactly once");
+        t.eq(log[0].feature.id, feature.id,
+             "onEnter called with expected feature");
+    }
+
     function test_Control_DragFeature_down(t) {
         t.plan(3);
         var map = new OpenLayers.Map("map");
@@ -284,6 +319,44 @@
              "onLeave called with expected feature");
     }
 
+    function test_Control_DragFeature_out_touch(t) {
+        t.plan(5);
+        var log = [];
+        var map = new OpenLayers.Map("map");
+        var layer = new OpenLayers.Layer.Vector();
+        map.addLayer(layer);
+        var control = new OpenLayers.Control.DragFeature(layer, {
+            onLeave: function(f) { log.push({feature: f}); }
+        });
+        map.addControl(control);
+
+        control.activate();
+
+        // simulate a touch on a feature
+        var feature = new OpenLayers.Feature.Vector();
+        feature.layer = layer;
+        layer.getFeatureFromEvent = function() {
+            return feature;
+        };
+        map.events.triggerEvent("touchstart", {type: "touchstart", touches: ['foo']});
+        t.eq(control.feature.id, feature.id,
+             "feature is set on mouse over");
+
+        // simulate a touch outside the feature
+        layer.getFeatureFromEvent = function() {
+            return null;
+        };
+        map.events.triggerEvent("touchstart", {type: "touchstart", touches: ['foo']});
+        t.ok(control.feature == null,
+             "feature is set to null on mouse out");
+        t.ok(control.handlers.drag.stopDown,
+             "drag handler is stopping down again");
+        t.eq(log.length, 1,
+             "onLeave called exactly once");
+        t.eq(log[0].feature.id, feature.id,
+             "onLeave called with expected feature");
+    }
+
     </script>
 </head>
 <body>

Modified: sandbox/tschaub/canvas/tests/Control/Measure.html
===================================================================
--- sandbox/tschaub/canvas/tests/Control/Measure.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Control/Measure.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -19,7 +19,6 @@
         
     }
     
-    // test for <http://trac.openlayers.org/ticket/2691>
     function test_cancel(t) {
         
         t.plan(4);
@@ -37,7 +36,7 @@
         map.addControl(control);
         
         control.activate();
-        
+
         try {
             control.cancel();
             t.ok(true, "calling cancel before drawing works");
@@ -52,24 +51,29 @@
                 xy: new OpenLayers.Pixel(x, y)
             })
         };
+
+        // keep a reference to the line being drawn
+        var line = control.handler.line;
+
         trigger("mousemove", 0, 0);
         trigger("mousedown", 0, 0);
         trigger("mouseup", 0, 0);
         trigger("mousemove", 10, 10);
         trigger("mousedown", 10, 10);
         trigger("mouseup", 10, 10);
+        trigger("dblclick", 10, 10);
+
+        // the geometry is finalized, we first confirm that it is persisted
+        t.ok(line.layer === control.handler.layer, "feature persists");
         
-        // confirm that the sketch persists
-        t.eq(control.handler.layer.features.length, 1, "feature persists");
-        // cancel and see that sketch is gone (do not forget that
-        // cancel will create the new feature)
+        // cancel and see that sketch is gone
         control.cancel();
-        t.eq(control.handler.layer.features.length, 2, "feature is gone after cancel");
-        
+        t.eq(line.layer, null, "feature is gone after cancel");
+
         map.destroy();
-        
     }
 
+    // test for <http://trac.openlayers.org/ticket/2691>
     function test_partial(t) {        
 
         t.plan(28);        

Modified: sandbox/tschaub/canvas/tests/Geometry/LinearRing.html
===================================================================
--- sandbox/tschaub/canvas/tests/Geometry/LinearRing.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Geometry/LinearRing.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -58,7 +58,7 @@
     }
     
     function test_LinearRing_removeComponent(t) {
-        t.plan(11);
+        t.plan(10);
         
         var components = [new OpenLayers.Geometry.Point(0,0), 
                     new OpenLayers.Geometry.Point(0,10),
@@ -71,7 +71,7 @@
         t.eq(ring.components.length, 4, "removing from linear ring with 5 points: length ok");
         t.ok(ring.components[0].equals(components[0]), "point one correct");
         t.ok(ring.components[1].equals(components[1]), "point two correct");
-        t.ok(ring.components[2].equals(components[3]), "point one correct");
+        t.ok(ring.components[2].equals(components[3]), "point three correct");
         t.ok(ring.components[0] === ring.components[ring.components.length - 1],
              "first and last point are the same");
  
@@ -80,10 +80,10 @@
         t.ok(ringBounds.equals(testBounds), "bounds correctly recalculated");
         
         ring.removeComponent( ring.components[2] );
-        t.eq(ring.components.length, 4, "cant remove from linear ring with only 4 points. new length ok (unchanged)");
+        ring.removeComponent( ring.components[1] );
+        t.eq(ring.components.length, 3, "cant remove from linear ring with only 3 points. new length ok");
         t.ok(ring.components[0].equals(components[0]), "point one correct");
         t.ok(ring.components[1].equals(components[1]), "point two correct");
-        t.ok(ring.components[2].equals(components[3]), "point one correct");
         t.ok(ring.components[0] === ring.components[ring.components.length - 1],
              "first and last point are the same");
         

Modified: sandbox/tschaub/canvas/tests/Handler/Feature.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Feature.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Handler/Feature.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -53,7 +53,7 @@
         
     }
     function test_events(t) {
-        t.plan(30);
+        t.plan(35);
         
         var map = new OpenLayers.Map('map');
         var control = new OpenLayers.Control();
@@ -64,8 +64,8 @@
  
         // list below events that should be handled (events) and those
         // that should not be handled (nonevents) by the handler
-        var events = ["mousedown", "mouseup", "mousemove", "click", "dblclick", "touchstart"];
-        var nonevents = ["mouseout", "resize", "focus", "blur"];
+        var events = ["mousedown", "mouseup", "mousemove", "click", "dblclick", "touchstart", "touchmove"];
+        var nonevents = ["mouseout", "resize", "focus", "blur", "touchend"];
         map.events.registerPriority = function(type, obj, func) {
             var output = func();
             // Don't listen for setEvent handlers (#902)
@@ -255,6 +255,82 @@
         map.events.triggerEvent('touchstart', evtPx);
     }
 
+    function test_touchstart(t) {
+        // a test to verify that the touchstart function does
+        // unregister the mouse listeners when it's called the
+        // first time
+
+        t.plan(4);
+
+        // set up
+
+        var map = new OpenLayers.Map('map', {controls: []});
+        var control = new OpenLayers.Control();
+        map.addControl(control);
+        var layer = new OpenLayers.Layer();
+        map.addLayer(layer);
+
+        var handler = new OpenLayers.Handler.Feature(control, layer, {});
+        handler.mousedown = function() {}; // mock mousedown
+        handler.activate();
+
+        function allRegistered() {
+            var eventTypes = ['mousedown', 'mouseup', 'mousemove', 'click', 'dblclick'],
+                eventType,
+                listeners,
+                listener,
+                flag;
+            for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+                flag =  false;
+                eventType = eventTypes[i];
+                listeners = map.events.listeners[eventType];
+                for(var j=0, jlen=listeners.length; j<jlen; j++) {
+                    listener = listeners[j];
+                    if(listener.func === handler[eventType] && listener.obj === handler) {
+                        flag = true;
+                        break;
+                    }
+                }
+                if(!flag) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        function noneRegistered() {
+            var eventTypes = ['mousedown', 'mouseup', 'mousemove', 'click', 'dblclick'],
+                eventType,
+                listeners,
+                listener;
+            for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+                eventType = eventTypes[i];
+                listeners = map.events.listeners[eventType];
+                for(var j=0, jlen=listeners.length; j<jlen; j++) {
+                    listener = listeners[j];
+                    if(listener.func === handler[eventType] && listener.obj === handler) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        // test
+
+        t.ok(allRegistered(), 'mouse listeners are registered');
+        handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+        t.ok(noneRegistered(), 'mouse listeners are unregistered');
+        t.ok(handler.touch, 'handler.touch is set');
+
+        handler.deactivate();
+        t.ok(!handler.touch, 'handler.touch is not set');
+
+        // tear down
+
+        map.destroy();
+    }
+
     function test_deactivate(t) {
         t.plan(3);
         var map = new OpenLayers.Map('map');

Modified: sandbox/tschaub/canvas/tests/Handler/Path.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Path.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Handler/Path.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -105,7 +105,7 @@
     }
 
     function test_bounds(t) {
-        t.plan(2);
+        t.plan(5);
         var geometry;
         var map = new OpenLayers.Map('map');
         map.addLayer(new OpenLayers.Layer.WMS("", "", {}));
@@ -115,16 +115,22 @@
         var handler = new OpenLayers.Handler.Path(control, {},
             {stopDown: true, stopUp: true});
         var activated = handler.activate();
+        t.eq(handler.layer.features.length, 2,
+            "There are two features in the layer after activation.");
         // click on (150, 75)
         var evt = {xy: new OpenLayers.Pixel(150, 75), which: 1};
         handler.mousemove(evt);
         handler.mousedown(evt);
         handler.mouseup(evt);
+        t.eq(handler.layer.features.length, 2,
+            "There are two features in the layer after first click.");
         // click on (175, 100)
         evt = {xy: new OpenLayers.Pixel(175, 100), which: 1};
         handler.mousemove(evt);
         handler.mousedown(evt);
         handler.mouseup(evt);
+        t.eq(handler.layer.features.length, 2,
+            "There are two features in the layer after second click.");
         t.ok(handler.line.geometry.getBounds().equals(
                     new OpenLayers.Bounds(0,-35.15625,35.15625,0)),
              "Correct bounds");

Modified: sandbox/tschaub/canvas/tests/Handler/Point.html
===================================================================
--- sandbox/tschaub/canvas/tests/Handler/Point.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Handler/Point.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -417,6 +417,88 @@
              "handler.point is null after destroy");
     }
 
+    function test_touchstart(t) {
+        // a test to verify that the touchstart function does
+        // unregister the mouse listeners when it's called the
+        // first time
+
+        t.plan(4);
+
+        // set up
+
+        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);
+        map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+        handler.activate();
+
+        function allRegistered() {
+            var eventTypes = ['mousedown', 'mouseup', 'mousemove', 'click', 'dblclick'],
+                eventType,
+                listeners,
+                listener,
+                flag;
+            for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+                flag =  false;
+                eventType = eventTypes[i];
+                listeners = map.events.listeners[eventType];
+                for(var j=0, jlen=listeners.length; j<jlen; j++) {
+                    listener = listeners[j];
+                    if(listener.func === handler[eventType] && listener.obj === handler) {
+                        flag = true;
+                        break;
+                    }
+                }
+                if(!flag) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        function noneRegistered() {
+            var eventTypes = ['mousedown', 'mouseup', 'mousemove', 'click', 'dblclick'],
+                eventType,
+                listeners,
+                listener;
+            for(var i=0, ilen=eventTypes.length; i<ilen; i++) {
+                eventType = eventTypes[i];
+                listeners = map.events.listeners[eventType];
+                for(var j=0, jlen=listeners.length; j<jlen; j++) {
+                    listener = listeners[j];
+                    if(listener.func === handler[eventType] && listener.obj === handler) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+
+        // test
+
+        t.ok(allRegistered(), 'mouse listeners are registered');
+        handler.touchstart({xy: new OpenLayers.Pixel(0, 0)});
+        t.ok(noneRegistered(), 'mouse listeners are unregistered');
+        t.ok(handler.touch, 'handler.touch is set');
+
+        handler.deactivate();
+        t.ok(!handler.touch, 'handler.touch is not set');
+
+        // tear down
+
+        map.destroy();
+    }
+
+
     //
     // Sequence tests
     // 

Modified: sandbox/tschaub/canvas/tests/Renderer/Canvas.html
===================================================================
--- sandbox/tschaub/canvas/tests/Renderer/Canvas.html	2011-04-01 21:37:06 UTC (rev 11856)
+++ sandbox/tschaub/canvas/tests/Renderer/Canvas.html	2011-04-01 22:40:36 UTC (rev 11857)
@@ -12,6 +12,7 @@
         
         t.ok(r instanceof OpenLayers.Renderer.Canvas, "new OpenLayers.Renderer.Canvas returns Renderer.Canvas object" );
         t.ok(r.container == el, "renderer container is correctly set");
+        r.destroy();
     }
     
     function test_Renderer_Canvas_setextent(t) {
@@ -26,6 +27,7 @@
         r.setExtent(extent, true);
         t.ok(r.extent.equals(extent), "extent is correctly set");
         t.eq(r.resolution, null, "resolution nullified");
+        r.destroy();
     }
     
     function test_Renderer_Canvas_setsize(t) {
@@ -40,6 +42,7 @@
         r.setSize(size);
         t.ok(r.size.equals(size), "size is correctly set");
         t.eq(r.resolution, null, "resolution nullified");
+        r.destroy();
     }
     
     function test_Renderer_Canvas_getresolution(t) {
@@ -54,6 +57,7 @@
         var resolution = r.getResolution();
         t.eq(resolution, map.getResolution(), "resolution matches the map resolution");
         t.eq(r.resolution, resolution, "resolution is correctly set");
+        map.destroy();
     }
 
     function test_featureIdToRGB(t) {
@@ -224,6 +228,8 @@
             t.eq(feature && feature.id, c.id, c.msg);
         }
         
+        map.destroy();
+        
     }
 
   </script>



More information about the Commits mailing list