[OpenLayers-Commits] r11694 - in sandbox/tschaub/click: . examples lib lib/OpenLayers/Control lib/OpenLayers/Format lib/OpenLayers/Format/WFSCapabilities lib/OpenLayers/Layer lib/OpenLayers/Protocol lib/OpenLayers/Protocol/WFS tests tests/Control tests/Format tests/Format/WFSCapabilities tests/Layer tests/Protocol

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Thu Mar 10 20:29:19 EST 2011


Author: tschaub
Date: 2011-03-10 17:29:19 -0800 (Thu, 10 Mar 2011)
New Revision: 11694

Added:
   sandbox/tschaub/click/examples/arcgiscache_ags.html
   sandbox/tschaub/click/examples/arcgiscache_direct.html
   sandbox/tschaub/click/examples/arcgiscache_jsonp.html
   sandbox/tschaub/click/examples/cross-origin.html
   sandbox/tschaub/click/examples/cross-origin.js
   sandbox/tschaub/click/lib/OpenLayers/Layer/ArcGISCache.js
   sandbox/tschaub/click/lib/OpenLayers/Protocol/Script.js
   sandbox/tschaub/click/lib/OpenLayers/Protocol/SimpleFilterSerializer.js
   sandbox/tschaub/click/tests/Layer/ArcGISCache.html
   sandbox/tschaub/click/tests/Layer/ArcGISCache.json
   sandbox/tschaub/click/tests/Protocol/Script.html
   sandbox/tschaub/click/tests/Protocol/SimpleFilterSerializer.html
Modified:
   sandbox/tschaub/click/
   sandbox/tschaub/click/examples/example-list.html
   sandbox/tschaub/click/examples/fullScreen.js
   sandbox/tschaub/click/lib/OpenLayers.js
   sandbox/tschaub/click/lib/OpenLayers/Control/KeyboardDefaults.js
   sandbox/tschaub/click/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js
   sandbox/tschaub/click/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js
   sandbox/tschaub/click/lib/OpenLayers/Format/WMSGetFeatureInfo.js
   sandbox/tschaub/click/lib/OpenLayers/Layer/KaMap.js
   sandbox/tschaub/click/lib/OpenLayers/Layer/MapGuide.js
   sandbox/tschaub/click/lib/OpenLayers/Layer/Zoomify.js
   sandbox/tschaub/click/lib/OpenLayers/Protocol/HTTP.js
   sandbox/tschaub/click/lib/OpenLayers/Protocol/WFS/v1.js
   sandbox/tschaub/click/tests/Control/KeyboardDefaults.html
   sandbox/tschaub/click/tests/Format/WFSCapabilities/v1.html
   sandbox/tschaub/click/tests/Format/WMSGetFeatureInfo.html
   sandbox/tschaub/click/tests/Protocol/HTTP.html
   sandbox/tschaub/click/tests/list-tests.html
Log:
Merging r11672:11693 from trunk.


Property changes on: sandbox/tschaub/click
___________________________________________________________________
Modified: svn:mergeinfo
   - /sandbox/roberthl/openlayers:9745-9748
/trunk/openlayers:11572-11671
   + /sandbox/roberthl/openlayers:9745-9748
/trunk/openlayers:11572-11693

Copied: sandbox/tschaub/click/examples/arcgiscache_ags.html (from rev 11693, trunk/openlayers/examples/arcgiscache_ags.html)
===================================================================
--- sandbox/tschaub/click/examples/arcgiscache_ags.html	                        (rev 0)
+++ sandbox/tschaub/click/examples/arcgiscache_ags.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/examples/arcgiscache_direct.html (from rev 11693, trunk/openlayers/examples/arcgiscache_direct.html)
===================================================================
--- sandbox/tschaub/click/examples/arcgiscache_direct.html	                        (rev 0)
+++ sandbox/tschaub/click/examples/arcgiscache_direct.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/examples/arcgiscache_jsonp.html (from rev 11693, trunk/openlayers/examples/arcgiscache_jsonp.html)
===================================================================
--- sandbox/tschaub/click/examples/arcgiscache_jsonp.html	                        (rev 0)
+++ sandbox/tschaub/click/examples/arcgiscache_jsonp.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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>

Copied: sandbox/tschaub/click/examples/cross-origin.html (from rev 11693, trunk/openlayers/examples/cross-origin.html)
===================================================================
--- sandbox/tschaub/click/examples/cross-origin.html	                        (rev 0)
+++ sandbox/tschaub/click/examples/cross-origin.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/examples/cross-origin.js (from rev 11693, trunk/openlayers/examples/cross-origin.js)
===================================================================
--- sandbox/tschaub/click/examples/cross-origin.js	                        (rev 0)
+++ sandbox/tschaub/click/examples/cross-origin.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/examples/example-list.html
===================================================================
--- sandbox/tschaub/click/examples/example-list.html	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/examples/example-list.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -101,7 +101,7 @@
             }
             #examples .mainlink {
                 height: 8em;
-                overflow: hidden;
+                overflow: auto;
             }
             #exwin {
                 position: absolute;

Modified: sandbox/tschaub/click/examples/fullScreen.js
===================================================================
--- sandbox/tschaub/click/examples/fullScreen.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/examples/fullScreen.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/lib/OpenLayers/Control/KeyboardDefaults.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Control/KeyboardDefaults.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Control/KeyboardDefaults.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -37,19 +37,7 @@
     /**
      * Constructor: OpenLayers.Control.KeyboardDefaults
      */
-    
-    /**
-     * 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/click/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Format/WFSCapabilities/v1_0_0.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Format/WFSCapabilities/v1_1_0.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/lib/OpenLayers/Format/WMSGetFeatureInfo.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Format/WMSGetFeatureInfo.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Format/WMSGetFeatureInfo.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -230,12 +230,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;

Copied: sandbox/tschaub/click/lib/OpenLayers/Layer/ArcGISCache.js (from rev 11693, trunk/openlayers/lib/OpenLayers/Layer/ArcGISCache.js)
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Layer/ArcGISCache.js	                        (rev 0)
+++ sandbox/tschaub/click/lib/OpenLayers/Layer/ArcGISCache.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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.floor((point.x - this.tileOrigin.lon) / (this.tileSize.w * res)),
+           Math.floor((this.tileOrigin.lat - point.y) / (this.tileSize.h * res))
+        );
+    },
+    
+   /** 
+    * 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 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/click/lib/OpenLayers/Layer/KaMap.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Layer/KaMap.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Layer/KaMap.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -102,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/click/lib/OpenLayers/Layer/MapGuide.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Layer/MapGuide.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Layer/MapGuide.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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.  
@@ -439,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/click/lib/OpenLayers/Layer/Zoomify.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Layer/Zoomify.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Layer/Zoomify.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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)
@@ -258,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/click/lib/OpenLayers/Protocol/HTTP.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Protocol/HTTP.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Protocol/HTTP.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -14,6 +14,11 @@
  */
 
 /**
+ * TODO: remove this dependency in 3.0
+ * @requires OpenLayers/Protocol/SimpleFilterSerializer.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,12 @@
         this.params = {};
         this.headers = {};
         OpenLayers.Protocol.prototype.initialize.apply(this, arguments);
+
+        if (!this.filterToParams && OpenLayers.Protocol.simpleFilterSerializer) {
+            this.filterToParams = OpenLayers.Function.bind(
+                OpenLayers.Protocol.simpleFilterSerializer, this
+            );
+        }
     },
     
     /**
@@ -120,8 +131,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 +174,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 +217,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 +557,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/click/lib/OpenLayers/Protocol/Script.js (from rev 11693, trunk/openlayers/lib/OpenLayers/Protocol/Script.js)
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Protocol/Script.js	                        (rev 0)
+++ sandbox/tschaub/click/lib/OpenLayers/Protocol/Script.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -0,0 +1,363 @@
+/* Copyright (c) 2006-2010 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,
+
+    /**
+     * 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.Protocol.simpleFilterSerializer) {
+            this.filterToParams = OpenLayers.Function.bind(
+                OpenLayers.Protocol.simpleFilterSerializer, this
+            );
+        }
+    },
+    
+    /**
+     * 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;
+    };
+})();
+

Copied: sandbox/tschaub/click/lib/OpenLayers/Protocol/SimpleFilterSerializer.js (from rev 11693, trunk/openlayers/lib/OpenLayers/Protocol/SimpleFilterSerializer.js)
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Protocol/SimpleFilterSerializer.js	                        (rev 0)
+++ sandbox/tschaub/click/lib/OpenLayers/Protocol/SimpleFilterSerializer.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -0,0 +1,145 @@
+/* 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
+ */
+
+/**
+ * Function: OpenLayers.Protocol.simpleFilterSerializer
+ * 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.
+ */
+OpenLayers.Protocol.simpleFilterSerializer = (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 function simpleFilterSerializer(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.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;
+    };
+    
+})();

Modified: sandbox/tschaub/click/lib/OpenLayers/Protocol/WFS/v1.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers/Protocol/WFS/v1.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers/Protocol/WFS/v1.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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);

Modified: sandbox/tschaub/click/lib/OpenLayers.js
===================================================================
--- sandbox/tschaub/click/lib/OpenLayers.js	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/lib/OpenLayers.js	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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",
@@ -245,12 +246,14 @@
                 "OpenLayers/Filter/Spatial.js",
                 "OpenLayers/Protocol.js",
                 "OpenLayers/Protocol/HTTP.js",
+                "OpenLayers/Protocol/SimpleFilterSerializer.js",
                 "OpenLayers/Protocol/SQL.js",
                 "OpenLayers/Protocol/SQL/Gears.js",
                 "OpenLayers/Protocol/WFS.js",
                 "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",

Modified: sandbox/tschaub/click/tests/Control/KeyboardDefaults.html
===================================================================
--- sandbox/tschaub/click/tests/Control/KeyboardDefaults.html	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/tests/Control/KeyboardDefaults.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/tests/Format/WFSCapabilities/v1.html
===================================================================
--- sandbox/tschaub/click/tests/Format/WFSCapabilities/v1.html	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/tests/Format/WFSCapabilities/v1.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/tests/Format/WMSGetFeatureInfo.html
===================================================================
--- sandbox/tschaub/click/tests/Format/WMSGetFeatureInfo.html	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/tests/Format/WMSGetFeatureInfo.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -52,7 +52,7 @@
     }
 
     function test_read_msGMLOutput(t) {
-        t.plan(12);
+        t.plan(13);
         
         var parser = new OpenLayers.Format.WMSGetFeatureInfo();
 
@@ -69,6 +69,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"?>' +

Copied: sandbox/tschaub/click/tests/Layer/ArcGISCache.html (from rev 11693, trunk/openlayers/tests/Layer/ArcGISCache.html)
===================================================================
--- sandbox/tschaub/click/tests/Layer/ArcGISCache.html	                        (rev 0)
+++ sandbox/tschaub/click/tests/Layer/ArcGISCache.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -0,0 +1,208 @@
+<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 ');        
+    }
+
+  </script>
+</head>
+<body>
+<div id="map" style="width:500px;height:550px;"></div>
+</body>
+</html>

Copied: sandbox/tschaub/click/tests/Layer/ArcGISCache.json (from rev 11693, trunk/openlayers/tests/Layer/ArcGISCache.json)
===================================================================
--- sandbox/tschaub/click/tests/Layer/ArcGISCache.json	                        (rev 0)
+++ sandbox/tschaub/click/tests/Layer/ArcGISCache.json	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/tests/Protocol/HTTP.html
===================================================================
--- sandbox/tschaub/click/tests/Protocol/HTTP.html	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/tests/Protocol/HTTP.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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/click/tests/Protocol/Script.html (from rev 11693, trunk/openlayers/tests/Protocol/Script.html)
===================================================================
--- sandbox/tschaub/click/tests/Protocol/Script.html	                        (rev 0)
+++ sandbox/tschaub/click/tests/Protocol/Script.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -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>

Copied: sandbox/tschaub/click/tests/Protocol/SimpleFilterSerializer.html (from rev 11693, trunk/openlayers/tests/Protocol/SimpleFilterSerializer.html)
===================================================================
--- sandbox/tschaub/click/tests/Protocol/SimpleFilterSerializer.html	                        (rev 0)
+++ sandbox/tschaub/click/tests/Protocol/SimpleFilterSerializer.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -0,0 +1,310 @@
+<html>
+<head>
+  <script src="../../lib/OpenLayers.js"></script>
+  <script type="text/javascript">
+
+
+    function test_filterToParams(t) {
+        t.plan(30);
+
+        // setup
+
+        var protocol, filter, params;
+
+        protocol = new OpenLayers.Protocol.HTTP({
+            filterToParams: OpenLayers.Protocol.simpleFilterSerializer
+        });
+
+        // 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({
+            filterToParams: OpenLayers.Protocol.simpleFilterSerializer
+        });
+
+        var value;
+        var filter = new OpenLayers.Filter.Comparison({
+            type: OpenLayers.Filter.Comparison.LIKE,
+            property: "prop"
+        });
+        
+        function serialize(value) {
+            filter.value = value;
+            return protocol.filterToParams(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/click/tests/list-tests.html
===================================================================
--- sandbox/tschaub/click/tests/list-tests.html	2011-03-11 00:54:25 UTC (rev 11693)
+++ sandbox/tschaub/click/tests/list-tests.html	2011-03-11 01:29:19 UTC (rev 11694)
@@ -133,6 +133,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 +178,8 @@
     <li>Projection.html</li>
     <li>Protocol.html</li>
     <li>Protocol/HTTP.html</li>
+    <li>Protocol/Script.html</li>
+    <li>Protocol/SimpleFilterSerializer.html</li>
     <li>Protocol/SQL.html</li>
     <li>Protocol/SQL/Gears.html</li>
     <li>Protocol/WFS.html</li>



More information about the Commits mailing list