Index: lib/OpenLayers/Layer/XYZ.js
===================================================================
--- lib/OpenLayers/Layer/XYZ.js	(revision 10937)
+++ lib/OpenLayers/Layer/XYZ.js	(working copy)
@@ -205,3 +205,67 @@
      },
      CLASS_NAME: "OpenLayers.Layer.OSM"
 });
+
+OpenLayers.Layer.Bing = OpenLayers.Class(OpenLayers.Layer.XYZ, {
+    sphericalMercator: true,
+    initialize: function(name, options) {
+        OpenLayers.Layer.XYZ.prototype.initialize.apply(this, [name, null, options])
+        var metadataUrl = "http://dev.virtualearth.net/REST/v1/Imagery/Metadata/" +
+            options.imagerySet;
+        OpenLayers.Request.GET({
+            url: metadataUrl,
+            params: {
+                key: options.key
+            },
+            callback: function(resp) {
+                var metadata = new OpenLayers.Format.JSON().read(resp.responseText);
+                var res = metadata.resourceSets[0].resources[0];
+                var url = res.imageUrl.replace("{quadkey}", "${quadkey}");
+                this.url = [];
+                for (var i=0; i<res.imageUrlSubdomains.length; ++i) {
+                    this.url.push(url.replace("{subdomain}", res.imageUrlSubdomains[i]));
+                };
+                this.addOptions({
+                    maxResolution: this.resolutions[res.zoomMin],
+                    numZoomLevels: res.zoomMax - res.zoomMin,
+                    zoomOffset: res.zoomMin
+                });
+                this.redraw();
+            },
+            scope: this
+        });
+    },
+    getURL: function(bounds) {
+        if (!this.url) {
+            return OpenLayers.Util.getImagesLocation() + "blank.gif";
+        }
+        var res = this.map.getResolution();
+        var x = Math.round((bounds.left - this.maxExtent.left) 
+            / (res * this.tileSize.w));
+        var y = Math.round((this.maxExtent.top - bounds.top) 
+            / (res * this.tileSize.h));
+        var z = this.map.getZoom() + this.zoomOffset;
+        var quadDigits = [];
+        for (var i = z; i > 0; --i) {
+            var digit = '0';
+            var mask = 1 << (i - 1);
+            if ((x & mask) != 0) {
+                digit++;
+            }
+            if ((y & mask) != 0) {
+                digit++;
+                digit++;
+            }
+            quadDigits.push(digit);
+        }
+        var quadKey = quadDigits.join("");
+        var url = this.url;
+        if (url instanceof Array) {
+            url = this.selectUrl(quadKey, url);
+        }
+        var path = OpenLayers.String.format(url, {'quadkey': quadKey});
+
+        return path;
+    },
+    CLASS_NAME: "OpenLayers.Layer.Bing"
+});
Index: examples/proxy.cgi
===================================================================
--- examples/proxy.cgi	(revision 10937)
+++ examples/proxy.cgi	(working copy)
@@ -21,7 +21,7 @@
                 'sigma.openplans.org', 'demo.opengeo.org',
                 'www.openstreetmap.org', 'sample.azavea.com',
                 'v2.suite.opengeo.org', 'v-swe.uni-muenster.de:8080', 
-                'vmap0.tiles.osgeo.org']
+                'vmap0.tiles.osgeo.org', 'dev.virtualearth.net']
 
 method = os.environ["REQUEST_METHOD"]
 
Index: examples/bing-tiles.html
===================================================================
--- examples/bing-tiles.html	(revision 0)
+++ examples/bing-tiles.html	(revision 0)
@@ -0,0 +1,48 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <title>OpenLayers Bing Tiles Example</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 type="text/javascript">
+        OpenLayers.ProxyHost = "proxy.cgi?url=";
+        var apiKey = "AqTGBsziZHIJYYxgivLBf0hVdrAk9mWO5cQcb8Yux8sW5M8c8opEC2lZqKR1ZZXf";
+        var map, road, aerial;
+        function init(){
+            map = new OpenLayers.Map( 'map');
+            road = new OpenLayers.Layer.Bing( "Bing Road Map", {
+                key: apiKey,
+                imagerySet: "Road"
+            });
+            aerial = new OpenLayers.Layer.Bing( "Bing Aerial Map", {
+                key: apiKey,
+                imagerySet: "Aerial"
+            });
+            map.addLayers([road, aerial, new OpenLayers.Layer.OSM()]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher())
+            map.setCenter(
+                new OpenLayers.LonLat(-71.147, 42.472).transform(
+                    new OpenLayers.Projection("EPSG:4326"),
+                    map.getProjectionObject()
+                ), 12
+            );    
+        }
+    </script>
+  </head>
+  <body onload="init()">
+    <h1 id="title">Basic Bing Tiles Example</h1>
+
+    <div id="tags">
+        bing tiles
+    </div>
+
+    <div id="shortdesc">Use Bing with direct tile access</div>
+
+    <div id="map" class="smallmap"></div>
+
+    <div id="docs">
+        This example shows a very simple map with Bing layers that use direct
+        tile access. 
+    </div>
+  </body>
+</html>
