Index: tests/Control/test_Attribution.html
===================================================================
--- tests/Control/test_Attribution.html	(revision 0)
+++ tests/Control/test_Attribution.html	(revision 0)
@@ -0,0 +1,32 @@
+<html>
+<head>
+  <script src="../../lib/OpenLayers.js"></script>
+  <script type="text/javascript"><!--
+    var map; 
+    function test_01_Control_Attribution_constructor (t) {
+        t.plan( 2 );
+    
+        control = new OpenLayers.Control.Attribution();
+        t.ok( control instanceof OpenLayers.Control.Attribution, "new OpenLayers.Control returns object" );
+        t.eq( control.displayClass,  "olControlAttribution", "displayClass is correct" );
+    }
+    function test_Control_Attribution_draw (t) {
+        t.plan(3);
+        control = new OpenLayers.Control.Attribution();
+        map = new OpenLayers.Map("map");
+        map.addControl(control);
+        map.addLayer(new OpenLayers.Layer("name",{'attribution':'My layer!'}));
+        t.eq(control.div.innerHTML, 'My layer!', "Attribution correct with one layer.");
+        map.addLayer(new OpenLayers.Layer("name", {'attribution':'My layer 2!'}));
+        t.eq(control.div.innerHTML, 'My layer!, My layer 2!', "Attribution correct with two layers.");
+        control.seperator = '|';
+        map.addLayer(new OpenLayers.Layer("name",{'attribution':'My layer 3!'}));
+        t.eq(control.div.innerHTML, 'My layer!|My layer 2!|My layer 3!', "Attribution correct with three layers and diff seperator.");
+    }    
+  // -->
+  </script>
+</head>
+<body>
+    <div id="map" style="width: 1024px; height: 512px;"/>
+</body>
+</html>
Index: tests/list-tests.html
===================================================================
--- tests/list-tests.html	(revision 4228)
+++ tests/list-tests.html	(working copy)
@@ -62,6 +62,7 @@
     <li>Tile/test_Image.html</li>
     <li>Tile/test_WFS.html</li>
     <li>test_Control.html</li>
+    <li>Control/test_Attribution.html</li>
     <li>Control/test_SelectFeature.html</li>
     <li>Control/test_DragFeature.html</li>
     <li>Control/test_DragPan.html</li>
Index: theme/default/style.css
===================================================================
--- theme/default/style.css	(revision 4228)
+++ theme/default/style.css	(working copy)
@@ -10,7 +10,13 @@
     left: 2px;
     bottom: 15px;   
 }
-
+.olControlAttribution {
+    font-size: smaller; 
+    right: 3px; 
+    bottom: 4.5em; 
+    position: absolute; 
+    display: block;
+}
 .olControlScale {
     right: 3px;
     bottom: 3em;
Index: lib/OpenLayers/Control/Attribution.js
===================================================================
--- lib/OpenLayers/Control/Attribution.js	(revision 0)
+++ lib/OpenLayers/Control/Attribution.js	(revision 0)
@@ -0,0 +1,78 @@
+/* Copyright (c) 2006-2007 MetaCarta, Inc., published under a modified BSD license.
+ * See http://svn.openlayers.org/trunk/openlayers/repository-license.txt 
+ * for the full text of the license. */
+
+/**
+ * @requires OpenLayers/Control.js
+ * 
+ * Class: OpenLayers.Control.Attribution
+ * Add attribution from layers to the map display. Uses 'attribution' property
+ * of each layer.
+ */
+OpenLayers.Control.Attribution = 
+  OpenLayers.Class(OpenLayers.Control, {
+    
+    /**
+     * APIProperty: seperator
+     * {String} String used to seperate layers.
+     */
+    seperator: ", ",
+       
+    /**
+     * Constructor: OpenLayers.Control.Attribution 
+     * 
+     * Parameters:
+     * options - {Object} Options for control.
+     */
+    initialize: function(options) {
+        OpenLayers.Control.prototype.initialize.apply(this, arguments);
+    },
+
+    /** 
+     * Method: destroy
+     * Destroy control.
+     */
+    destroy: function() {
+        this.map.events.unregister("removelayer", this, this.updateAttribution);
+        this.map.events.unregister("addlayer", this, this.updateAttribution);
+        this.map.events.unregister("changelayer", this, this.updateAttribution);
+        
+        OpenLayers.Control.prototype.destroy.apply(this, arguments);
+    },    
+    
+    /**
+     * Method: draw
+     * Initialize control.
+     * 
+     * Returns: 
+     * {DOMElement} A reference to the DIV DOMElement containing the control
+     */    
+    draw: function() {
+        OpenLayers.Control.prototype.draw.apply(this, arguments);
+        
+        this.map.events.register('changelayer', this, this.updateAttribution);
+        this.map.events.register('addlayer', this, this.updateAttribution);
+        this.map.events.register('removelayer', this, this.updateAttribution);
+        this.updateAttribution();
+        
+        return this.div;    
+    },
+
+    /**
+     * Method: updateAttribution
+     * Update attribution string.
+     */
+    updateAttribution: function() {
+        var attributions = [];
+        for(var i=0; i < this.map.layers.length; i++) {
+            var layer = this.map.layers[i];
+            if (layer.attribution && layer.getVisibility()) {
+                attributions.push( layer.attribution );
+            }
+        }  
+        this.div.innerHTML = attributions.join(this.seperator);
+    },
+
+    /** @final @type String */
+    CLASS_NAME: "OpenLayers.Control.Attribution"
+});
Index: lib/OpenLayers/Layer.js
===================================================================
--- lib/OpenLayers/Layer.js	(revision 4229)
+++ lib/OpenLayers/Layer.js	(working copy)
@@ -73,6 +73,13 @@
      */
     visibility: true,
 
+    /**
+     * APIProperty: attribution
+     * {String} Attribution string, displayed when an 
+     *     <OpenLayers.Control.Attribution> has been added to the map.
+     */
+    attribution: null, 
+
     /** 
      * Property: inRange
      * {Boolean} The current map resolution is within the layer's min/max 
Index: lib/OpenLayers.js
===================================================================
--- lib/OpenLayers.js	(revision 4228)
+++ lib/OpenLayers.js	(working copy)
@@ -125,6 +125,7 @@
             "OpenLayers/Handler/MouseWheel.js",
             "OpenLayers/Handler/Keyboard.js",
             "OpenLayers/Control.js",
+            "OpenLayers/Control/Attribution.js",
             "OpenLayers/Control/ZoomBox.js",
             "OpenLayers/Control/ZoomToMaxExtent.js",
             "OpenLayers/Control/DragPan.js",
Index: examples/attribution.html
===================================================================
--- examples/attribution.html	(revision 0)
+++ examples/attribution.html	(revision 0)
@@ -0,0 +1,36 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+  <head>
+    <style type="text/css">
+        #map {
+            width: 512px;
+            height: 512px;
+            border: 1px solid black;
+        }
+    </style>
+    <script src="../lib/OpenLayers.js"></script>
+    <script type="text/javascript">
+        var map; 
+        function init(){
+            map = new OpenLayers.Map('map');
+            
+            var ol_wms = new OpenLayers.Layer.WMS( "OpenLayers WMS", 
+                "http://labs.metacarta.com/wms/vmap0?", {layers: 'basic'},
+                {'attribution': 'Provided by OpenLayers'}); 
+
+            var jpl_wms = new OpenLayers.Layer.WMS( "NASA Global Mosaic",
+                "http://wms.jpl.nasa.gov/wms.cgi", 
+                {layers: "modis,global_mosaic"},{attribution:"Provided by NASA"});
+
+            map.addLayers([ol_wms, jpl_wms]);
+            map.addControl(new OpenLayers.Control.LayerSwitcher());
+            map.addControl(new OpenLayers.Control.Attribution());
+            // map.setCenter(new OpenLayers.LonLat(0, 0), 0);
+            map.zoomToMaxExtent();
+        }
+    </script>
+  </head>
+  <body onload="init()">
+    <h1>OpenLayers Example</h1>
+    <div id="map"></div>
+  </body>
+</html>
