Index: clients/java/iedoc2java.xml
===================================================================
--- clients/java/iedoc2java.xml	(revision 2160)
+++ clients/java/iedoc2java.xml	(working copy)
@@ -23,7 +23,7 @@
  *
  */
 -->
-   
+
 <xsl:output method="html"/>
 <xsl:strip-space elements="*"/>
 <xsl:param name="mode" />
@@ -61,15 +61,32 @@
 </xsl:template>
 
 <!--********************
-TOP 
+TOP
 ***************-->
 
 <xsl:template match="top" mode="interface">
 	<xsl:text>package com.thoughtworks.selenium;&nl;</xsl:text>
+    <xsl:text>import java.awt.image.BufferedImage;&nl;</xsl:text>
 	<xsl:text>/**&nl;</xsl:text>
 	<xsl:apply-templates />
 	<xsl:text>*/&nl;public interface Selenium {
 
+    /**
+    * Captures a screenshot and sends it back to the client.
+    *
+    * @param fileType whatever type of image is supported by the current jvm, e.g. "png", "jpeg" or "bmp"
+    */
+    public BufferedImage getScreenshot(String fileType);
+    /**
+     * Captures the full webpage and sends it back to the client.
+     * @param fileType whatever type of image is supported by the current jvm, e.g. "png", "jpeg" or "bmp"
+     * @throws Exception on wrong filetype
+     */
+    public BufferedImage getFullScreenshot(String fileType) throws Exception;
+	/**
+	 * Call this function once, before you call initScreenshots.
+	 */
+	public String initScreenshots() throws Exception;
 	/** Launches the browser with a new Selenium session */
     void start();
 
@@ -80,6 +97,7 @@
 
 <xsl:template match="top" mode="implementation">
 	<xsl:text>package com.thoughtworks.selenium;&nl;</xsl:text>
+    <xsl:text>import java.awt.image.BufferedImage;&nl;</xsl:text>
 	<xsl:text disable-output-escaping="yes"><![CDATA[
 /** The default implementation of the Selenium interface; <i>end users will primarily interact with this object.</i> */
 public class DefaultSelenium implements Selenium {
@@ -88,7 +106,7 @@
 	// This part of the file is hard-coded in the XSL
 	protected CommandProcessor commandProcessor;
 	/** Uses a CommandBridgeClient, specifying a server host/port, a command to launch the browser, and a starting URL for the browser.
-     * 
+     *
      * <p><i>browserString</i> may be any one of the following:
      * <ul>
      * <li><code>*firefox [absolute path]</code> - Automatically launch a new Firefox process using a custom Firefox profile.
@@ -108,7 +126,7 @@
      * specify your own custom browser, it's up to you to configure it correctly.  At a minimum, you'll need to configure your
      * browser to use the Selenium Server as a proxy, and disable all browser-specific prompting.
      * </ul>
-     * 
+     *
      * @param serverHost the host name on which the Selenium Server resides
      * @param serverPort the port on which the Selenium Server is listening
      * @param browserString the command string used to launch the browser, e.g. "*firefox", "*iexplore" or "c:\\program files\\internet explorer\\iexplore.exe"
@@ -118,12 +136,34 @@
     public DefaultSelenium(String serverHost, int serverPort, String browserStartCommand, String browserURL) {
         this.commandProcessor = new HttpCommandProcessor(serverHost, serverPort, browserStartCommand, browserURL);
     }
-    
+
     /** Uses an arbitrary CommandProcessor */
     public DefaultSelenium(CommandProcessor processor) {
         this.commandProcessor = processor;
     }
-    
+
+    /**
+    * Captures a screenshot and sends it back to the client.
+    *
+    * @param fileType whatever type of image is supported by the current jvm, e.g. "png", "jpeg" or "bmp"
+    */
+    public BufferedImage getScreenshot(String fileType) {
+       return this.commandProcessor.getScreenshot(fileType);
+    }
+    /**
+     * Captures the full webpage and sends it back to the client.
+     * @param fileType whatever type of image is supported by the current jvm, e.g. "png", "jpeg" or "bmp"
+     * @throws Exception on wrong filetype
+     */
+    public BufferedImage getFullScreenshot(String fileType) throws Exception {
+       return this.commandProcessor.getFullScreenshot(fileType);
+    }
+
+	public String initScreenshots() throws Exception {
+		String[] args ;
+	   return this.commandProcessor.getString("initScreenshots", null);
+	}
+
     public void start() {
 		try {
             commandProcessor.start();
@@ -160,9 +200,9 @@
 		<xsl:text>void </xsl:text>
 	</xsl:if>
 	<xsl:apply-templates select="return" mode="declaration" />
-	
+
 	<xsl:value-of select="@name" />
-	
+
 	<!-- Params -->
 	<xsl:text>(</xsl:text>
 	<xsl:apply-templates select="param" mode="declaration" />
@@ -176,14 +216,14 @@
 		<xsl:text>void </xsl:text>
 	</xsl:if>
 	<xsl:apply-templates select="return" mode="declaration" />
-	
+
 	<xsl:value-of select="@name" />
-	
+
 	<!-- Params -->
 	<xsl:text>(</xsl:text>
 	<xsl:apply-templates select="param" mode="declaration" />
 	<xsl:text>) {&nl;&tab;&tab;</xsl:text>
-	
+
 	<!-- Return only if necessary -->
 	<xsl:if test="count(./return) = 0">
 		<xsl:text>commandProcessor.doCommand</xsl:text>
Index: clients/java/src/test/java/com/thoughtworks/selenium/ScreenshotTest.java
===================================================================
--- clients/java/src/test/java/com/thoughtworks/selenium/ScreenshotTest.java	(revision 0)
+++ clients/java/src/test/java/com/thoughtworks/selenium/ScreenshotTest.java	(revision 0)
@@ -0,0 +1,27 @@
+package com.thoughtworks.selenium;
+
+import junit.framework.TestCase;
+
+public class ScreenshotTest extends TestCase {
+
+	protected void setUp() throws Exception {
+		super.setUp();
+	}
+
+	public void testScreenshotValidation() {
+		HttpCommandProcessor p = new HttpCommandProcessor("","","");
+		try {
+			p.getFullScreenshot("rrr");
+		} catch (Exception e) {
+			assertEquals("Unknown filetype: rrr", e.getMessage());
+			return;
+		}
+		fail("Wrong format should raise exception!");
+
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+	}
+
+}
Index: clients/java/src/main/java/com/thoughtworks/selenium/CommandProcessor.java
===================================================================
--- clients/java/src/main/java/com/thoughtworks/selenium/CommandProcessor.java	(revision 2160)
+++ clients/java/src/main/java/com/thoughtworks/selenium/CommandProcessor.java	(working copy)
@@ -17,18 +17,20 @@
 
 package com.thoughtworks.selenium;
 
+import java.awt.image.BufferedImage;
+
 /**
  * <p>Provides a <code>doCommand</code> method, which sends the command to the browser
  * to be performed.</p>
- *  
- *  
+ *
+ *
  * @author Paul Hammant
  * @version $Revision$
  */
 public interface CommandProcessor {
 
     /** Send the specified remote command to the browser to be performed
-     * 
+     *
      * @param command - the remote command verb
      * @param args - the arguments to the remote command (depends on the verb)
      * @return - the command result, defined by the remote JavaScript.  "getX" style
@@ -37,9 +39,17 @@
      */
     String doCommand(String command, String[] args);
 
+    /** Send capture command to the remote
+     *
+     * @param fileType - e.g. "PNG", "JPEG", or "BMP"
+     * @return - a screen shot as buffered image
+     */
+    public BufferedImage getScreenshot(String fileType);
+    public BufferedImage getFullScreenshot(String fileType) throws Exception;
+
     /** Starts a new Selenium testing session */
     public void start();
-    
+
     /** Ends the current Selenium testing session (normally killing the browser) */
     public void stop();
 
Index: clients/java/src/main/java/com/thoughtworks/selenium/HttpCommandProcessor.java
===================================================================
--- clients/java/src/main/java/com/thoughtworks/selenium/HttpCommandProcessor.java	(revision 2160)
+++ clients/java/src/main/java/com/thoughtworks/selenium/HttpCommandProcessor.java	(working copy)
@@ -17,6 +17,8 @@
 
 package com.thoughtworks.selenium;
 
+import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
 import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
@@ -35,9 +37,9 @@
     private String browserStartCommand;
     private String browserURL;
     private String sessionId;
-    
+
     /** Specifies a server host/port, a command to launch the browser, and a starting URL for the browser.
-     * 
+     *
      * @param serverHost - the host name on which the Selenium Server resides
      * @param serverPort - the port on which the Selenium Server is listening
      * @param browserStartCommand - the command string used to launch the browser, e.g. "*firefox" or "c:\\program files\\internet explorer\\iexplore.exe"
@@ -45,14 +47,14 @@
      * e.g. "http://www.google.com" would send the browser to "http://www.google.com/selenium-server/core/RemoteRunner.html"
      */
     public HttpCommandProcessor(String serverHost, int serverPort, String browserStartCommand, String browserURL) {
-        this.pathToServlet = "http://" + serverHost + 
+        this.pathToServlet = "http://" + serverHost +
         ":"+ Integer.toString(serverPort) + "/selenium-server/driver/";
         this.browserStartCommand = browserStartCommand;
         this.browserURL = browserURL;
     }
-    
+
     /** Specifies the URL to the CommandBridge servlet, a command to launch the browser, and a starting URL for the browser.
-     * 
+     *
      * @param pathToServlet - the URL of the Selenium Server Driver, e.g. "http://localhost:4444/selenium-server/driver/" (don't forget the final slash!)
      * @param browserStartCommand - the command string used to launch the browser, e.g. "*firefox" or "c:\\program files\\internet explorer\\iexplore.exe"
      * @param browserURL - the starting URL including just a domain name.  We'll start the browser pointing at the Selenium resources on this URL,
@@ -75,7 +77,7 @@
         return result;
     }
 
-    /** Sends the specified command string to the bridge servlet */  
+    /** Sends the specified command string to the bridge servlet */
     public String executeCommandOnServlet(String command) {
         InputStream is = null;
         try {
@@ -104,10 +106,53 @@
         return sb.toString();
     }
 
+    /** Send capture command to the bridge servlet
+     *
+     * @param fileType - e.g. "PNG", "JPEG", or "BMP"
+     * @return - a screen shot as buffered image
+     */
+    public BufferedImage getScreenshot(String fileType) {
+        String[] args = new String[] {fileType};
+        DefaultRemoteCommand command = new DefaultRemoteCommand("getScreenshot", args);
+        return doScreenshot(command);
+    }
+    public BufferedImage getFullScreenshot(String fileType) throws Exception {
+    	String[] args = new String[] {fileType};
+    	if (!(fileType.equals("png")||fileType.equals("jpeg") || fileType.equals("bmp")) ) {
+    		throw new Exception("Unknown filetype: " + fileType);
+    	}
+        DefaultRemoteCommand command = new DefaultRemoteCommand("getFullScreenshot", args);
+        return doScreenshot(command);
+    }
+    /** Send capture command to the bridge servlet
+     *
+     * @param fileType - e.g. "PNG", "JPEG", or "BMP"
+     * @return - a screen shot as buffered image
+     */
+    private BufferedImage doScreenshot(DefaultRemoteCommand command) {
+
+        InputStream inputStream = null;
+        try {
+            inputStream = getCommandResponse(command.getCommandURLString(), inputStream);
+            return ImageIO.read(inputStream);
+        } catch (IOException ioExc) {
+            // TODO: maybe throwing an exception here is better
+            return null;
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException ioExc) {
+                    ioExc.printStackTrace();
+                }
+            }
+        }
+    }
+
     private InputStream getCommandResponse(String command, InputStream is) throws IOException {
         int responsecode = HttpURLConnection.HTTP_MOVED_PERM;
         while (responsecode == HttpURLConnection.HTTP_MOVED_PERM) {
-            URL result = new URL(pathToServlet); 
+            URL result = new URL(pathToServlet);
             String body = buildCommandBody(command);
             HttpURLConnection uc = (HttpURLConnection) result.openConnection();
             uc.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
@@ -142,7 +187,7 @@
     public void start() {
         String result = getString("getNewBrowserSession", new String[]{browserStartCommand, browserURL});
         sessionId = result;
-        
+
     }
 
     public void stop() {
@@ -167,8 +212,8 @@
     /** Convert backslash-escaped comma-delimited string into String array.  As described in SRC-CDP
      * spec section 5.2.1.2, these strings are comma-delimited, but commas
      * can be escaped with a backslash "\".  Backslashes can also be escaped
-     * as a double-backslash. 
-     * @param input the unparsed string, e.g. "veni\, vidi\, vici,c:\\foo\\bar,c:\\I came\, I \\saw\\\, I conquered" 
+     * as a double-backslash.
+     * @param input the unparsed string, e.g. "veni\, vidi\, vici,c:\\foo\\bar,c:\\I came\, I \\saw\\\, I conquered"
      * @return the string array resulting from parsing this string
      */
     public static String[] parseCSV(String input) {
@@ -187,12 +232,12 @@
                     // fall through to:
                 default:
                     sb.append(c);
-            }  
+            }
         }
         output.add(sb.toString());
         return (String[]) output.toArray(new String[0]);
     }
-    
+
     public Number getNumber(String commandName, String[] args) {
         String result = getString(commandName, args);
         Number n;
Index: server-coreless/.classpath
===================================================================
--- server-coreless/.classpath	(revision 2160)
+++ server-coreless/.classpath	(working copy)
@@ -1,15 +1,14 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/main/java"/>
-	<classpathentry kind="src" path="src/main/resources"/>
-	<classpathentry kind="src" path="src/test/java"/>
-	<classpathentry kind="src" path="src/test/resources"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry exported="true" kind="var" path="M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar" sourcepath="M2_REPO/junit/junit/3.8.1/junit-3.8.1-sources.jar"/>
-	<classpathentry exported="true" kind="var" path="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar" sourcepath="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4-sources.jar"/>
-	<classpathentry exported="true" kind="var" path="M2_REPO/ant/ant/1.6.5/ant-1.6.5.jar" sourcepath="M2_REPO/ant/ant/1.6.5/ant-1.6.5-sources.jar"/>
-	<classpathentry exported="true" kind="var" path="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4.jar" sourcepath="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4-sources.jar"/>
-	<classpathentry exported="true" kind="var" path="M2_REPO/jetty/org.mortbay.jetty/5.1.10/org.mortbay.jetty-5.1.10.jar"/>
-	<classpathentry exported="true" kind="var" path="M2_REPO/bouncycastle/bcprov-jdk15/135/bcprov-jdk15-135.jar"/>
-	<classpathentry kind="output" path="eclipse"/>
-</classpath>
+<classpath>
+  <classpathentry kind="src" path="src/main/java"/>
+  <classpathentry kind="src" path="src/main/resources" excluding="**/*.java"/>
+  <classpathentry kind="src" path="src/test/java" output="target/test-classes"/>
+  <classpathentry kind="src" path="src/test/resources" output="target/test-classes" excluding="**/*.java"/>
+  <classpathentry kind="output" path="target/classes"/>
+  <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+  <classpathentry kind="var" path="M2_REPO/junit/junit/3.8.1/junit-3.8.1.jar"/>
+  <classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar"/>
+  <classpathentry kind="var" path="M2_REPO/jetty/org.mortbay.jetty/5.1.10/org.mortbay.jetty-5.1.10.jar"/>
+  <classpathentry kind="var" path="M2_REPO/bouncycastle/bcprov-jdk15/135/bcprov-jdk15-135.jar"/>
+  <classpathentry kind="var" path="M2_REPO/ant/ant/1.6.5/ant-1.6.5.jar"/>
+  <classpathentry kind="var" path="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4.jar" sourcepath="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4-sources.jar"/>
+</classpath>
\ No newline at end of file
Index: server-coreless/.project
===================================================================
--- server-coreless/.project	(revision 2160)
+++ server-coreless/.project	(working copy)
@@ -5,7 +5,6 @@
   <buildSpec>
     <buildCommand>
       <name>org.eclipse.jdt.core.javabuilder</name>
-      <arguments/>
     </buildCommand>
   </buildSpec>
   <natures>
Index: server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/MergeTest.java
===================================================================
--- server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/MergeTest.java	(revision 0)
+++ server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/MergeTest.java	(revision 0)
@@ -0,0 +1,73 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import junit.framework.TestCase;
+
+public class MergeTest extends TestCase {
+
+	BufferedImage top ;
+	BufferedImage bottom;
+
+	protected void setUp() throws Exception {
+	    top = ImageIO.read(new File( FilePathHolder.getPath()+ "/outimg-0.png"));
+	    bottom = ImageIO.read(new File(FilePathHolder.getPath() + "/outimg-1.png"));
+	}
+
+	public void testFullMerge() throws IOException {
+		PageImageCollection container = new FileImageCollection(FilePathHolder.getPath() + "/newimage-X.png");
+		CropImages crop = new CropImages(container);
+		AreaFinder af = new AreaFinder(container.getCalibrationImage());
+		int [] color = { 24,24,24};
+		crop.cropAll(af.getBox(color));
+		//crop.getOutImages().saveCollectionToPath("/tmp/");
+		Merge merge= new Merge(0, log);
+		long t1,t2;
+		t1 = System.currentTimeMillis();
+
+		BufferedImage out = merge.mergeAll(crop.getOutImages());
+		t2 = System.currentTimeMillis();
+		System.out.println("Time taken: " + (t2 - t1) + "");
+		PrintUtils.imageInfo(out);
+		ImageIO.write(out, "png",new File("/tmp/allmerged1.png"));
+		assertEquals(5980, out.getHeight());
+		//ImageIO.write(out, "png",new File(FilePathHolder.getPath() + "/allmerged.png"));
+
+	}
+	private static Log log = LogFactory.getLog(TestCase.class);
+	public void testGetStartLine() {
+
+		Merge merge= new Merge(0, log);
+		int [] votes = { 0, 4, 5, 0};
+		assertEquals(merge.getStartLine(votes, 1), 2);
+	}
+
+	public void testMerge() throws IOException {
+		Merge merge= new Merge(log);
+		BufferedImage res = merge.mergeImages(top, bottom);
+		ImageIO.write(res, "png", new File( "/tmp/outimg-mergetest.png"));
+		assertEquals(res.getHeight(), 965);
+
+	}
+
+	public void testCheckLines() {
+		Merge merge= new Merge(log);
+		int [] votes =  merge.checkLines(top.getData(), bottom.getData(),top.getHeight()- bottom.getHeight() );
+		int ind = 0;
+
+		for (int i = 0; i < votes.length; i++) {
+			if (votes[i] / merge.getTemplateArea(bottom) == 1) {
+				ind = i;
+			}
+		}
+
+		assertTrue(ind > 0);
+	}
+}
\ No newline at end of file
Index: server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/AreaFinderTest.java
===================================================================
--- server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/AreaFinderTest.java	(revision 0)
+++ server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/AreaFinderTest.java	(revision 0)
@@ -0,0 +1,50 @@
+package org.openqa.selenium.server.imagecapture;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.io.File;
+
+import javax.imageio.ImageIO;
+import junit.framework.TestCase;
+
+public class AreaFinderTest extends TestCase {
+	private AreaFinder af;
+	private BufferedImage image;
+	protected void setUp() throws Exception {
+		File file = new File(FilePathHolder.getPath() + "/newimage-c.png");
+	    image = ImageIO.read(file);
+		af = new AreaFinder(image);
+	}
+
+	protected void tearDown() throws Exception {
+		super.tearDown();
+	}
+
+	public void testGetBox() throws Exception {
+		int[] color = {24,24,24};
+		Rectangle rec = af.getBox(color);
+		assertEquals(4, rec.x);
+		assertEquals(116, rec.y);
+		assertEquals(1015, rec.width);
+		assertEquals(627, rec.height);
+		//System.out.println(rec.toString());
+	}
+
+	public void testIsColor() {
+		int [] pixel = {0,1,2};
+		int[] color = {1,2,3};
+		assertFalse(af.isColor(pixel, color));
+		color[2] = 3;
+		color[0] = 0;
+		assertFalse(af.isColor(pixel, color));
+		color[2] = 2;
+		color[1] = 1;
+		assertTrue(af.isColor(pixel, color));
+	}
+
+	private void printInt(int[] p) {
+		for (int i = 0 ; i < p.length; i++) System.out.print(" " + p[i]);
+		System.out.println();
+	}
+
+}
Index: server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/CropImagesTest.java
===================================================================
--- server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/CropImagesTest.java	(revision 0)
+++ server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/CropImagesTest.java	(revision 0)
@@ -0,0 +1,33 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.Rectangle;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+/**
+ * @todo: Move a set of testimages into a resources subdir
+ * @author tarjei
+ *
+ */
+public class CropImagesTest extends TestCase {
+	FileImageCollection fileImages;
+	protected void setUp() throws Exception {
+		fileImages = new FileImageCollection( FilePathHolder.getPath() + "/newimage-X.png");
+	}
+
+
+	public void testbuildRectangle() throws IOException {
+		CropImages ci = new CropImages(fileImages);
+		int[] color = {24,24,24};
+		Rectangle r = ci.buildRectangle(color);
+		assertTrue(r.x == 4);
+	}
+	public void testCropAll() throws IOException {
+		CropImages ci = new CropImages(fileImages);
+		int[] color = {24,24,24};
+		Rectangle r = ci.buildRectangle(color);
+		ci.cropAll(r);
+		ci.getOutImages().saveCollectionToPath(FilePathHolder.getPath());
+	}
+
+}
Index: server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/FileImageCollectionTest.java
===================================================================
--- server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/FileImageCollectionTest.java	(revision 0)
+++ server-coreless/src/test/java/org/openqa/selenium/server/imagecapture/FileImageCollectionTest.java	(revision 0)
@@ -0,0 +1,30 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+/**
+ * Todo: change filehandling to use mocks.
+ */
+public class FileImageCollectionTest extends TestCase {
+	private FileImageCollection it;
+	protected void setUp() throws Exception {
+		it = new FileImageCollection(FilePathHolder.getPath() + "/newimage-X.png");
+	}
+
+	public void testGetCalibrationImage() throws IOException {
+		BufferedImage img = it.getCalibrationImage();
+		assertNotNull(img);
+	}
+	public void testIterator() throws IOException {
+		Iterator<BufferedImage> i = it.iterator();
+		int j ;
+		for (j= 0; i.hasNext(); j++) {
+			assertNotNull(i.next());
+		}
+		assertEquals(j ,18);
+	}
+
+}
Index: server-coreless/src/test/resources/imagecapture/outimg-0.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-0.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-1.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-1.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-2.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-2.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-3.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-3.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-4.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-4.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-5.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-5.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-6.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-6.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-7.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-7.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-8.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-8.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-9.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-9.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-c.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-c.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-10.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-10.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-11.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-11.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-12.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-12.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-13.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-13.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-14.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-14.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-15.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-15.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-16.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-16.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-0.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-0.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/outimg-17.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/outimg-17.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-1.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-1.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-2.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-2.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-3.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-3.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-4.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-4.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-5.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-5.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-6.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-6.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-7.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-7.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-8.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-8.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-9.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-9.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-10.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-10.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-11.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-11.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-12.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-12.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-13.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-13.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-14.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-14.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-15.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-15.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-16.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-16.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/test/resources/imagecapture/newimage-17.png
===================================================================
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream

Property changes on: server-coreless/src/test/resources/imagecapture/newimage-17.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Index: server-coreless/src/main/java/org/openqa/selenium/server/SeleniumDriverResourceHandler.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/SeleniumDriverResourceHandler.java	(revision 2160)
+++ server-coreless/src/main/java/org/openqa/selenium/server/SeleniumDriverResourceHandler.java	(working copy)
@@ -62,11 +62,12 @@
 import org.openqa.selenium.server.browserlaunchers.BrowserLauncher;
 import org.openqa.selenium.server.browserlaunchers.BrowserLauncherFactory;
 import org.openqa.selenium.server.htmlrunner.HTMLLauncher;
+import org.openqa.selenium.server.imagecapture.FullImageCaptureCommand;
 import org.openqa.selenium.server.log.AntJettyLoggerBuildListener;
 
 /**
  * A Jetty handler that takes care of remote Selenium requests.
- * 
+ *
  * Remote Selenium requests are described in detail in the class description for
  * <code>SeleniumServer</code>
  * @see org.openqa.selenium.server.SeleniumServer
@@ -87,14 +88,17 @@
         Collections.synchronizedMap(new HashMap<String, String>());
     private StringBuffer logMessagesBuffer = new StringBuffer();
     private BrowserLauncherFactory browserLauncherFactory = new BrowserLauncherFactory();
-
+    /**
+     * Handler for making screenshots.
+     */
+    private FullImageCaptureCommand fullImageCaptureCommand = new FullImageCaptureCommand();
     public SeleniumDriverResourceHandler(SeleniumServer server) {
         this.server = server;
-        
+
     }
 
     /** Handy helper to retrieve the first parameter value matching the name
-     * 
+     *
      * @param req - the Jetty HttpRequest
      * @param name - the HTTP parameter whose value we'll return
      * @return the value of the first HTTP parameter whose name matches <code>name</code>, or <code>null</code> if there is no such parameter
@@ -138,7 +142,7 @@
             } else if ("POST".equalsIgnoreCase(method) || justLoaded || logging) {
                 handleBrowserResponse(req, res, sessionId, logging, jsState,
 						justLoaded, retrying, closing);
-            } else if (-1 != req.getRequestURL().indexOf("selenium-server/core/scripts/user-extensions.js") 
+            } else if (-1 != req.getRequestURL().indexOf("selenium-server/core/scripts/user-extensions.js")
                     || -1 != req.getRequestURL().indexOf("selenium-server/tests/html/tw.jpg")){
                 // ignore failure to find these items...
             }
@@ -167,7 +171,7 @@
 			throws IOException {
 		String seleniumWindowName = getParam(req, "seleniumWindowName");
 		String localFrameAddress = getParam(req, "localFrameAddress");
-		FrameAddress frameAddress = FrameGroupCommandQueueSet.makeFrameAddress(seleniumWindowName, 
+		FrameAddress frameAddress = FrameGroupCommandQueueSet.makeFrameAddress(seleniumWindowName,
 		        localFrameAddress);
 		String uniqueId = getParam(req, "uniqueId");
 		String sequenceNumberString = getParam(req, "sequenceNumber");
@@ -178,8 +182,8 @@
 		    sequenceNumber = Integer.parseInt(sequenceNumberString);
 	        browserResponseSequencer.waitUntilNumIsAtLeast(sequenceNumber);
 		}
-		
-		
+
+
 		String postedData = readPostedData(req, sessionId, uniqueId);
 		if (logging) {
 			handleLogMessages(postedData);
@@ -208,7 +212,7 @@
 		}
 		req.setHandled(true);
 	}
-	
+
     private void logPostedData(FrameAddress frameAddress, boolean justLoaded, String sessionId, String postedData, String uniqueId) {
         StringBuffer sb = new StringBuffer();
         sb.append("Browser " + sessionId + "/" + frameAddress + " " + uniqueId + " posted " + postedData);
@@ -246,7 +250,7 @@
      *
      * @param req
      * @param sessionId
-     * @param uniqueId 
+     * @param uniqueId
      * @return a string containing the posted data (with piggybacked log info stripped)
      * @throws IOException
      */
@@ -350,7 +354,7 @@
     }
 
     /** Try to extract the name of the file whose absence caused the exception
-     * 
+     *
      * @param e - the exception
      * @return the name of the file whose absence caused the exception
      */
@@ -427,7 +431,7 @@
           FrameGroupCommandQueueSet queue = FrameGroupCommandQueueSet.getQueueSet(sessionId);
           try {
             File downloadedFile = downloadFile(values.get(1));
-            List<File> tempFilesForSession = getTempFiles(sessionId);            
+            List<File> tempFilesForSession = getTempFiles(sessionId);
             tempFilesForSession.add(downloadedFile);
             results = queue.doCommand("type", values.get(0), downloadedFile.getAbsolutePath());
           } catch (Exception e) {
@@ -441,6 +445,35 @@
                 log.error("Problem capturing screenshot", e);
                 results = "ERROR: Problem capturing screenshot: " + e.getMessage();
             }
+        } else if ("getScreenshot".equals(cmd)) {
+           try {
+               res.setContentType("image/" + values.get(0));
+               getScreenshot(values.get(0), res);
+               results = "OK,";
+           } catch (Exception e) {
+               log.error("Problem get screenshot", e);
+               results = "ERROR: Problem get screenshot: " + e.getMessage();
+           }
+        } else if ("getFullScreenshot".equals(cmd)) {
+            try {
+                res.setContentType("image/" + values.get(0));
+                getFullScreenshot(sessionId, values.get(0), res);
+                results = "OK,";
+            } catch (Exception e) {
+            	log.info(e.getMessage());
+                log.error("Problem get fullscreenshot", e);
+                results = "ERROR: Problem get full screenshot: " + e.getMessage();
+            }
+        } else if ("initScreenshots".equals(cmd)) {
+            try {
+                //res.setContentType("image/" + values.get(0));
+            	log.info("Initscreenshots. Sessionid: " + sessionId);
+                initScreenshots(sessionId);
+                results = "OK,";
+            } catch (Exception e) {
+                log.error("Problem init screenshots", e);
+                results = "ERROR: Problem init screenshots: " + e.getMessage();
+            }
         } else if ("keyDownNative".equals(cmd)) {
             try {
                 RobotRetriever.getRobot().keyPress(Integer.parseInt(values.get(0)));
@@ -500,7 +533,7 @@
                 if (values.size() > 4) {
                     output = new File(values.get(4));
                 }
-                
+
                 try {
                     results = launcher.runHTMLSuite( values.get(0),  values.get(1),  values.get(2), output, SeleniumServer.getTimeoutInSeconds(), "true".equals(values.get(3)));
                 } catch (IOException e) {
@@ -562,12 +595,12 @@
         g.execute();
         return outputFile;
     }
-    
+
     protected static String getSpeedForSession(String sessionId) {
       String results = null;
       if (null != sessionId) {
         // get the speed for this session's queues
-        FrameGroupCommandQueueSet queueSet = 
+        FrameGroupCommandQueueSet queueSet =
           FrameGroupCommandQueueSet.getQueueSet(sessionId);
         if (null != queueSet) {
           results = "OK," + queueSet.getSpeed();
@@ -583,7 +616,7 @@
     protected static void setSpeedForSession(String sessionId, int speed) {
       if (null != sessionId) {
          // set the speed for this session's queues
-         FrameGroupCommandQueueSet queueSet = 
+         FrameGroupCommandQueueSet queueSet =
            FrameGroupCommandQueueSet.getQueueSet(sessionId);
          if (null != queueSet) {
            queueSet.setSpeed(speed);
@@ -594,18 +627,51 @@
        }
     }
 
-    private void captureScreenshot(String fileName) throws AWTException, IOException, InterruptedException, ExecutionException, TimeoutException {
+    public BufferedImage doScreenshot() throws AWTException, IOException, InterruptedException, ExecutionException, TimeoutException {
         Robot robot = RobotRetriever.getRobot();
         Rectangle captureSize = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
-        BufferedImage bufferedImage = robot.createScreenCapture(captureSize);
+        return robot.createScreenCapture(captureSize);
+    }
+
+
+    private void captureScreenshot(String fileName) throws AWTException, IOException, InterruptedException, ExecutionException, TimeoutException {
+        BufferedImage bufferedImage = doScreenshot();
         File outFile = new File(fileName);
         ImageIO.write(bufferedImage, "png", outFile);
-        
+
     }
+    /**
+     * Returns a picture of the whole page - not just the viewport.
+     * @throws Exception
+     */
+    private void getFullScreenshot(String sessionId, String formatName, HttpResponse res) throws Exception {
+    	fullImageCaptureCommand.setSessionid(sessionId);
+        ImageIO.write(fullImageCaptureCommand.captureWhole(this), formatName, res.getOutputStream());
+    }
+    /**
+     * Makes a calibration image
+     */
+    private void initScreenshots(String sessionId) throws Exception {
+    	if (sessionId == null) throw new Exception("We need a sessionId");
+    	fullImageCaptureCommand.setSessionid(sessionId);
+    	fullImageCaptureCommand.captureCalibrationImage(this);
+    }
+    /**
+     * Used by the FullImageCaptureCommand to get its log
+     */
+    public Log getLog() {
+    	return log;
+    }
 
+    private void getScreenshot(String formatName, HttpResponse res) throws AWTException, IOException, InterruptedException, ExecutionException, TimeoutException {
+    	// TODO maybe implement null- and empty string check to formatName
+    	BufferedImage bufferedImage = doScreenshot();
+    	ImageIO.write(bufferedImage, formatName, res.getOutputStream());
+    }
+
     private void shutDown(HttpResponse res) {
         log.info("Shutdown command received");
-        
+
         Runnable initiateShutDown = new Runnable() {
             public void run() {
                 log.info("initiating shutdown");
@@ -613,11 +679,11 @@
                 System.exit(0);
             }
         };
-        
+
         Thread isd = new Thread(initiateShutDown);
         isd.setName("initiateShutDown");
         isd.start();
-        
+
         if (res != null) {
             try {
                 res.getOutputStream().write("OK".getBytes());
@@ -626,7 +692,7 @@
                 throw new RuntimeException(e);
             }
         }
-        
+
     }
 
     private String endBrowserSession(String sessionId, boolean cacheUnused) {
@@ -638,12 +704,12 @@
             List<File> tempFilesForSession = getTempFiles(sessionId);
             if (launcher == null) {
                 return "ERROR: No launcher found for sessionId " + sessionId;
-            } 
+            }
             if (tempFilesForSession == null) {
                 return "ERROR: Can't find temp file storage for sessionId " + sessionId; //TODO invariant violated, never should have null, as we set up a new ArrayList when creating the session
             }
             try {
-               launcher.close(); 
+               launcher.close();
             } catch (RuntimeException re) {
               throw re;
             } finally {
@@ -653,9 +719,9 @@
               }
               FrameGroupCommandQueueSet.clearQueueSet(sessionId);
             }
-    
+
             try {
-                sessionIdToListOfTempFiles.remove(sessionId);            
+                sessionIdToListOfTempFiles.remove(sessionId);
             } catch(RuntimeException re) {
                 throw re;
             } finally {
@@ -689,7 +755,7 @@
         return UUID.randomUUID().toString().replaceAll("-", "");
     }
 
-    protected String getNewBrowserSession(String browserString, String startURL) 
+    protected String getNewBrowserSession(String browserString, String startURL)
           throws RemoteCommandException {
 
         browserString = validateBrowserString(browserString);
@@ -699,19 +765,19 @@
         }
 
         String sessionId = UUID.randomUUID().toString().replace("-", "");
-        setLastSessionId(sessionId); 
+        setLastSessionId(sessionId);
         FrameGroupCommandQueueSet queueSet = FrameGroupCommandQueueSet.makeQueueSet(sessionId);
         BrowserLauncher launcher = browserLauncherFactory.getBrowserLauncher(browserString, sessionId);
         registerBrowserLauncher(sessionId, launcher);
         sessionIdsToBrowserStrings.put(sessionId, browserString);
         log.info("Allocated session " + sessionId + " for " + startURL + ", launching...");
-            
+
         boolean multiWindow = server.isMultiWindow();
         launcher.launchRemoteSession(startURL, multiWindow);
-        
+
         try {
           queueSet.waitForLoad(SeleniumServer.getTimeoutInSeconds() * 1000l);
-  
+
           // TODO DGF log4j only
           // NDC.push("sessionId="+sessionId);
           FrameGroupCommandQueueSet queue = FrameGroupCommandQueueSet.getQueueSet(sessionId);
@@ -730,7 +796,7 @@
     private String validateBrowserString(String inputString) throws IllegalArgumentException {
         String browserString = inputString;
         if (SeleniumServer.getForcedBrowserMode()!=null) {
-            browserString = SeleniumServer.getForcedBrowserMode(); 
+            browserString = SeleniumServer.getForcedBrowserMode();
             log.info("overriding browser mode w/ forced browser mode setting: " + browserString);
         }
         if (SeleniumServer.isProxyInjectionMode() && browserString.equals("*iexplore")) {
@@ -794,7 +860,7 @@
             return launchers.get(sessionId);
         }
     }
-    
+
     /** Retrieves the temp files for the specified sessionId, or <code>null</code> if there are no such files. */
     private List<File> getTempFiles(String sessionId) {
         synchronized (sessionIdToListOfTempFiles) {
@@ -806,13 +872,13 @@
             return sessionIdToListOfTempFiles.get(sessionId);
         }
     }
-   
+
     public void registerBrowserLauncher(String sessionId, BrowserLauncher launcher) {
         synchronized (launchers) {
             launchers.put(sessionId, launcher);
         }
     }
-    
+
     /** Kills all running browsers */
     public void stopAllBrowsers() {
       synchronized(launchers) {
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/Merge.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/Merge.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/Merge.java	(revision 0)
@@ -0,0 +1,122 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+
+import org.apache.commons.logging.Log;
+
+public class Merge {
+	private static final int KERN_WIDTH = 200;
+	public final int KERN_HEIGHT = 15;
+	int scrollLength = 0;
+	Log log;
+	public Merge(Log log) {
+		this.log = log;
+	}
+
+	public Merge(int scrollLength, Log log) {
+		this.log = log;
+		this.scrollLength = scrollLength;
+	}
+
+	/**
+	 * Merges all the images in a PageImageCollection into one
+	 * large image.
+	 */
+	int i;
+	public BufferedImage mergeAll(PageImageCollection container) throws IOException {
+		BufferedImage ret, next;
+		container.saveAndReload();
+		Iterator<BufferedImage> it = container.iterator();
+		ret = it.next();
+		i  = 0;
+		while (it.hasNext()) {
+			next = it.next();
+			ret = mergeImages(ret, next);
+			i++;
+		}
+		return ret;
+	}
+
+	public BufferedImage mergeImages (BufferedImage top, BufferedImage bottom) {
+
+		int start = getStartLine(checkLines(top.getData(), bottom.getData(),top.getHeight() -  bottom.getHeight()), top.getWidth());
+		int height = start + bottom.getHeight() + bottom.getData().getSampleModelTranslateY() ;
+		/*
+		log.info("Height:" + height + " start: " + start + ", " + top.getData().getBounds().toString() + ", " +
+		bottom.getData().getBounds().toString() );
+		*/
+		BufferedImage ret = new BufferedImage(bottom.getWidth(), height, BufferedImage.TYPE_INT_RGB);
+		WritableRaster canvas = ret.getRaster();
+		canvas.setRect(0,
+				       0, top.getData());
+		try {
+			ImageIO.write(ret, "png",new File("/tmp/ret"+i+".png"));
+		} catch (IOException e) {
+			log.error("Imagewrite failed: ", e);
+		}
+		canvas.setRect(0,start+bottom.getData().getSampleModelTranslateY() , bottom.getData());
+		return ret;
+
+	}
+
+	public double getTemplateArea(BufferedImage bot) {
+		return KERN_WIDTH * KERN_HEIGHT;
+	}
+
+	public int getStartLine(int[] votes, int width) {
+		int max = 0;
+		int maxIndex = -1;
+		for (int i = 0; i < votes.length; i++) {
+			if (votes[i] > max) {
+				maxIndex = i;
+				max = votes[i];
+			}
+		}
+		double quality = max / width;
+		if (quality < 0.85) {
+			log.info("Low quality match, image no "+ i +" might not be perfect: " + quality + " index: " + maxIndex);
+		} else {
+			log.info("Merging image at: " + maxIndex);
+		}
+		return maxIndex;
+	}
+
+	public int [] checkLines(Raster top, Raster bottom, int topRasterStart) {
+		int x,y, line, maxHeight, maxWidth;
+		int [] topPx = new int [3];
+		int [] botPx = new int [3];
+		int [] votes = new int[top.getHeight()];
+		if (false)
+		{
+			log.info("Top    Minx:" + top.getMinX() + " min y:" + top.getMinY()  ) ;
+			log.info("Bottom Minx:" + bottom.getMinX() + " min y:" + bottom.getMinY());
+		}
+
+		maxHeight = top.getHeight() - KERN_HEIGHT + top.getSampleModelTranslateY();
+		maxWidth = (top.getWidth() > KERN_WIDTH) ? KERN_WIDTH : top.getWidth();
+		for (line = topRasterStart ; line < maxHeight; line++) {
+			for (y = 0 ; y < KERN_HEIGHT; y++) {
+				for (x = 0 ; x < maxWidth; x++) {
+					top.getPixel(x, line + y, topPx); // starts off at the curr line
+					bottom.getPixel(x, y , botPx);
+					if (topPx[0] == botPx[0] && topPx[1] == botPx[1] && topPx[2] == botPx[2]) {
+						votes[line]++;
+					}
+				}
+				/* @todo: time this simple optimization. */
+				if (votes[line] < (maxWidth * 0.9)) break; // breakoff.
+
+			}
+		}
+
+		return votes;
+
+	}
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/ScreenshotMerger.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/ScreenshotMerger.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/ScreenshotMerger.java	(revision 0)
@@ -0,0 +1,23 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+
+public class ScreenshotMerger {
+
+	public final static int [] COLOR = {24,24,24} ; // todo: make it a parameter
+
+	public static BufferedImage getMergedImage(PageImageCollection images) throws IOException {
+		AreaFinder af = new AreaFinder(images.getCalibrationImage());
+		CropImages cropImages = new CropImages(images);
+		cropImages.cropAll(af.getBox(ScreenshotMerger.COLOR));
+		PageImageCollection cropedImages = cropImages.getOutImages();
+		Merge merge = new Merge(null);
+		return merge.mergeAll(cropedImages);
+	}
+
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/AreaFinder.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/AreaFinder.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/AreaFinder.java	(revision 0)
@@ -0,0 +1,156 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+/**
+ * This class finds the area in an image that is covert by just one color.
+ *
+ * @author tarjei
+ *
+ */
+public class AreaFinder {
+
+	private BufferedImage image;
+
+	public AreaFinder(BufferedImage pi) {
+		image = pi;
+	}
+
+	public Rectangle getBox(int [] color)  {
+		Rectangle ret;
+        Raster raster = image.getData();
+        int bands = raster.getSampleModel().getNumBands();
+        int height = raster.getHeight();
+        int width = raster.getWidth();
+        bands = 3;
+
+		if (image.getColorModel().hasAlpha()) System.out.println("Has alpha channel");
+        // used to access the source image
+        int[] pixel = new int[bands];
+        int[][] outImage = new int[height][width];
+        int[] heightHist = new int[height];
+        int[] widthHist = new int[width];
+
+        int hits  = 0;
+        for(int h=0;h<height;h++)
+          for(int w=0;w<width;w++)
+            {
+        	  raster.getPixel(w,h, pixel);
+
+        	  if (isColor(pixel, color)) {
+        		  outImage[h][w] = 1;
+        		  heightHist[h]++;
+        		  widthHist[w]++;
+        		  hits++;
+        	  } else {
+        		  outImage[h][w] = 0;
+        	  }
+            }
+        int xHistInfo [] = getHistogramTopAndBottom(widthHist);
+        int yHistInfo [] = getHistogramTopAndBottom(heightHist);
+        ret = histInfoToRectangle(xHistInfo, yHistInfo);
+
+        //printHistInfo(widthHist);
+        //printHistInfo(heightHist);
+        //hits = hits/(height*width);
+		return ret;
+
+	}
+	/**
+	 * Makes a rectangle from two histogram info arrays
+	 * @param xH
+	 * @param yH
+	 * @return
+	 */
+	private Rectangle histInfoToRectangle(int[] xH, int[] yH) {
+		Rectangle ret = new Rectangle();
+		ret.x = xH[0];
+		ret.y = yH[0];
+		ret.height = yH[1] - yH[0];
+		ret.width = xH[1] - xH[0];
+		return ret;
+	}
+
+	/**
+	 * Finds the area of the histogram that is used by the color.
+	 *
+	 * @param histogram
+	 * @return  int array 0 = min, 1 = max.
+	 */
+	private int[] getHistogramTopAndBottom(int[] histogram) {
+		int i;
+		int[] ret = new int[2];
+		for (i = 0; i < histogram.length; i++) {
+        	if (histogram[i] > 0) {
+        		if (ret[0] == 0) ret[0] = i;
+        		ret[1]= i;
+        	}
+        }
+
+		return ret;
+	}
+
+	protected void saveImage(int height, int width, int[][] outImage)
+			throws IOException {
+		BufferedImage bimage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+        WritableRaster rast = bimage.getRaster();
+        for (int y = 0; y < height; y++) {
+        	for (int x = 0; x< width; x++) {
+        		if (outImage[y][x] == 1) {
+        			rast.setSample(x, y, 0, 0);
+        		} else {
+        			rast.setSample(x, y, 0, 255);
+        		}
+        	}
+        }
+        File n = new File("/tmp/outimg.png");
+        ImageIO.write(bimage, "png",n );
+	}
+
+	private void printHistInfo(int[] hist) throws IOException {
+		int max = 0; int min = hist.length;
+		int maxC,minC; maxC = minC= 0;
+		StringBuffer bufer = new StringBuffer("");
+		for (int i = 0; i < hist.length; i++) {
+			bufer.append(i + ":" + hist[i] + "\n" );
+			if (hist[i] > max) {
+				max = hist[i];
+				maxC = i;
+			}
+			if (hist[i] < min) {
+				min = hist[i];
+				minC = i;
+			}
+		}
+		FileWriter fstream = new FileWriter("/tmp/hist-" + hist.length + ".txt");
+        BufferedWriter out = new BufferedWriter(fstream);
+        out.write(bufer.toString());
+        //	Close the output stream
+        out.close();
+		System.out.println("Hist: min: " + min + "(" + minC + ")" + " max: " + max + "(" + maxC + ")");
+	}
+	/**
+	 *@todo: check out which colormodel is in use!
+	 */
+	public boolean isColor(int [] pixel,int [] color) {
+		if (pixel[0] == color[0] && pixel[1] == color[1] && pixel[2] == color[2]) {
+
+			return true;
+		}
+		return false;
+	}
+	private void printInt(int[] p, String s) {
+		System.out.print(s + "  ");
+		for (int i = 0 ; i < p.length; i++) System.out.print(" " + p[i]);
+		System.out.println();
+	}
+
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FullImageCaptureCommand.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FullImageCaptureCommand.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FullImageCaptureCommand.java	(revision 0)
@@ -0,0 +1,160 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.AWTException;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.Buffer;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeoutException;
+import org.apache.commons.logging.Log;
+import org.openqa.selenium.server.FrameGroupCommandQueueSet;
+import org.openqa.selenium.server.SeleniumDriverResourceHandler;
+/**
+ * Class that creates a full screen capture.
+ * @author tarjei
+ *
+ */
+public class FullImageCaptureCommand {
+
+
+	Log log;
+	String sessionId;
+	int pageLength; // the total length of the page
+	int scrollLength; // viewport size, as reported by browser.
+	final int WINDOW_WIDTH = 2;
+    final int WINDOW_HEIGHT = 3;
+    final int PAGE_HEIGHT = 1;
+    final int PAGE_WIDTH = 0;
+    PageImageCollection images = new InMemoryImageCollection();
+    Rectangle cropRectangle = null;
+	public FullImageCaptureCommand () {
+
+	}
+	public BufferedImage captureWhole(SeleniumDriverResourceHandler resourceHandler) throws Exception {
+		log = resourceHandler.getLog();
+		log.info("getFullScreenCommand");
+
+		if (images.getCalibrationImage() == null) {
+			throw new Exception("You must call initScreenshots before you call this method.");
+		}
+		log.info(cropRectangle.toString());
+		images.removeImages();
+        runJs("windowMaximize", "", "");
+        getPageSize();
+        Thread.sleep(500);
+        runJs("getEval",getScrollJs(0),"");
+        for (int i = 0 , j = 0; i < pageLength; j++) {
+        	images.addImage(cropImage(resourceHandler.doScreenshot()));
+        	i += scrollLength + 100;
+        	log.info("Capturing new screenshot at " + i + "(scroll: " + scrollLength + ") pl: " + pageLength);
+        	log.info(runJs("getEval", getScrollJs(i),"" ));
+
+        	Thread.sleep(1000);
+        	log.info("Done sleeping");
+        }
+        images.saveAndReload();
+		Merge merge = new Merge(scrollLength, log);
+		return merge.mergeAll(images);
+	}
+
+	protected BufferedImage cropImage(BufferedImage image) {
+		CropImages cropImages = new CropImages();
+		return cropImages.cropToRectangle(image, cropRectangle);
+	}
+
+	public void captureCalibrationImage(SeleniumDriverResourceHandler resourceHandler) throws Exception {
+		log = resourceHandler.getLog();
+
+		String color = "1a1a1a";
+		BufferedImage cropImage;
+		runJs("windowMaximize", "", "");
+		runJs("getEval", getSetColorJs(color), "");
+        Thread.sleep(1000);
+        cropImage = resourceHandler.doScreenshot();
+        AreaFinder af = new AreaFinder(cropImage);
+        cropRectangle =  af.getBox(ScreenshotMerger.COLOR);
+        if (cropRectangle.height == 0 || cropRectangle.width == 0) {
+        	throw new Exception("Calibrationimage failed: " + cropRectangle.toString());
+        }
+        images.setCalibrationImage(cropImage);
+
+	}
+	/* returns the js for scrolling the window */
+	private String getScrollJs(int y) {
+		return "window.scroll(0," + y + ");";
+	}
+
+	private String getSetColorJs(String color) {
+		return "var root = window;" + //.selenium.browserbot.topFrame
+		"root.document.open();" +
+		"root.document.write('<html><head></head><body></body></html>');" +
+		"root.document.close();" +
+		"root.document.bgColor= '#" + color + "';" ;
+
+	}
+	/**
+	 * Gets the size of the viewport and the total pagesize.
+	 */
+	private void getPageSize() throws Exception {
+		String[] result;
+		String res = runJs("getEval", getJsScript() + "\ngetSeleniumPageSize();","");
+		log.info("Pagesize: " + res);
+		result = res.split(" ");
+		if (result.length != 4) throw new Exception("Error: " + res);
+        log.debug("Pageheight: " +result[PAGE_HEIGHT]);
+        log.debug("Pagewidth: " +result[PAGE_WIDTH]);
+        log.debug("Windowheight: " +result[WINDOW_HEIGHT]);
+        log.debug("Windowwidth: " +result[WINDOW_WIDTH]);
+        pageLength = Integer.parseInt(result[PAGE_HEIGHT]);
+        scrollLength = Integer.parseInt(result[WINDOW_HEIGHT]);
+	}
+
+	private String runJs(String cmd, String arg1, String arg2) throws Exception{
+		String results;
+
+		FrameGroupCommandQueueSet queue = FrameGroupCommandQueueSet.getQueueSet(sessionId);
+        if (queue == null || sessionId == null) {
+        	throw new Exception("FrameGroupCommandQueueSet  null");
+        }
+        if (cmd == null || arg1 == null || arg2 == null) {
+        	throw new Exception("Cmd or args are null!");
+        }
+		log.debug("Session "+sessionId+" going to doCommand("+cmd+','+arg1+','+ arg2 + ")");
+        results = queue.doCommand(cmd, arg1, arg2);
+	    if (! results.startsWith("OK")) {
+	    	log.error("RunJs result: " + results);
+	    	throw new Exception("Bad js command: " + results);
+	    }
+	    return results;
+	}
+
+	private String getJsScript() {
+		InputStream stream = this.getClass().getClassLoader().getResourceAsStream("imagecapture/imagecapture.js" );
+		StringBuffer sb = new StringBuffer();
+		java.io.BufferedReader br = new java.io.BufferedReader(new java.io.InputStreamReader(stream));
+		try{
+			String line = null;
+			while((line=br.readLine()) != null) sb.append(line + "\n");
+		} catch(Exception ex){
+			log.info(ex.getMessage());
+		}finally{
+			try {
+				stream.close();
+				br.close();
+			} catch (IOException e) {
+				log.info(e.getMessage());
+			}
+		}
+		return sb.toString();
+	}
+
+
+	/* must be set before calling captureCalibaraionImage or captureWhole */
+	public void setSessionid(String sessionId2) {
+		this.sessionId = sessionId2;
+	}
+
+
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/CropImages.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/CropImages.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/CropImages.java	(revision 0)
@@ -0,0 +1,46 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.Iterator;
+
+
+public class CropImages {
+
+	private PageImageCollection collection;
+	private InMemoryImageCollection outImages = new InMemoryImageCollection();
+	public CropImages( PageImageCollection collection) {
+		this.collection = collection;
+	}
+
+	public CropImages( ) {
+
+	}
+	/**
+	 * Creates the rectangle object
+	 * @throws IOException
+	 */
+	public Rectangle buildRectangle(int[] color) throws IOException {
+		AreaFinder af = new AreaFinder(collection.getCalibrationImage());
+		return af.getBox(color);
+	}
+
+	public void cropAll(Rectangle r) throws IOException {
+		Iterator<BufferedImage> it = collection.iterator();
+		while(it.hasNext())	outImages.addImage( cropToRectangle( it.next(), r ) );
+	}
+	/**
+	 * @return the outImages
+	 */
+	public InMemoryImageCollection getOutImages() {
+		return outImages;
+	}
+
+
+	public BufferedImage cropToRectangle(BufferedImage inImage, Rectangle rectangle) {
+		return inImage.getSubimage(rectangle.x, rectangle.y, rectangle.width - 15,rectangle.height);
+	}
+
+}
+
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FilePathHolder.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FilePathHolder.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FilePathHolder.java	(revision 0)
@@ -0,0 +1,7 @@
+package org.openqa.selenium.server.imagecapture;
+
+public class FilePathHolder {
+	public static String getPath() {
+		return "/home/tarjei/ifi/MASTER/src/imagelib/sandbox/selenium-rc/server-coreless/src/test/resources/imagecapture";
+	}
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/PageImageCollection.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/PageImageCollection.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/PageImageCollection.java	(revision 0)
@@ -0,0 +1,24 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.Iterator;
+
+public interface PageImageCollection {
+
+	public BufferedImage getCalibrationImage() throws IOException;
+	public void setCalibrationImage(BufferedImage image);
+	public Iterator<BufferedImage> iterator() throws IOException;
+	/**
+	 * Adds an image to the collection
+	 * @param image
+	 */
+	public void addImage(BufferedImage image);
+	/**
+	 * Removes the images (but not the calibaration image) in the collection
+	 *
+	 */
+	public void removeImages();
+
+	public void saveAndReload() throws IOException;
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/PrintUtils.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/PrintUtils.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/PrintUtils.java	(revision 0)
@@ -0,0 +1,22 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.Rectangle;
+import java.awt.image.Raster;
+
+public class PrintUtils {
+
+	public static void imageInfo(java.awt.image.BufferedImage image) {
+		Raster raster = image.getData();
+		System.out.print("Dimensions: ");
+		System.out.print(raster.getWidth()+"x"+raster.getHeight()+" pixels. ");
+		System.out.print(" (from "+raster.getMinX()+","+raster.getMinY());
+		System.out.print(" to "+raster.getWidth()+","+raster.getHeight() + ") ");
+		System.out.println("Colormodel: " + image.getColorModel().getClass());
+		System.out.println("Type: " + image.getType());
+	}
+
+	public static void rectangleInfo(Rectangle r) {
+		System.out.println(
+		"[" + r.x + ", " + r.y + "] to [" + r.width + ", " + r.height + "]");
+	}
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/InMemoryImageCollection.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/InMemoryImageCollection.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/InMemoryImageCollection.java	(revision 0)
@@ -0,0 +1,91 @@
+/**
+ *
+ */
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+
+/**
+ * @author tarjei
+ * A collection of bufferedImage objects kept in memory.
+ *
+ */
+public class InMemoryImageCollection implements PageImageCollection {
+
+	BufferedImage calibrationImage = null;
+	java.util.List<BufferedImage> images = new ArrayList<BufferedImage>();
+	/**
+	 *
+	 */
+	public InMemoryImageCollection() {	}
+
+	/**
+	 * @see no.kraken.imgops.PageImageCollection#getCalibrationImage()
+	 */
+	public BufferedImage getCalibrationImage() throws IOException {
+		return calibrationImage;
+	}
+	/**
+	 * Sets the calibration image
+	 * @param image
+	 */
+	public void setCalibrationImage(BufferedImage image) {
+		calibrationImage = image;
+	}
+
+	public void addImage(BufferedImage image ) {
+		images.add(image);
+	}
+	public void removeImages() {
+		images.clear();
+	}
+	/**
+	 * @see no.kraken.imgops.PageImageCollection#iterator()
+	 */
+	public Iterator<BufferedImage> iterator() throws IOException {
+		return images.iterator();
+	}
+	/**
+	 * Saves the files to a directory with the form
+	 * outimg-$i.png where $i is the images number in the list.
+	 * @param path
+	 * @throws IOException
+	 */
+	public void saveCollectionToPath(String path) throws IOException {
+		Iterator<BufferedImage> it = images.iterator();
+		for(int i = 0; it.hasNext(); i++) {
+			File n = new File(path + "/Saved-outimg-" + i + ".png");
+			ImageIO.write(it.next(), "png", n);
+		}
+	}
+
+	public void saveCalibrationImageToPath(String path)
+	throws IOException {
+		File n = new File(path);
+		ImageIO.write(getCalibrationImage(), "png",n );
+	}
+
+	@Override
+	public void saveAndReload() throws IOException {
+		Iterator <BufferedImage> it = iterator();
+		java.util.List<BufferedImage> newImages = new ArrayList<BufferedImage>();
+		File n;
+		while (it.hasNext()) {
+			n = File.createTempFile("tmpimg", "");
+			ImageIO.write(it.next(), "png", n );
+			newImages.add(ImageIO.read(n));
+			it.remove();
+            n.delete();
+		}
+		images = newImages;
+
+	}
+
+}
Index: server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FileImageCollection.java
===================================================================
--- server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FileImageCollection.java	(revision 0)
+++ server-coreless/src/main/java/org/openqa/selenium/server/imagecapture/FileImageCollection.java	(revision 0)
@@ -0,0 +1,88 @@
+package org.openqa.selenium.server.imagecapture;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.imageio.ImageIO;
+
+public class FileImageCollection implements PageImageCollection {
+
+	private String pathPattern;
+
+	FileImageCollection (String pathPattern) {
+		this.pathPattern = pathPattern;
+	}
+
+	@Override
+	public BufferedImage getCalibrationImage() throws IOException {
+		return loadImage(pathPattern.replace('X', 'c'));
+	}
+
+	private BufferedImage loadImage(String fileName) throws IOException {
+		File f = new File(fileName);
+		if (! f.exists()) throw new IOException("Cannot find file:" + fileName);
+		return ImageIO.read(f);
+	}
+
+	@Override
+	public Iterator<BufferedImage> iterator() throws IOException {
+		ArrayList <BufferedImage>it = new ArrayList<BufferedImage>();
+		File f;
+		for (int i= 0; ; i++) {
+			f = new File(pathPattern.replaceFirst("X", i + ""));
+			if (! f.exists()) break;
+			it.add( ImageIO.read(f));
+		}
+		return it.iterator();
+	}
+
+	@Override
+	public void addImage(BufferedImage image) {
+		int i;
+		File f;
+		for (i= 0; ; i++) {
+			f = new File(pathPattern.replaceFirst("X", i + ""));
+			if (! f.exists()) break;
+		}
+		try {
+			ImageIO.write(image, "png", f);
+		} catch (IOException e) {
+			// TODO add throws
+			e.printStackTrace();
+		}
+
+	}
+
+	@Override
+	public void setCalibrationImage(BufferedImage image) {
+		File f = new File(pathPattern.replaceFirst("X", "c"));
+		try {
+			ImageIO.write(image, "png", f);
+		} catch (IOException e) {
+			// TODO throw exception
+			e.printStackTrace();
+		}
+
+	}
+
+	public void removeImages() {
+		int i;
+		File f;
+		for (i= 0; ; i++) {
+			f = new File(pathPattern.replaceFirst("X", i + ""));
+			if (f.exists()) {
+				f.delete();
+			}
+		}
+	}
+
+	@Override
+	public void saveAndReload() throws IOException {
+		// doesn't do anything.
+
+	}
+
+}
Index: server-coreless/src/main/resources/imagecapture/imagecapture.js
===================================================================
--- server-coreless/src/main/resources/imagecapture/imagecapture.js	(revision 0)
+++ server-coreless/src/main/resources/imagecapture/imagecapture.js	(revision 0)
@@ -0,0 +1,77 @@
+/**
+ * Functions used by the imagecapture command.
+ * TODO: find a better way to organize them.
+ */
+// getPageSize()
+// Returns array with page width, height and window width, height
+// from: http://forum.oscandy.com/topic/570
+ /* All code copyright 2007 Taras Mankovski */
+/* email: tarasm@gmail.com */
+/* website: http://www.oscandy.com/author/taras */
+
+function getPageSize(wind, doc){
+
+    var xScroll, yScroll, pageHeight, pageWidth;
+
+    if (wind.innerHeight && wind.scrollMaxY) {
+        xScroll = doc.body.scrollWidth;
+        yScroll = wind.innerHeight + wind.scrollMaxY;
+    } else if (doc.body.scrollHeight > doc.body.offsetHeight){ // all but Explorer Mac
+        xScroll = doc.body.scrollWidth;
+        yScroll = doc.body.scrollHeight;
+    } else { // Explorer Mac...would also work in Explorer 6 Strict, Mozilla and Safari
+        xScroll = doc.body.offsetWidth;
+        yScroll = doc.body.offsetHeight;
+    }
+
+    var windowWidth, windowHeight;
+    if (self.innerHeight) { // all except Explorer
+        windowWidth = self.innerWidth;
+        windowHeight = self.innerHeight;
+    } else if (doc.documentElement && doc.documentElement.clientHeight) { // Explorer 6 Strict Mode
+        windowWidth = doc.documentElement.clientWidth;
+        windowHeight = doc.documentElement.clientHeight;
+    } else if (doc.body) { // other Explorers
+        windowWidth = doc.body.clientWidth;
+        windowHeight = doc.body.clientHeight;
+    }
+
+    // for small pages with total height less then height of the viewport
+    if(yScroll < windowHeight){
+        pageHeight = windowHeight;
+    } else {
+        pageHeight = yScroll;
+    }
+
+    // for small pages with total width less then width of the viewport
+    if(xScroll < windowWidth){
+        pageWidth = windowWidth;
+    } else {
+        pageWidth = xScroll;
+    }
+    return pageWidth + " " + pageHeight + " " + windowWidth+ " " + windowHeight;
+
+}
+
+function getSeleniumPageSize() {
+    return getPageSize(window, document);
+};
+
+function scroll(y) {
+    /*
+    var bot = window.selenium.browserbot;
+    var wind = bot.topFrame;
+    wind.scroll(0,y);
+    * */
+    window.scroll(0,y);
+    return "OK";
+};
+
+function blankScreen() {
+	var root = window.selenium.browserbot.topFrame;
+    root.document.open();
+    root.document.write("<html><head></head><body></body></html>");
+    root.document.close();
+    return "OK";
+}
+
