Эх сурвалжийг харах

Line endings fixed back to unix format.

Charles Howes 12 жил өмнө
parent
commit
6936c64ad3

+ 14 - 14
driver-example.pl

@@ -1,14 +1,14 @@
-#!/usr/bin/perl
-use Selenium::Remote::Driver;
-use Test::More tests=>4;
-
-my $driver = Selenium::Remote::Driver->new;
-$driver->get("http://www.google.com");
-$driver->find_element('q','name')->send_keys("Hello WebDriver!");
-
-ok($driver->get_title =~ /Google/,"title matches google");
-is($driver->get_title,'Google',"Title is google");
-ok($driver->get_title eq 'Google','Title equals google');
-like($driver->get_title,qr/Google/,"Title matches google");
-
-$driver->quit();
+#!/usr/bin/perl
+use Selenium::Remote::Driver;
+use Test::More tests=>4;
+
+my $driver = Selenium::Remote::Driver->new;
+$driver->get("http://www.google.com");
+$driver->find_element('q','name')->send_keys("Hello WebDriver!");
+
+ok($driver->get_title =~ /Google/,"title matches google");
+is($driver->get_title,'Google',"Title is google");
+ok($driver->get_title eq 'Google','Title equals google');
+like($driver->get_title,qr/Google/,"Title matches google");
+
+$driver->quit();

+ 399 - 399
ide-plugin.js

@@ -1,399 +1,399 @@
-/*
- * Formatter for Selenium 2 / WebDriver perl-rc client.
- * To install...
-    1. Open the Selenium IDE
-    2. Options >> Options
-    3. Formats Tab
-    4. Click Add at the bottom
-    5. In the name field call it 'Perl-Webdriver'
-    6. Paste this entire source in the main textbox
-    7. Click 'Save'
-    8. Click 'Ok'
- */
-
-var subScriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
-subScriptLoader.loadSubScript('chrome://selenium-ide/content/formats/webdriver.js', this);
-
-function testClassName(testName) {
-  return testName.split(/[^0-9A-Za-z]+/).map(
-      function(x) {
-        return capitalize(x);
-      }).join('');
-}
-
-function testMethodName(testName) {
-  return "test_" + underscore(testName);
-}
-
-function nonBreakingSpace() {
-  return "\"\\xa0\"";
-}
-
-function array(value) {
-  var str = '[';
-  for (var i = 0; i < value.length; i++) {
-    str += string(value[i]);
-    if (i < value.length - 1) str += ", ";
-  }
-  str += ']';
-  return str;
-}
-
-notOperator = function() {
-  return "not ";
-};
-
-Equals.prototype.toString = function() {
-  return this.e2.toString() + " == " + this.e1.toString();
-};
-
-Equals.prototype.assert = function() {
-  return statement("is(" + this.e2.toString() + "," + this.e1.toString() + ")");
-};
-
-Equals.prototype.verify = function() {
-  return verify(this.assert());
-};
-
-NotEquals.prototype.toString = function() {
-  return this.e1.toString() + " != " + this.e2.toString();
-};
-
-NotEquals.prototype.assert = function() {
-  return statement("isnt(" + this.e2.toString() + "," + this.e1.toString() + ")");
-};
-
-NotEquals.prototype.verify = function() {
-  return verify(this.assert());
-};
-
-function joinExpression(expression) {
-  return "join(\",\"," + expression.toString() + ")";
-}
-
-function statement(expression) {
-  expression.noBraces = true;
-  var s = expression.toString();
-  if(s.length == 0) {
-    return null;
-  }
-  return s + ';';
-}
-
-function assignToVariable(type, variable, expression) {
-  return variable + " = " + expression.toString();
-}
-
-function ifCondition(expression, callback) {
-  return "if (" + expression.toString() + ") {\n" + callback() + "}";
-}
-
-function tryCatch(tryStatement, catchStatement, exception) {
-  return "eval {\n" +
-      indents(1) + tryStatement + "\n" +
-      "};\n if ($@) {\n" +
-      indents(1) + catchStatement + "\n" +
-      "}";
-}
-
-function assertTrue(expression) {
-  var exp = expression.toString();
-  //var r = exp.match(/^(.+)\.([0-9A-Za-z_]+)\?$/);
-  //if (r && r.length == 3) {
-  //  return "ok(" + r[1] + ".should be_" + r[2];
-  //} else {
-    return statement("ok(" + exp + ")");
-  //}
-}
-
-function assertFalse(expression) {
-  //return expression.invert().toString() + ".should be_false";
-  var exp = expression.toString();
-  //var r = exp.match(/^(.+)\.([0-9A-Za-z_]+)\?$/);
-  //if (r && r.length == 3) {
-  //  return r[1] + ".should_not be_" + r[2];
-  //} else {
-    return statement("ok(!" + exp + ")");
-  //}
-}
-
-function verify(stmt) {
-  return stmt;
-}
-
-function verifyTrue(expression) {
-  return verify(assertTrue(expression));
-}
-
-function verifyFalse(expression) {
-  return verify(assertFalse(expression));
-}
-
-RegexpMatch.patternAsRegEx = function(pattern) {
-  var str = pattern.replace(/\//g, "\\/");
-  if (str.match(/\n/)) {
-    str = str.replace(/\n/g, '\\n');
-    return '/' + str + '/m';
-  } else {
-    return str = '/' + str + '/';
-  }
-};
-
-RegexpMatch.prototype.patternAsRegEx = function() {
-  return RegexpMatch.patternAsRegEx(this.pattern);
-};
-
-RegexpMatch.prototype.toString = function() {
-  return this.expression + " =~ " + this.patternAsRegEx();
-};
-
-RegexpMatch.prototype.assert = function() {
-  return statement("like(qr" + this.patternAsRegEx() + "," + this.expression + ")");
-};
-
-RegexpMatch.prototype.verify = function() {
-  return verify(this.assert());
-};
-
-RegexpNotMatch.prototype.patternAsRegEx = function() {
-  return RegexpMatch.patternAsRegEx(this.pattern);
-};
-
-RegexpNotMatch.prototype.toString = function() {
-  return this.expression + " !~ " + this.patternAsRegEx();
-};
-
-RegexpNotMatch.prototype.assert = function() {
-  return statement("unlike(qr" + this.patternAsRegEx() + "," + this.expression + ")");
-};
-
-RegexpNotMatch.prototype.verify = function() {
-  return verify(this.assert());
-};
-
-function waitFor(expression) {
-  if (expression.negative) {
-    return "for(0..60) { my $ret = 1; eval { $ret = (" + expression.invert().toString() + ") }; if($@ || !$ret) { break }; sleep 1 }"
-  } else {
-    return "!60.times{ break if (" + expression.toString() + " rescue false); sleep 1 }"
-  }
-}
-
-function assertOrVerifyFailure(line, isAssert) {
-  return "assert_raise(Kernel) { " + line + "}";
-}
-
-function pause(milliseconds) {
-  return "sleep " + (parseInt(milliseconds) / 1000);
-}
-
-function echo(message) {
-  return "note " + xlateArgument(message);
-}
-
-function formatComment(comment) {
-  return comment.comment.replace(/.+/mg, function(str) {
-    return "# " + str;
-  });
-}
-
-/**
- * Returns a string representing the suite for this formatter language.
- *
- * @param testSuite  the suite to format
- * @param filename   the file the formatted suite will be saved as
- */
-function formatSuite(testSuite, filename) {
-  formattedSuite = 'require "spec/ruby"\n' +
-      'require "spec/runner"\n' +
-      '\n' +
-      "# output T/F as Green/Red\n" +
-      "ENV['RSPEC_COLOR'] = 'true'\n" +
-      '\n';
-
-  for (var i = 0; i < testSuite.tests.length; ++i) {
-    // have saved or loaded a suite
-    if (typeof testSuite.tests[i].filename != 'undefined') {
-      formattedSuite += 'require File.join(File.dirname(__FILE__),  "' + testSuite.tests[i].filename.replace(/\.\w+$/, '') + '")\n';
-    } else {
-      // didn't load / save as a suite
-      var testFile = testSuite.tests[i].getTitle();
-      formattedSuite += 'require "' + testFile + '"\n';
-    }
-  }
-  return formattedSuite;
-}
-
-this.options = {
-  receiver: "$driver",
-  rcHost: "localhost",
-  rcPort: "4444",
-  environment: "firefox",
-  showSelenese: 'false',
-  header:
-      "use strict;\n" +
-      "use warnings;\n" +
-      "use Selenium::Remote::Driver;\n" +
-      "use Test::More;\n" +
-      "\n" +
-      'my ${receiver} = Selenium::Remote::Driver->new( remote_server_addr => "${rcHost}",\n' +
-      '                                               port => ${rcPort},\n' +
-      '                                               browser_name => "${environment}");\n' +
-      "\n",
-  footer:
-      "$driver->quit();\n" +
-      "done_testing();\n",
-  indent: "0",
-  initialIndents: "0"
-};
-
-this.configForm =
-    '<description>Variable for Selenium instance</description>' +
-        '<textbox id="options_receiver" />' +
-        '<description>Selenium RC host</description>' +
-        '<textbox id="options_rcHost" />' +
-        '<description>Selenium RC port</description>' +
-        '<textbox id="options_rcPort" />' +
-        '<description>Environment</description>' +
-        '<textbox id="options_environment" />' +
-        '<description>Header</description>' +
-        '<textbox id="options_header" multiline="true" flex="1" rows="4"/>' +
-        '<description>Footer</description>' +
-        '<textbox id="options_footer" multiline="true" flex="1" rows="4"/>' +
-        '<description>Indent</description>' +
-        '<menulist id="options_indent"><menupopup>' +
-        '<menuitem label="Tab" value="tab"/>' +
-        '<menuitem label="1 space" value="1"/>' +
-        '<menuitem label="2 spaces" value="2"/>' +
-        '<menuitem label="3 spaces" value="3"/>' +
-        '<menuitem label="4 spaces" value="4"/>' +
-        '<menuitem label="5 spaces" value="5"/>' +
-        '<menuitem label="6 spaces" value="6"/>' +
-        '<menuitem label="7 spaces" value="7"/>' +
-        '<menuitem label="8 spaces" value="8"/>' +
-        '</menupopup></menulist>' +
-        '<checkbox id="options_showSelenese" label="Show Selenese"/>';
-
-this.name = "Perl Test::More(WebDriver)";
-this.testcaseExtension = ".t";
-this.suiteExtension = ".t";
-this.webdriver = true;
-
-WDAPI.Driver = function() {
-  this.ref = options.receiver;
-};
-
-WDAPI.Driver.searchContext = function(locatorType, locator) {
-  var locatorString = xlateArgument(locator).replace(/([@%$])/,"\\$1");
-  switch (locatorType) {
-    case 'xpath':
-      return locatorString + ', "xpath"';
-    case 'css':
-      return locatorString + ', "css"';
-    case 'id':
-      return locatorString + ', "id"';
-    case 'link':
-      return locatorString + ', "link"';
-    case 'name':
-      return locatorString + ', "name"';
-    case 'tag_name':
-      return locatorString + ', "tag_name"';
-  }
-  throw 'Error: unknown strategy [' + locatorType + '] for locator [' + locator + ']';
-};
-
-WDAPI.Driver.prototype.back = function() {
-  return this.ref + "->navigate->back";
-};
-
-WDAPI.Driver.prototype.close = function() {
-  return this.ref + "->close";
-};
-
-WDAPI.Driver.prototype.findElement = function(locatorType, locator) {
-  return new WDAPI.Element(this.ref + "->find_element(" + WDAPI.Driver.searchContext(locatorType, locator) + ")");
-};
-
-WDAPI.Driver.prototype.findElements = function(locatorType, locator) {
-  return new WDAPI.ElementList(this.ref + "->find_elements(" + WDAPI.Driver.searchContext(locatorType, locator) + ")");
-};
-
-WDAPI.Driver.prototype.getCurrentUrl = function() {
-  return this.ref + "->get_current_url";
-};
-
-WDAPI.Driver.prototype.get = function(url) {
-  return this.ref + "->get(" + url + ")";
-};
-
-WDAPI.Driver.prototype.getTitle = function() {
-  return this.ref + "->get_title";
-};
-
-WDAPI.Driver.prototype.refresh = function() {
-  return this.ref + "->refresh";
-};
-
-WDAPI.Driver.prototype.frame = function(locator) {
-  return this.ref + "->switch_to_frame(" + xlateArgument(locator) + ")";
-}
-
-WDAPI.Element = function(ref) {
-  this.ref = ref;
-};
-
-WDAPI.Element.prototype.clear = function() {
-  return this.ref + "->clear";
-};
-
-WDAPI.Element.prototype.click = function() {
-  return this.ref + "->click";
-};
-
-WDAPI.Element.prototype.getAttribute = function(attributeName) {
-  return this.ref + "->attribute(" + xlateArgument(attributeName) + ")";
-};
-
-WDAPI.Element.prototype.getText = function() {
-  return this.ref + "->get_text";
-};
-
-WDAPI.Element.prototype.isDisplayed = function() {
-  return this.ref + "->is_displayed";
-};
-
-WDAPI.Element.prototype.isSelected = function() {
-  return this.ref + "->is_selected";
-};
-
-WDAPI.Element.prototype.sendKeys = function(text) {
-  return this.ref + "->send_keys(" + xlateArgument(text) + ")";
-};
-
-WDAPI.Element.prototype.submit = function() {
-  return this.ref + "->submit";
-};
-
-WDAPI.ElementList = function(ref) {
-  this.ref = ref;
-};
-
-WDAPI.ElementList.prototype.getItem = function(index) {
-  return this.ref + "[" + index + "]";
-};
-
-WDAPI.ElementList.prototype.getSize = function() {
-  return this.ref + "->size";
-};
-
-WDAPI.ElementList.prototype.isEmpty = function() {
-  return this.ref + "->is_empty";
-};
-
-
-WDAPI.Utils = function() {
-};
-
-WDAPI.Utils.isElementPresent = function(how, what) {
-  return this.ref + "->is_element_present(" + xlateArgument(what) + ", \"" + how + "\")";
-};
+/*
+ * Formatter for Selenium 2 / WebDriver perl-rc client.
+ * To install...
+    1. Open the Selenium IDE
+    2. Options >> Options
+    3. Formats Tab
+    4. Click Add at the bottom
+    5. In the name field call it 'Perl-Webdriver'
+    6. Paste this entire source in the main textbox
+    7. Click 'Save'
+    8. Click 'Ok'
+ */
+
+var subScriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"].getService(Components.interfaces.mozIJSSubScriptLoader);
+subScriptLoader.loadSubScript('chrome://selenium-ide/content/formats/webdriver.js', this);
+
+function testClassName(testName) {
+  return testName.split(/[^0-9A-Za-z]+/).map(
+      function(x) {
+        return capitalize(x);
+      }).join('');
+}
+
+function testMethodName(testName) {
+  return "test_" + underscore(testName);
+}
+
+function nonBreakingSpace() {
+  return "\"\\xa0\"";
+}
+
+function array(value) {
+  var str = '[';
+  for (var i = 0; i < value.length; i++) {
+    str += string(value[i]);
+    if (i < value.length - 1) str += ", ";
+  }
+  str += ']';
+  return str;
+}
+
+notOperator = function() {
+  return "not ";
+};
+
+Equals.prototype.toString = function() {
+  return this.e2.toString() + " == " + this.e1.toString();
+};
+
+Equals.prototype.assert = function() {
+  return statement("is(" + this.e2.toString() + "," + this.e1.toString() + ")");
+};
+
+Equals.prototype.verify = function() {
+  return verify(this.assert());
+};
+
+NotEquals.prototype.toString = function() {
+  return this.e1.toString() + " != " + this.e2.toString();
+};
+
+NotEquals.prototype.assert = function() {
+  return statement("isnt(" + this.e2.toString() + "," + this.e1.toString() + ")");
+};
+
+NotEquals.prototype.verify = function() {
+  return verify(this.assert());
+};
+
+function joinExpression(expression) {
+  return "join(\",\"," + expression.toString() + ")";
+}
+
+function statement(expression) {
+  expression.noBraces = true;
+  var s = expression.toString();
+  if(s.length == 0) {
+    return null;
+  }
+  return s + ';';
+}
+
+function assignToVariable(type, variable, expression) {
+  return variable + " = " + expression.toString();
+}
+
+function ifCondition(expression, callback) {
+  return "if (" + expression.toString() + ") {\n" + callback() + "}";
+}
+
+function tryCatch(tryStatement, catchStatement, exception) {
+  return "eval {\n" +
+      indents(1) + tryStatement + "\n" +
+      "};\n if ($@) {\n" +
+      indents(1) + catchStatement + "\n" +
+      "}";
+}
+
+function assertTrue(expression) {
+  var exp = expression.toString();
+  //var r = exp.match(/^(.+)\.([0-9A-Za-z_]+)\?$/);
+  //if (r && r.length == 3) {
+  //  return "ok(" + r[1] + ".should be_" + r[2];
+  //} else {
+    return statement("ok(" + exp + ")");
+  //}
+}
+
+function assertFalse(expression) {
+  //return expression.invert().toString() + ".should be_false";
+  var exp = expression.toString();
+  //var r = exp.match(/^(.+)\.([0-9A-Za-z_]+)\?$/);
+  //if (r && r.length == 3) {
+  //  return r[1] + ".should_not be_" + r[2];
+  //} else {
+    return statement("ok(!" + exp + ")");
+  //}
+}
+
+function verify(stmt) {
+  return stmt;
+}
+
+function verifyTrue(expression) {
+  return verify(assertTrue(expression));
+}
+
+function verifyFalse(expression) {
+  return verify(assertFalse(expression));
+}
+
+RegexpMatch.patternAsRegEx = function(pattern) {
+  var str = pattern.replace(/\//g, "\\/");
+  if (str.match(/\n/)) {
+    str = str.replace(/\n/g, '\\n');
+    return '/' + str + '/m';
+  } else {
+    return str = '/' + str + '/';
+  }
+};
+
+RegexpMatch.prototype.patternAsRegEx = function() {
+  return RegexpMatch.patternAsRegEx(this.pattern);
+};
+
+RegexpMatch.prototype.toString = function() {
+  return this.expression + " =~ " + this.patternAsRegEx();
+};
+
+RegexpMatch.prototype.assert = function() {
+  return statement("like(qr" + this.patternAsRegEx() + "," + this.expression + ")");
+};
+
+RegexpMatch.prototype.verify = function() {
+  return verify(this.assert());
+};
+
+RegexpNotMatch.prototype.patternAsRegEx = function() {
+  return RegexpMatch.patternAsRegEx(this.pattern);
+};
+
+RegexpNotMatch.prototype.toString = function() {
+  return this.expression + " !~ " + this.patternAsRegEx();
+};
+
+RegexpNotMatch.prototype.assert = function() {
+  return statement("unlike(qr" + this.patternAsRegEx() + "," + this.expression + ")");
+};
+
+RegexpNotMatch.prototype.verify = function() {
+  return verify(this.assert());
+};
+
+function waitFor(expression) {
+  if (expression.negative) {
+    return "for(0..60) { my $ret = 1; eval { $ret = (" + expression.invert().toString() + ") }; if($@ || !$ret) { break }; sleep 1 }"
+  } else {
+    return "!60.times{ break if (" + expression.toString() + " rescue false); sleep 1 }"
+  }
+}
+
+function assertOrVerifyFailure(line, isAssert) {
+  return "assert_raise(Kernel) { " + line + "}";
+}
+
+function pause(milliseconds) {
+  return "sleep " + (parseInt(milliseconds) / 1000);
+}
+
+function echo(message) {
+  return "note " + xlateArgument(message);
+}
+
+function formatComment(comment) {
+  return comment.comment.replace(/.+/mg, function(str) {
+    return "# " + str;
+  });
+}
+
+/**
+ * Returns a string representing the suite for this formatter language.
+ *
+ * @param testSuite  the suite to format
+ * @param filename   the file the formatted suite will be saved as
+ */
+function formatSuite(testSuite, filename) {
+  formattedSuite = 'require "spec/ruby"\n' +
+      'require "spec/runner"\n' +
+      '\n' +
+      "# output T/F as Green/Red\n" +
+      "ENV['RSPEC_COLOR'] = 'true'\n" +
+      '\n';
+
+  for (var i = 0; i < testSuite.tests.length; ++i) {
+    // have saved or loaded a suite
+    if (typeof testSuite.tests[i].filename != 'undefined') {
+      formattedSuite += 'require File.join(File.dirname(__FILE__),  "' + testSuite.tests[i].filename.replace(/\.\w+$/, '') + '")\n';
+    } else {
+      // didn't load / save as a suite
+      var testFile = testSuite.tests[i].getTitle();
+      formattedSuite += 'require "' + testFile + '"\n';
+    }
+  }
+  return formattedSuite;
+}
+
+this.options = {
+  receiver: "$driver",
+  rcHost: "localhost",
+  rcPort: "4444",
+  environment: "firefox",
+  showSelenese: 'false',
+  header:
+      "use strict;\n" +
+      "use warnings;\n" +
+      "use Selenium::Remote::Driver;\n" +
+      "use Test::More;\n" +
+      "\n" +
+      'my ${receiver} = Selenium::Remote::Driver->new( remote_server_addr => "${rcHost}",\n' +
+      '                                               port => ${rcPort},\n' +
+      '                                               browser_name => "${environment}");\n' +
+      "\n",
+  footer:
+      "$driver->quit();\n" +
+      "done_testing();\n",
+  indent: "0",
+  initialIndents: "0"
+};
+
+this.configForm =
+    '<description>Variable for Selenium instance</description>' +
+        '<textbox id="options_receiver" />' +
+        '<description>Selenium RC host</description>' +
+        '<textbox id="options_rcHost" />' +
+        '<description>Selenium RC port</description>' +
+        '<textbox id="options_rcPort" />' +
+        '<description>Environment</description>' +
+        '<textbox id="options_environment" />' +
+        '<description>Header</description>' +
+        '<textbox id="options_header" multiline="true" flex="1" rows="4"/>' +
+        '<description>Footer</description>' +
+        '<textbox id="options_footer" multiline="true" flex="1" rows="4"/>' +
+        '<description>Indent</description>' +
+        '<menulist id="options_indent"><menupopup>' +
+        '<menuitem label="Tab" value="tab"/>' +
+        '<menuitem label="1 space" value="1"/>' +
+        '<menuitem label="2 spaces" value="2"/>' +
+        '<menuitem label="3 spaces" value="3"/>' +
+        '<menuitem label="4 spaces" value="4"/>' +
+        '<menuitem label="5 spaces" value="5"/>' +
+        '<menuitem label="6 spaces" value="6"/>' +
+        '<menuitem label="7 spaces" value="7"/>' +
+        '<menuitem label="8 spaces" value="8"/>' +
+        '</menupopup></menulist>' +
+        '<checkbox id="options_showSelenese" label="Show Selenese"/>';
+
+this.name = "Perl Test::More(WebDriver)";
+this.testcaseExtension = ".t";
+this.suiteExtension = ".t";
+this.webdriver = true;
+
+WDAPI.Driver = function() {
+  this.ref = options.receiver;
+};
+
+WDAPI.Driver.searchContext = function(locatorType, locator) {
+  var locatorString = xlateArgument(locator).replace(/([@%$])/,"\\$1");
+  switch (locatorType) {
+    case 'xpath':
+      return locatorString + ', "xpath"';
+    case 'css':
+      return locatorString + ', "css"';
+    case 'id':
+      return locatorString + ', "id"';
+    case 'link':
+      return locatorString + ', "link"';
+    case 'name':
+      return locatorString + ', "name"';
+    case 'tag_name':
+      return locatorString + ', "tag_name"';
+  }
+  throw 'Error: unknown strategy [' + locatorType + '] for locator [' + locator + ']';
+};
+
+WDAPI.Driver.prototype.back = function() {
+  return this.ref + "->navigate->back";
+};
+
+WDAPI.Driver.prototype.close = function() {
+  return this.ref + "->close";
+};
+
+WDAPI.Driver.prototype.findElement = function(locatorType, locator) {
+  return new WDAPI.Element(this.ref + "->find_element(" + WDAPI.Driver.searchContext(locatorType, locator) + ")");
+};
+
+WDAPI.Driver.prototype.findElements = function(locatorType, locator) {
+  return new WDAPI.ElementList(this.ref + "->find_elements(" + WDAPI.Driver.searchContext(locatorType, locator) + ")");
+};
+
+WDAPI.Driver.prototype.getCurrentUrl = function() {
+  return this.ref + "->get_current_url";
+};
+
+WDAPI.Driver.prototype.get = function(url) {
+  return this.ref + "->get(" + url + ")";
+};
+
+WDAPI.Driver.prototype.getTitle = function() {
+  return this.ref + "->get_title";
+};
+
+WDAPI.Driver.prototype.refresh = function() {
+  return this.ref + "->refresh";
+};
+
+WDAPI.Driver.prototype.frame = function(locator) {
+  return this.ref + "->switch_to_frame(" + xlateArgument(locator) + ")";
+}
+
+WDAPI.Element = function(ref) {
+  this.ref = ref;
+};
+
+WDAPI.Element.prototype.clear = function() {
+  return this.ref + "->clear";
+};
+
+WDAPI.Element.prototype.click = function() {
+  return this.ref + "->click";
+};
+
+WDAPI.Element.prototype.getAttribute = function(attributeName) {
+  return this.ref + "->attribute(" + xlateArgument(attributeName) + ")";
+};
+
+WDAPI.Element.prototype.getText = function() {
+  return this.ref + "->get_text";
+};
+
+WDAPI.Element.prototype.isDisplayed = function() {
+  return this.ref + "->is_displayed";
+};
+
+WDAPI.Element.prototype.isSelected = function() {
+  return this.ref + "->is_selected";
+};
+
+WDAPI.Element.prototype.sendKeys = function(text) {
+  return this.ref + "->send_keys(" + xlateArgument(text) + ")";
+};
+
+WDAPI.Element.prototype.submit = function() {
+  return this.ref + "->submit";
+};
+
+WDAPI.ElementList = function(ref) {
+  this.ref = ref;
+};
+
+WDAPI.ElementList.prototype.getItem = function(index) {
+  return this.ref + "[" + index + "]";
+};
+
+WDAPI.ElementList.prototype.getSize = function() {
+  return this.ref + "->size";
+};
+
+WDAPI.ElementList.prototype.isEmpty = function() {
+  return this.ref + "->is_empty";
+};
+
+
+WDAPI.Utils = function() {
+};
+
+WDAPI.Utils.isElementPresent = function(how, what) {
+  return this.ref + "->is_element_present(" + xlateArgument(what) + ", \"" + how + "\")";
+};

+ 358 - 358
lib/Selenium/Remote/Commands.pm

@@ -1,358 +1,358 @@
-package Selenium::Remote::Commands;
-
-use strict;
-use warnings;
-
-sub new {
-    my $class = shift;
-    
-    # http://code.google.com/p/selenium/wiki/JsonWireProtocol
-    my $self = {
-        'status'     => {
-                          'method' => 'GET',
-                          'url'    => 'status'
-        },
-        'newSession' => {
-                          'method' => 'POST',
-                          'url'    => 'session'
-        },
-        'getSessions' => {
-                          'method' => 'GET',
-                          'url'    => 'sessions'
-        },
-        'getCapabilities' => {
-                          'method' => 'GET',
-                          'url'    => 'session/:sessionId'
-        },
-        'setTimeout' => {
-                        'method' => 'POST',
-                        'url'    => 'session/:sessionId/timeouts'
-        },
-        'setAsyncScriptTimeout' => {
-                        'method' => 'POST',
-                        'url'    => 'session/:sessionId/timeouts/async_script'
-        },
-        'setImplicitWaitTimeout' => {
-                        'method' => 'POST',
-                        'url'    => 'session/:sessionId/timeouts/implicit_wait'
-        },
-        'quit' => {
-                    'method' => 'DELETE',
-                    'url'    => "session/:sessionId"
-        },
-        'getCurrentWindowHandle' => {
-                 'method' => 'GET',
-                 'url' => "session/:sessionId/window_handle"
-        },
-        'getWindowHandles' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/window_handles"
-        },
-        'getWindowSize' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/window/:windowHandle/size"
-        },
-        'getWindowPosition' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/window/:windowHandle/position"
-        },
-        'setWindowSize' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/window/:windowHandle/size"
-        },
-        'setWindowPosition' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/window/:windowHandle/position"
-        },
-        'getCurrentUrl' => {
-                           'method' => 'GET',
-                           'url' => "session/:sessionId/url"
-        },
-        'get' => {
-                   'method' => 'POST',
-                   'url'    => "session/:sessionId/url"
-        },
-        'goForward' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/forward"
-        },
-        'goBack' => {
-                      'method' => 'POST',
-                      'url'    => "session/:sessionId/back"
-        },
-        'refresh' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/refresh"
-        },
-        'executeScript' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/execute"
-        },
-        'executeAsyncScript' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/execute_async"
-        },
-        'screenshot' => {
-                    'method' => 'GET',
-                    'url' => "session/:sessionId/screenshot"
-        },
-        'availableEngines' => {
-                    'method' => 'GET',
-                    'url' => "session/:sessionId/ime/available_engines"
-        },
-        'switchToFrame' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/frame"
-        },
-        'switchToWindow' => {
-             'method' => 'POST',
-             'url' => "session/:sessionId/window"
-        },
-        'getAllCookies' => {
-                        'method' => 'GET',
-                        'url' => "session/:sessionId/cookie"
-        },
-        'addCookie' => {
-                        'method' => 'POST',
-                        'url' => "session/:sessionId/cookie"
-        },
-        'deleteAllCookies' => {
-                        'method' => 'DELETE',
-                        'url' => "session/:sessionId/cookie"
-        },
-        'deleteCookieNamed' => {
-             'method' => 'DELETE',
-             'url' => "session/:sessionId/cookie/:name"
-        },
-        'getPageSource' => {
-                        'method' => 'GET',
-                        'url' => "session/:sessionId/source"
-        },
-        'getTitle' => {
-                        'method' => 'GET',
-                        'url' => "session/:sessionId/title"
-        },
-        'findElement' => {
-                       'method' => 'POST',
-                       'url' => "session/:sessionId/element"
-        },
-        'findElements' => {
-                      'method' => 'POST',
-                      'url' => "session/:sessionId/elements"
-        },
-        'getActiveElement' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/element/active"
-        },
-        'describeElement' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/element/:id"
-        },
-        'findChildElement' => {
-            'method' => 'POST',
-            'url' => "session/:sessionId/element/:id/element"
-        },
-        'findChildElements' => {
-            'method' => 'POST',
-            'url' => "session/:sessionId/element/:id/elements"
-        },
-        'clickElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/click"
-        },
-        'submitElement' => {
-              'method' => 'POST',
-              'url' => "session/:sessionId/element/:id/submit"
-        },
-        'sendKeysToElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/value"
-        },
-        'sendKeysToActiveElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/keys"
-        },
-        'sendModifier' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/modifier"
-        },
-        'isElementSelected' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/selected"
-        },
-        'setElementSelected' => {
-            'method' => 'POST',
-            'url' => "session/:sessionId/element/:id/selected"
-        },
-        'toggleElement' => {
-              'method' => 'POST',
-              'url' => "session/:sessionId/element/:id/toggle"
-        },
-        'isElementEnabled' => {
-             'method' => 'GET',
-             'url' => "session/:sessionId/element/:id/enabled"
-        },
-        'getElementLocation' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/location"
-        },
-        'getElementLocationInView' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/location_in_view"
-        },
-        'getElementTagName' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/element/:id/name"
-        },
-        'clearElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/clear"
-        },
-        'getElementAttribute' => {
-            'method' => 'GET',
-            'url' =>
-"session/:sessionId/element/:id/attribute/:name"
-        },
-        'elementEquals' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/equals/:other"
-        },
-        'isElementDisplayed' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/displayed"
-        },
-        'close' => {
-                     'method' => 'DELETE',
-                     'url'    => "session/:sessionId/window"
-        },
-        'dragElement' => {
-                'method' => 'POST',
-                'url' => "session/:sessionId/element/:id/drag"
-        },
-        'getElementSize' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/element/:id/size"
-        },
-        'getElementText' => {
-                'method' => 'GET',
-                'url' => "session/:sessionId/element/:id/text"
-        },
-        'getElementValueOfCssProperty' => {
-            'method' => 'GET',
-            'url' => "session/:sessionId/element/:id/css/:propertyName"
-        },
-        'hoverOverElement' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/element/:id/hover"
-        },
-        'mouseMoveToLocation' => {
-               'method' => 'POST',
-               'url' => "session/:sessionId/moveto"
-        },
-        'getAlertText' => {
-               'method' => 'GET',
-               'url'    => 'session/:sessionId/alert_text'
-        },
-        'sendKeysToPrompt' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/alert_text'
-        },
-        'acceptAlert' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/accept_alert'
-        },
-        'dismissAlert' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/dismiss_alert'
-        },
-        'click' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/click'
-        },
-        'doubleClick' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/doubleclick'
-        },
-        'buttonDown' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/buttondown'
-        },
-        'buttonUp' => {
-               'method' => 'POST',
-               'url'    => 'session/:sessionId/buttonup'
-        },
-        #'setVisible' => {
-        #               'method' => 'POST',
-        #               'url' => "session/:sessionId/visible"
-        #},
-        #'getVisible' => {
-        #               'method' => 'GET',
-        #               'url' => "session/:sessionId/visible"
-        #},
-    };
-
-    bless $self, $class or die "Can't bless $class: $!";
-    return $self;
-}
-
-# This method will replace the template & return
-sub get_params {
-    my ($self, $args) = @_;
-    if (!(defined $args->{'session_id'})) {
-        return;
-    }
-    my $data = {};
-    my $command = $args->{'command'};
-    my $url = $self->{$command}->{'url'};
-    
-    # Do the var substitutions.
-    $url =~ s/:sessionId/$args->{'session_id'}/;
-    $url =~ s/:id/$args->{'id'}/;
-    $url =~ s/:name/$args->{'name'}/;
-    $url =~ s/:propertyName/$args->{'property_name'}/;
-    $url =~ s/:other/$args->{'other'}/;
-    $url =~ s/:windowHandle/$args->{'window_handle'}/;
-
-    $data->{'method'} = $self->{$command}->{'method'};
-    $data->{'url'}    = $url;
-
-    return $data;
-}
-
-1;
-
-__END__
-
-=head1 SEE ALSO
-
-For more information about Selenium , visit the website at
-L<http://code.google.com/p/selenium/>.
-
-=head1 BUGS
-
-The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
-
-=head1 CURRENT MAINTAINER
-
-Gordon Child C<< <gchild@gordonchild.com> >>
-
-=head1 AUTHOR
-
-Perl Bindings for Selenium Remote Driver by Aditya Ivaturi C<< <ivaturi@gmail.com> >>
-
-=head1 LICENSE
-
-Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+package Selenium::Remote::Commands;
+
+use strict;
+use warnings;
+
+sub new {
+    my $class = shift;
+    
+    # http://code.google.com/p/selenium/wiki/JsonWireProtocol
+    my $self = {
+        'status'     => {
+                          'method' => 'GET',
+                          'url'    => 'status'
+        },
+        'newSession' => {
+                          'method' => 'POST',
+                          'url'    => 'session'
+        },
+        'getSessions' => {
+                          'method' => 'GET',
+                          'url'    => 'sessions'
+        },
+        'getCapabilities' => {
+                          'method' => 'GET',
+                          'url'    => 'session/:sessionId'
+        },
+        'setTimeout' => {
+                        'method' => 'POST',
+                        'url'    => 'session/:sessionId/timeouts'
+        },
+        'setAsyncScriptTimeout' => {
+                        'method' => 'POST',
+                        'url'    => 'session/:sessionId/timeouts/async_script'
+        },
+        'setImplicitWaitTimeout' => {
+                        'method' => 'POST',
+                        'url'    => 'session/:sessionId/timeouts/implicit_wait'
+        },
+        'quit' => {
+                    'method' => 'DELETE',
+                    'url'    => "session/:sessionId"
+        },
+        'getCurrentWindowHandle' => {
+                 'method' => 'GET',
+                 'url' => "session/:sessionId/window_handle"
+        },
+        'getWindowHandles' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/window_handles"
+        },
+        'getWindowSize' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/window/:windowHandle/size"
+        },
+        'getWindowPosition' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/window/:windowHandle/position"
+        },
+        'setWindowSize' => {
+                'method' => 'POST',
+                'url' => "session/:sessionId/window/:windowHandle/size"
+        },
+        'setWindowPosition' => {
+                'method' => 'POST',
+                'url' => "session/:sessionId/window/:windowHandle/position"
+        },
+        'getCurrentUrl' => {
+                           'method' => 'GET',
+                           'url' => "session/:sessionId/url"
+        },
+        'get' => {
+                   'method' => 'POST',
+                   'url'    => "session/:sessionId/url"
+        },
+        'goForward' => {
+                       'method' => 'POST',
+                       'url' => "session/:sessionId/forward"
+        },
+        'goBack' => {
+                      'method' => 'POST',
+                      'url'    => "session/:sessionId/back"
+        },
+        'refresh' => {
+                       'method' => 'POST',
+                       'url' => "session/:sessionId/refresh"
+        },
+        'executeScript' => {
+                       'method' => 'POST',
+                       'url' => "session/:sessionId/execute"
+        },
+        'executeAsyncScript' => {
+                       'method' => 'POST',
+                       'url' => "session/:sessionId/execute_async"
+        },
+        'screenshot' => {
+                    'method' => 'GET',
+                    'url' => "session/:sessionId/screenshot"
+        },
+        'availableEngines' => {
+                    'method' => 'GET',
+                    'url' => "session/:sessionId/ime/available_engines"
+        },
+        'switchToFrame' => {
+                'method' => 'POST',
+                'url' => "session/:sessionId/frame"
+        },
+        'switchToWindow' => {
+             'method' => 'POST',
+             'url' => "session/:sessionId/window"
+        },
+        'getAllCookies' => {
+                        'method' => 'GET',
+                        'url' => "session/:sessionId/cookie"
+        },
+        'addCookie' => {
+                        'method' => 'POST',
+                        'url' => "session/:sessionId/cookie"
+        },
+        'deleteAllCookies' => {
+                        'method' => 'DELETE',
+                        'url' => "session/:sessionId/cookie"
+        },
+        'deleteCookieNamed' => {
+             'method' => 'DELETE',
+             'url' => "session/:sessionId/cookie/:name"
+        },
+        'getPageSource' => {
+                        'method' => 'GET',
+                        'url' => "session/:sessionId/source"
+        },
+        'getTitle' => {
+                        'method' => 'GET',
+                        'url' => "session/:sessionId/title"
+        },
+        'findElement' => {
+                       'method' => 'POST',
+                       'url' => "session/:sessionId/element"
+        },
+        'findElements' => {
+                      'method' => 'POST',
+                      'url' => "session/:sessionId/elements"
+        },
+        'getActiveElement' => {
+                'method' => 'POST',
+                'url' => "session/:sessionId/element/active"
+        },
+        'describeElement' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/element/:id"
+        },
+        'findChildElement' => {
+            'method' => 'POST',
+            'url' => "session/:sessionId/element/:id/element"
+        },
+        'findChildElements' => {
+            'method' => 'POST',
+            'url' => "session/:sessionId/element/:id/elements"
+        },
+        'clickElement' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/element/:id/click"
+        },
+        'submitElement' => {
+              'method' => 'POST',
+              'url' => "session/:sessionId/element/:id/submit"
+        },
+        'sendKeysToElement' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/element/:id/value"
+        },
+        'sendKeysToActiveElement' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/keys"
+        },
+        'sendModifier' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/modifier"
+        },
+        'isElementSelected' => {
+            'method' => 'GET',
+            'url' => "session/:sessionId/element/:id/selected"
+        },
+        'setElementSelected' => {
+            'method' => 'POST',
+            'url' => "session/:sessionId/element/:id/selected"
+        },
+        'toggleElement' => {
+              'method' => 'POST',
+              'url' => "session/:sessionId/element/:id/toggle"
+        },
+        'isElementEnabled' => {
+             'method' => 'GET',
+             'url' => "session/:sessionId/element/:id/enabled"
+        },
+        'getElementLocation' => {
+            'method' => 'GET',
+            'url' => "session/:sessionId/element/:id/location"
+        },
+        'getElementLocationInView' => {
+            'method' => 'GET',
+            'url' => "session/:sessionId/element/:id/location_in_view"
+        },
+        'getElementTagName' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/element/:id/name"
+        },
+        'clearElement' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/element/:id/clear"
+        },
+        'getElementAttribute' => {
+            'method' => 'GET',
+            'url' =>
+"session/:sessionId/element/:id/attribute/:name"
+        },
+        'elementEquals' => {
+            'method' => 'GET',
+            'url' => "session/:sessionId/element/:id/equals/:other"
+        },
+        'isElementDisplayed' => {
+            'method' => 'GET',
+            'url' => "session/:sessionId/element/:id/displayed"
+        },
+        'close' => {
+                     'method' => 'DELETE',
+                     'url'    => "session/:sessionId/window"
+        },
+        'dragElement' => {
+                'method' => 'POST',
+                'url' => "session/:sessionId/element/:id/drag"
+        },
+        'getElementSize' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/element/:id/size"
+        },
+        'getElementText' => {
+                'method' => 'GET',
+                'url' => "session/:sessionId/element/:id/text"
+        },
+        'getElementValueOfCssProperty' => {
+            'method' => 'GET',
+            'url' => "session/:sessionId/element/:id/css/:propertyName"
+        },
+        'hoverOverElement' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/element/:id/hover"
+        },
+        'mouseMoveToLocation' => {
+               'method' => 'POST',
+               'url' => "session/:sessionId/moveto"
+        },
+        'getAlertText' => {
+               'method' => 'GET',
+               'url'    => 'session/:sessionId/alert_text'
+        },
+        'sendKeysToPrompt' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/alert_text'
+        },
+        'acceptAlert' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/accept_alert'
+        },
+        'dismissAlert' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/dismiss_alert'
+        },
+        'click' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/click'
+        },
+        'doubleClick' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/doubleclick'
+        },
+        'buttonDown' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/buttondown'
+        },
+        'buttonUp' => {
+               'method' => 'POST',
+               'url'    => 'session/:sessionId/buttonup'
+        },
+        #'setVisible' => {
+        #               'method' => 'POST',
+        #               'url' => "session/:sessionId/visible"
+        #},
+        #'getVisible' => {
+        #               'method' => 'GET',
+        #               'url' => "session/:sessionId/visible"
+        #},
+    };
+
+    bless $self, $class or die "Can't bless $class: $!";
+    return $self;
+}
+
+# This method will replace the template & return
+sub get_params {
+    my ($self, $args) = @_;
+    if (!(defined $args->{'session_id'})) {
+        return;
+    }
+    my $data = {};
+    my $command = $args->{'command'};
+    my $url = $self->{$command}->{'url'};
+    
+    # Do the var substitutions.
+    $url =~ s/:sessionId/$args->{'session_id'}/;
+    $url =~ s/:id/$args->{'id'}/;
+    $url =~ s/:name/$args->{'name'}/;
+    $url =~ s/:propertyName/$args->{'property_name'}/;
+    $url =~ s/:other/$args->{'other'}/;
+    $url =~ s/:windowHandle/$args->{'window_handle'}/;
+
+    $data->{'method'} = $self->{$command}->{'method'};
+    $data->{'url'}    = $url;
+
+    return $data;
+}
+
+1;
+
+__END__
+
+=head1 SEE ALSO
+
+For more information about Selenium , visit the website at
+L<http://code.google.com/p/selenium/>.
+
+=head1 BUGS
+
+The Selenium issue tracking system is available online at
+L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+
+=head1 CURRENT MAINTAINER
+
+Gordon Child C<< <gchild@gordonchild.com> >>
+
+=head1 AUTHOR
+
+Perl Bindings for Selenium Remote Driver by Aditya Ivaturi C<< <ivaturi@gmail.com> >>
+
+=head1 LICENSE
+
+Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 1848 - 1848
lib/Selenium/Remote/Driver.pm

@@ -1,1848 +1,1848 @@
-package Selenium::Remote::Driver;
-
-use strict;
-use warnings;
-
-use Carp;
-our @CARP_NOT;
-
-use Selenium::Remote::RemoteConnection;
-use Selenium::Remote::Commands;
-use Selenium::Remote::WebElement;
-
-use constant FINDERS => {
-      class             => 'class name',
-      class_name        => 'class name',
-      css               => 'css selector',
-      id                => 'id',
-      link              => 'link text',
-      link_text         => 'link text',
-      name              => 'name',
-      partial_link_text => 'partial link text',
-      tag_name          => 'tag name',
-      xpath             => 'xpath',
-};
-
-=head1 NAME
-
-Selenium::Remote::Driver - Perl Client for Selenium Remote Driver
-
-=cut
-
-=head1 SYNOPSIS
-
-    use Selenium::Remote::Driver;
-
-    my $driver = new Selenium::Remote::Driver;
-    $driver->get('http://www.google.com');
-    print $driver->get_title();
-    $driver->quit();
-
-=cut
-
-=head1 DESCRIPTION
-
-Selenium is a test tool that allows you to write
-automated web application UI tests in any programming language against
-any HTTP website using any mainstream JavaScript-enabled browser. This module is
-an implementation of the client for the Remote driver that Selenium provides.
-You can find bindings for other languages at this location:
-
-L<http://code.google.com/p/selenium/>
-
-This module sends commands directly to the Server using HTTP. Using this module
-together with the Selenium Server, you can automatically control any supported
-browser. To use this module, you need to have already downloaded and started
-the Selenium Server (Selenium Server is a Java application).
-
-=cut
-
-=head1 USAGE (read this first)
-
-=head2 Remote Driver Response
-
-Selenium::Remote::Driver uses the L<JsonWireProtocol|http://code.google.com/p/selenium/wiki/JsonWireProtocol> to communicate with the
-Selenium Server. If an error occurs while executing the command then the server
-sends back an HTTP error code with a JSON encoded reponse that indicates the
-precise L<Response Error Code|http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes>. The module will then croak with the error message
-associated with this code. If no error occurred, then the subroutine called will
-return the value sent back from the server (if a return value was sent).
-
-So a rule of thumb while invoking methods on the driver is if the method did not
-croak when called, then you can safely assume the command was successful even if
-nothing was returned by the method.
-
-=head2 WebElement
-
-Selenium Webdriver represents all the HTML elements as WebElement, which is
-in turn represented by Selenium::Remote::WebElement module. So any method that
-deals with WebElements will return and/or expect WebElement object. The POD for
-that module describes all the methods that perform various actions on the
-WebElements like click, submit etc.
-
-To interact with any WebElement you have to first "find" it, read the POD for
-find_element or find_elements for further info. Once you find the required element
-then you can perform various actions. If you don't call find_* method first, all
-your further actions will fail for that element. Finally, just remember that you
-don't have to instantiate WebElement objects at all - they will be automatically
-created when you use the find_* methods.
-
-=cut
-
-=head1 FUNCTIONS
-
-=cut
-
-=head2 new
-
- Description:
-    Constructor for Driver. It'll instantiate the object if it can communicate
-    with the Selenium RC server.
-
- Input: (all optional)
-    desired_capabilities - HASH - Following options are accepted:
-      Optional:
-        'remote_server_addr' - <string> - IP or FQDN of the RC server machine
-        'browser_name' - <string> - desired browser string:
-                      {iphone|firefox|internet explorer|htmlunit|iphone|chrome}
-        'version' - <string> - desired browser version number
-        'platform' - <string> - desired platform:
-                                {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANY}
-        'javascript' - <boolean> - whether javascript should be supported
-        'accept_ssl_certs' - <boolean> - whether SSL certs should be accepted, default is true.
-        'auto_close' - <boolean> - whether driver should end session on remote
-                                   server on close.
-        'extra_capabilities' - HASH of extra capabilities
-        'proxy' - HASH - Proxy configuration with the following keys:
-            'proxyType' - <string> - REQUIRED, Possible values are:
-                direct - A direct connection - no proxy in use,
-                manual - Manual proxy settings configured, e.g. setting a proxy for HTTP, a proxy for FTP, etc,
-                pac - Proxy autoconfiguration from a URL,
-                autodetect - proxy autodetection, probably with WPAD,
-                system - Use system settings
-            'proxyAutoconfigUrl' - <string> - REQUIRED if proxyType is 'pac', ignored otherwise. Expected format: http://hostname.com:1234/pacfile.
-            'ftpProxy' - <string> - OPTIONAL, ignored if proxyType is not 'manual'. Expected format: hostname.com:1234
-            'httpProxy' - <string> - OPTIONAL, ignored if proxyType is not 'manual'. Expected format: hostname.com:1234
-            'sslProxy' - <string> - OPTIONAL, ignored if proxyType is not 'manual'. Expected format: hostname.com:1234
-            
-        If no values are provided, then these defaults will be assumed:
-            'remote_server_addr' => 'localhost'
-            'port'         => '4444'
-            'browser_name' => 'firefox'
-            'version'      => ''
-            'platform'     => 'ANY'
-            'javascript'   => 1
-            'auto_close'   => 1
-
- Output:
-    Remote Driver object
-
- Usage:
-    my $driver = new Selenium::Remote::Driver;
-    or
-    my $driver = new Selenium::Remote::Driver('browser_name' => 'firefox',
-                                              'platform' => 'MAC');
-    or
-    my $driver = new Selenium::Remote::Driver('remote_server_addr' => '10.10.1.1',
-                                              'port' => '2222',
-                                              auto_close => 0
-                                              );
-    or
-    my $driver = new Selenium::Remote::Driver('browser_name'       => 'chrome',
-                                              'platform'           => 'VISTA',
-                                              'extra_capabilities' => {'chrome.switches' => ["--user-data-dir=$ENV{LOCALAPPDATA}\\Google\\Chrome\\User Data"],},
-                                              );
-    or
-    my $driver = Selenium::Remote::Driver->new('proxy' => {'proxyType' => 'manual', 'httpProxy' => 'myproxy.com:1234'});
-    
-=cut
-
-sub new {
-    my ( $class, %args ) = @_;
-    my $ress = new Selenium::Remote::Commands;
-
-    # Set the defaults if user doesn't send any
-    my $self = {
-        remote_server_addr => delete $args{remote_server_addr} || 'localhost',
-        browser_name       => delete $args{browser_name}       || 'firefox',
-        platform           => delete $args{platform}           || 'ANY',
-        port               => delete $args{port}               || '4444',
-        version            => delete $args{version}            || '',
-        session_id         => undef,
-        remote_conn        => undef,
-        commands           => $ress,
-        auto_close         => 1, # by default we will close remote session on DESTROY
-        pid                => $$,
-    };
-    bless $self, $class or die "Can't bless $class: $!";
-    
-    # check for javascript 
-    if ( defined $args{javascript} ) {
-        if ( $args{javascript} ) {
-            $self->{javascript} = JSON::true;
-        }
-        else {
-            $self->{javascript} = JSON::false;
-        }
-    }
-    else {
-        $self->{javascript} = JSON::true;
-    }
-    
-    # check for acceptSslCerts
-    if ( defined $args{accept_ssl_certs} ) {
-        if ( $args{accept_ssl_certs} ) {
-            $self->{accept_ssl_certs} = JSON::true;
-        }
-        else {
-            $self->{accept_ssl_certs} = JSON::false;
-        }
-    }
-    else {
-        $self->{accept_ssl_certs} = JSON::true;
-    }
-    
-    # check for proxy
-    if ( defined $args{proxy} ) {
-        if ($args{proxy}{proxyType} eq 'pac') {
-            if (not defined $args{proxy}{proxyAutoconfigUrl}) {
-                croak "proxyAutoconfigUrl not provided\n";
-            } elsif (not ($args{proxy}{proxyAutoconfigUrl} =~ /^http/g)) {
-                croak "proxyAutoconfigUrl should be of format http://";
-            }
-        }
-        $self->{proxy} = $args{proxy};
-    }
-
-    # Connect to remote server & establish a new session
-    $self->{remote_conn} =
-      new Selenium::Remote::RemoteConnection( $self->{remote_server_addr},
-        $self->{port} );
-    $self->new_session(delete $args{extra_capabilities});
-
-    if ( !( defined $self->{session_id} ) ) {
-        croak "Could not establish a session with the remote server\n";
-    }
-
-    return $self;
-}
-
-sub DESTROY {
-    my ($self) = @_;
-    return if $$ != $self->{pid};
-    $self->quit() if ($self->{auto_close} && defined $self->{session_id});
-}
-
-# This is an internal method used the Driver & is not supposed to be used by
-# end user. This method is used by Driver to set up all the parameters
-# (url & JSON), send commands & receive processed response from the server.
-sub _execute_command {
-    my ( $self, $res, $params ) = @_;
-    $res->{'session_id'} = $self->{'session_id'};
-    my $resource = $self->{commands}->get_params($res);
-    if ($resource) {
-        my $resp = $self->{remote_conn}
-          ->request( $resource->{'method'}, $resource->{'url'}, $params );
-        if(ref($resp) eq 'HASH') {
-            if($resp->{cmd_status} eq 'OK') {
-               return $resp->{cmd_return};
-            } else {
-               my $msg = "Error while executing command";
-               if($resp->{cmd_error}) {
-                 $msg .= ": $resp->{cmd_error}" if $resp->{cmd_error};
-               } else {
-                   if(ref($resp->{cmd_return}) eq 'HASH') {
-                     $msg .= ": $resp->{cmd_return}->{error}->{msg}"
-                       if $resp->{cmd_return}->{error}->{msg};
-                     $msg .= ": $resp->{cmd_return}->{message}"
-                       if $resp->{cmd_return}->{message};
-                   } else {
-                     $msg .= ": $resp->{cmd_return}";
-                   }
-               }
-               croak $msg;
-            }
-        }
-        return $resp;
-    }
-    else {
-        croak "Couldn't retrieve command settings properly\n";
-    }
-}
-
-# A method that is used by the Driver itself. It'll be called to set the
-# desired capabilities on the server.
-sub new_session {
-    my ($self, $extra_capabilities) = @_;
-    $extra_capabilities ||= {};
-    my $args = {
-        'desiredCapabilities' => {
-            'browserName'       => $self->{browser_name},
-            'platform'          => $self->{platform},
-            'javascriptEnabled' => $self->{javascript},
-            'version'           => $self->{version},
-            'acceptSslCerts'    => $self->{accept_ssl_certs},
-            'proxy'             => $self->{proxy},
-            %$extra_capabilities,
-        },
-    };
-    my $resp =
-      $self->{remote_conn}
-      ->request( $self->{commands}->{'newSession'}->{'method'},
-        $self->{commands}->{'newSession'}->{'url'}, $args, );
-    if ( ( defined $resp->{'sessionId'} ) && $resp->{'sessionId'} ne '' ) {
-        $self->{session_id} = $resp->{'sessionId'};
-    }
-    else {
-        croak "Could not create new session";
-    }
-}
-
-=head2 debug_on
-
-  Description:
-    Turns on debugging mode and the driver will print extra info like request
-    and response to stdout. Useful, when you want to see what is being sent to
-    the server & what response you are getting back.
-
-  Usage:
-    $driver->debug_on;
-
-=cut
-
-sub debug_on {
-    my ($self) = @_;
-    $self->{'remote_conn'}->{'debug'} = 1;
-}
-
-=head2 debug_off
-
-  Description:
-    Turns off the debugging mode.
-
-  Usage:
-    $driver->debug_off;
-
-=cut
-
-sub debug_off {
-    my ($self) = @_;
-    $self->{'remote_conn'}->{'debug'} = 0;
-}
-
-=head2 get_sessions
-
-  Description:
-    Returns a list of the currently active sessions. Each session will be
-    returned as an array of Hashes with the following keys:
-    
-    'id' : The session ID
-    'capabilities: An object describing session's capabilities
-
-  Output:
-    Array of Hashes
-
-  Usage:
-    print Dumper $driver->get_sessions();
-
-=cut
-
-sub get_sessions{
-    my ($self) = @_;
-    my $res = { 'command' => 'getSessions' };
-    return $self->_execute_command($res);
-}
-
-=head2 status
-
-  Description:
-    Query the server's current status. All server implementations
-    should return two basic objects describing the server's current
-    platform and when the server was built.
-
-  Output:
-    Hash ref
-
-  Usage:
-    print Dumper $driver->status;
-
-=cut
-
-sub status {
-    my ($self) = @_;
-    my $res = { 'command' => 'status' };
-    return $self->_execute_command($res);
-}
-
-=head2 get_alert_text
-
- Description:
-    Gets the text of the currently displayed JavaScript alert(), confirm()
-    or prompt() dialog.
-
- Example
-    my $string = $driver->get_alert_text;
-
-=cut
-
-sub get_alert_text {
-  my ($self) = @_;
-  my $res = { 'command' => 'getAlertText' };
-  return $self->_execute_command($res);
-}
-
-=head2 send_keys_to_active_element
-
- Description:
-    Send a sequence of key strokes to the active element. This command is
-    similar to the send keys command in every aspect except the implicit
-    termination: The modifiers are not released at the end of the call.
-    Rather, the state of the modifier keys is kept between calls, so mouse
-    interactions can be performed while modifier keys are depressed.
-
- Input: 1
-    Required:
-        {ARRAY | STRING} - Array of strings or a string.
-
- Usage:
-    $driver->send_keys_to_active_element('abcd', 'efg');
-    $driver->send_keys_to_active_element('hijk');
-    
-    or
-    
-    # include the WDKeys module
-    use Selenium::Remote::WDKeys;
-    .
-    .
-    $driver->send_keys_to_active_element(KEYS->{'space'}, KEYS->{'enter'});
-
-=cut
-
-sub send_keys_to_active_element {
-    my ($self, @strings) = @_;
-    my $res = { 'command' => 'sendKeysToActiveElement' };
-    my $params = {
-        'value' => \@strings,
-    };
-    return $self->_execute_command($res, $params);
-}
-
-=head2 send_keys_to_alert
-
-Synonymous with send_keys_to_prompt
-
-=cut
-
-sub send_keys_to_alert {
-  return shift->send_keys_to_prompt(@_);
-}
-
-=head2 send_keys_to_prompt
-
- Description:
-    Sends keystrokes to a JavaScript prompt() dialog.
-
- Input:
-    {string} keys to send
-
- Example:
-    $driver->send_keys_to_prompt('hello world');
-  or
-    ok($driver->get_alert_text eq 'Please Input your name','prompt appears');
-    $driver->send_keys_to_alert("Larry Wall");
-    $driver->accept_alert;
-
-=cut
-
-sub send_keys_to_prompt {
-  my ($self,$keys) = @_;
-  my $res = { 'command' => 'sendKeysToPrompt' };
-  my $params = { 'text' => $keys };
-  return $self->_execute_command($res,$params);
-}
-
-=head2 accept_alert
-
- Description:
-    Accepts the currently displayed alert dialog.  Usually, this is
-    equivalent to clicking the 'OK' button in the dialog.
-
- Example:
-    $driver->accept_alert;
-
-=cut
-
-sub accept_alert {
-  my ($self) = @_;
-  my $res = { 'command' => 'acceptAlert' };
-  return $self->_execute_command($res);
-}
-
-=head2 dismiss_alert
-
- Description:
-    Dismisses the currently displayed alert dialog. For comfirm()
-    and prompt() dialogs, this is equivalent to clicking the
-    'Cancel' button. For alert() dialogs, this is equivalent to
-    clicking the 'OK' button.
-
- Example:
-    $driver->dismiss_alert;
-
-=cut
-
-sub dismiss_alert {
-  my ($self) = @_;
-  my $res = { 'command' => 'dismissAlert' };
-  return $self->_execute_command($res);
-}
-
-=head2 mouse_move_to_location
-
- Description:
-    Move the mouse by an offset of the specificed element. If no
-    element is specified, the move is relative to the current mouse
-    cursor. If an element is provided but no offset, the mouse will be
-    moved to the center of the element. If the element is not visible,
-    it will be scrolled into view.
-
- Output:
-    STRING - 
-
- Usage:
-    # element - the element to move to. If not specified or is null, the offset is relative to current position of the mouse.
-    # xoffset - X offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
-    # yoffset - Y offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
-
-    print $driver->mouse_move_to_location(element => e, xoffset => x, yoffset => y);
-
-=cut
-
-sub mouse_move_to_location {
-    my ($self, %params) = @_;
-    $params{element} = $params{element}{id} if exists $params{element};
-    my $res = { 'command' => 'mouseMoveToLocation' };
-    return $self->_execute_command($res, \%params);
-}
-
-=head2 move_to
-
-Synonymous with mouse_move_to_location
-
-=cut
-
-sub move_to {
-    return shift->mouse_move_to_location(@_);
-}
-
-=head2 get_capabilities
-
- Description:
-    Retrieve the capabilities of the specified session.
-
- Output:
-    HASH of all the capabilities.
-
- Usage:
-    my $capab = $driver->get_capabilities();
-    print Dumper($capab);
-
-=cut
-
-sub get_capabilities {
-    my $self = shift;
-    my $res  = {'command' => 'getCapabilities'};
-    return $self->_execute_command($res);
-}
-
-=head2 set_async_script_timeout
-
- Description:
-    Set the amount of time, in milliseconds, that asynchronous scripts executed
-    by execute_async_script() are permitted to run before they are
-    aborted and a |Timeout| error is returned to the client.
- 
- Input:
-    ms - <NUMBER> - The amount of time, in milliseconds, that time-limited
-            commands are permitted to run.
-
- Usage:
-    $driver->set_async_script_timeout(1000);
-
-=cut
-
-sub set_async_script_timeout {
-    my ($self, $ms) = @_;
-    if (not defined $ms)
-    {
-        return "Expecting timeout in ms";
-    }
-    my $res  = {'command' => 'setAsyncScriptTimeout'};
-    my $params  = {'ms' => $ms};
-    return $self->_execute_command($res, $params);
-}
-
-=head2 set_implicit_wait_timeout
-
- Description:
-    Set the amount of time the driver should wait when searching for elements.
-    When searching for a single element, the driver will poll the page until
-    an element is found or the timeout expires, whichever occurs first.
-    When searching for multiple elements, the driver should poll the page until
-    at least one element is found or the timeout expires, at which point it
-    will return an empty list. If this method is never called, the driver will
-    default to an implicit wait of 0ms.
- 
- Input:
-    Time in milliseconds.
-
- Output:
-    Server Response Hash with no data returned back from the server.
-
- Usage:
-    $driver->set_implicit_wait_timeout(10);
-
-=cut
-
-sub set_implicit_wait_timeout {
-    my ($self, $ms) = @_;
-    my $res  = {'command' => 'setImplicitWaitTimeout'};
-    my $params  = {'ms' => $ms};
-    return $self->_execute_command($res, $params);
-}
-
-=head2 close
-
- Description:
-    Close the current window.
-
- Usage:
-    $driver->close();
- or
-    #close a popup window
-    my $handles = $driver->get_window_handles;
-    $driver->switch_to_window($handles->[1]);
-    $driver->close();
-    $driver->switch_to_window($handles->[0]);
-
-=cut
-
-sub close {
-  my $self = shift;
-  my $res = { 'command' => 'close' };
-  $self->_execute_command($res);
-}
-
-=head2 quit
-
- Description:
-    Delete the session & close open browsers.
-
- Usage:
-    $driver->quit();
-
-=cut
-
-sub quit {
-    my $self = shift;
-    my $res = { 'command' => 'quit' };
-    $self->_execute_command($res);
-    $self->{session_id} = undef;
-}
-
-=head2 get_current_window_handle
-
- Description:
-    Retrieve the current window handle.
-
- Output:
-    STRING - the window handle
-
- Usage:
-    print $driver->get_current_window_handle();
-
-=cut
-
-sub get_current_window_handle {
-    my $self = shift;
-    my $res = { 'command' => 'getCurrentWindowHandle' };
-    return $self->_execute_command($res);
-}
-
-=head2 get_window_handles
-
- Description:
-    Retrieve the list of window handles used in the session.
-
- Output:
-    ARRAY of STRING - list of the window handles
-
- Usage:
-    print Dumper $driver->get_window_handles;
- or
-    # get popup, close, then back
-    my $handles = $driver->get_window_handles;
-    $driver->switch_to_window($handles->[1]);
-    $driver->close;
-    $driver->switch_to_window($handles->[0]);
-
-=cut
-
-sub get_window_handles {
-    my $self = shift;
-    my $res = { 'command' => 'getWindowHandles' };
-    return $self->_execute_command($res);
-}
-
-=head2 get_window_size
-
- Description:
-    Retrieve the window size
- 
- Input:
-    STRING - <optional> - window handle (default is 'current' window)
-
- Output:
-    HASH - containing keys 'height' & 'width'
-
- Usage:
-    my $window_size = $driver->get_window_size();
-    print $window_size->{'height'}, $window_size->('width');
-
-=cut
-
-sub get_window_size {
-    my ( $self, $window ) = @_;
-    $window = (defined $window)?$window:'current';
-    my $res = { 'command' => 'getWindowSize', 'window_handle' => $window };
-    return $self->_execute_command($res);
-}
-
-=head2 get_window_position
-
- Description:
-    Retrieve the window position
- 
- Input:
-    STRING - <optional> - window handle (default is 'current' window)
-
- Output:
-    HASH - containing keys 'x' & 'y'
-
- Usage:
-    my $window_size = $driver->get_window_position();
-    print $window_size->{'x'}, $window_size->('y');
-
-=cut
-
-sub get_window_position {
-    my ( $self, $window ) = @_;
-    $window = (defined $window)?$window:'current';
-    my $res = { 'command' => 'getWindowPosition', 'window_handle' => $window };
-    return $self->_execute_command($res);
-}
-
-=head2 get_current_url
-
- Description:
-    Retrieve the url of the current page
-
- Output:
-    STRING - url
-
- Usage:
-    print $driver->get_current_url();
-
-=cut
-
-sub get_current_url {
-    my $self = shift;
-    my $res = { 'command' => 'getCurrentUrl' };
-    return $self->_execute_command($res);
-}
-
-=head2 navigate
-
- Description:
-    Navigate to a given url. This is same as get() method.
-    
- Input:
-    STRING - url
-
- Usage:
-    $driver->navigate('http://www.google.com');
-
-=cut
-
-sub navigate {
-    my ( $self, $url ) = @_;
-    $self->get($url);
-}
-
-=head2 get
-
- Description:
-    Navigate to a given url
-    
- Input:
-    STRING - url
-
- Usage:
-    $driver->get('http://www.google.com');
-
-=cut
-
-sub get {
-    my ( $self, $url ) = @_;
-    my $res    = { 'command' => 'get' };
-    my $params = { 'url'     => $url };
-    return $self->_execute_command( $res, $params );
-}
-
-=head2 get_title
-
- Description:
-    Get the current page title
-
- Output:
-    STRING - Page title
-
- Usage:
-    print $driver->get_title();
-
-=cut
-
-sub get_title {
-    my $self = shift;
-    my $res = { 'command' => 'getTitle' };
-    return $self->_execute_command($res);
-}
-
-=head2 go_back
-
- Description:
-    Equivalent to hitting the back button on the browser.
-
- Usage:
-    $driver->go_back();
-
-=cut
-
-sub go_back {
-    my $self = shift;
-    my $res = { 'command' => 'goBack' };
-    return $self->_execute_command($res);
-}
-
-=head2 go_forward
-
- Description:
-    Equivalent to hitting the forward button on the browser.
-
- Usage:
-    $driver->go_forward();
-
-=cut
-
-sub go_forward {
-    my $self = shift;
-    my $res = { 'command' => 'goForward' };
-    return $self->_execute_command($res);
-}
-
-=head2 refresh
-
- Description:
-    Reload the current page.
-
- Usage:
-    $driver->refresh();
-
-=cut
-
-sub refresh {
-    my $self = shift;
-    my $res = { 'command' => 'refresh' };
-    return $self->_execute_command($res);
-}
-
-=head2 javascript
-
- Description:
-    returns true if javascript is enabled in the driver.
-
- Usage:
-    if ($driver->javascript) { ...; }
-
-=cut
-
-sub javascript {
-    my $self = shift;
-    return $self->{javascript} == JSON::true;
-}
-
-=head2 execute_async_script
-
- Description:
-    Inject a snippet of JavaScript into the page for execution in the context
-    of the currently selected frame. The executed script is assumed to be
-    asynchronous and must signal that is done by invoking the provided
-    callback, which is always provided as the final argument to the function.
-    The value to this callback will be returned to the client.
-
-    Asynchronous script commands may not span page loads. If an unload event
-    is fired while waiting for a script result, an error should be returned
-    to the client.
-
- Input: 2 (1 optional)
-    Required:
-        STRING - Javascript to execute on the page
-    Optional:
-        ARRAY - list of arguments that need to be passed to the script.
-
- Output:
-    {*} - Varied, depending on the type of result expected back from the script.
-
- Usage:
-    my $script = q{
-        var arg1 = arguments[0];
-        var callback = arguments[arguments.length-1];
-        var elem = window.document.findElementById(arg1);
-        callback(elem);
-    };
-    my $callback = q{return arguments[0];};
-    my $elem = $driver->execute_async_script($script,'myid',$callback);
-    $elem->click;
-
-=cut
-
-sub execute_async_script {
-    my ( $self, $script, @args ) = @_;
-    if ($self->javascript) {
-        if ( not defined $script ) {
-            return 'No script provided';
-        }
-        my $res  = { 'command'    => 'executeAsyncScript' };
-
-        # Check the args array if the elem obj is provided & replace it with
-        # JSON representation
-        for (my $i=0; $i<@args; $i++) {
-            if (ref $args[$i] eq 'Selenium::Remote::WebElement') {
-                $args[$i] = {'ELEMENT' => ($args[$i])->{id}};
-            }
-        }
-
-        my $params = {'script' => $script, 'args' => \@args};
-        my $ret = $self->_execute_command($res, $params);
-
-        # replace any ELEMENTS with WebElement
-        if (ref($ret) and (ref($ret) eq 'HASH') and exists $ret->{'ELEMENT'}) {
-            $ret =
-                new Selenium::Remote::WebElement(
-                                        $ret->{ELEMENT}, $self);
-        }
-        return $ret;
-    }
-    else {
-        croak 'Javascript is not enabled on remote driver instance.';
-    }
-}
-
-=head2 execute_script
-
- Description:
-    Inject a snippet of JavaScript into the page and return its result.
-    WebElements that should be passed to the script as an argument should be
-    specified in the arguments array as WebElement object. Likewise,
-    any WebElements in the script result will be returned as WebElement object.
-
- Input: 2 (1 optional)
-    Required:
-        STRING - Javascript to execute on the page
-    Optional:
-        ARRAY - list of arguments that need to be passed to the script.
-
- Output:
-    {*} - Varied, depending on the type of result expected back from the script.
-
- Usage:
-    my $script = q{
-        var arg1 = arguments[0];
-        var elem = window.document.findElementById(arg1);
-        return elem;
-    };
-    my $elem = $driver->execute_script($script,'myid');
-    $elem->click;
-
-=cut
-
-sub execute_script {
-    my ( $self, $script, @args ) = @_;
-    if ($self->javascript) {
-        if ( not defined $script ) {
-            return 'No script provided';
-        }
-        my $res  = { 'command'    => 'executeScript' };
-        
-        # Check the args array if the elem obj is provided & replace it with
-        # JSON representation
-        for (my $i=0; $i<@args; $i++) {
-            if (ref $args[$i] eq 'Selenium::Remote::WebElement') {
-                $args[$i] = {'ELEMENT' => ($args[$i])->{id}};
-            }
-        }
-        
-        my $params = {'script' => $script, 'args' => [@args]};
-        my $ret = $self->_execute_command($res, $params);
-        
-        return $self->_convert_to_webelement($ret);
-    }
-    else {
-        croak 'Javascript is not enabled on remote driver instance.';
-    }
-}
-
-# _convert_to_webelement
-# An internal method used to traverse a data structure
-# and convert any ELEMENTS with WebElements
-
-sub _convert_to_webelement {
-    my ($self, $ret ) = @_;
-
-    if (ref($ret) and (ref($ret) eq 'HASH')) {
-        if((keys %$ret==1) and exists $ret->{'ELEMENT'}) {
-            # replace an ELEMENT with WebElement
-            return new Selenium::Remote::WebElement($ret->{ELEMENT}, $self);
-        }
-
-        my %hash;
-        foreach my $key (keys %$ret) {
-            $hash{$key}=$self->_convert_to_webelement($ret->{$key});
-        }
-        return \%hash;
-    }
-
-    if(ref($ret) and (ref($ret) eq 'ARRAY')) {
-        my @array = map {$self->_convert_to_webelement($_)} @$ret;
-        return \@array;
-    }
-
-    return $ret;
-}
-
-=head2 screenshot
-
- Description:
-    Get a screenshot of the current page as a base64 encoded image.
-
- Output:
-    STRING - base64 encoded image
-
- Usage:
-    print $driver->screenshot();
- or
-    require MIME::Base64;
-    open(FH,'>','screenshot.png');
-    binmode FH;
-    my $png_base64 = $driver->screenshot();
-    print FH MIME::Base64::decode_base64($png_base64);
-    close FH;
-
-=cut
-
-sub screenshot {
-    my ($self) = @_;
-    my $res = { 'command' => 'screenshot' };
-    return $self->_execute_command($res);
-}
-
-=head2 available_engines
-
- Description:
-    List all available engines on the machine. To use an engine, it has to be present in this list.
-
- Output:
-    {Array.<string>} A list of available engines
-
- Usage:
-    print Dumper $driver->available_engines;
-
-=cut
-
-sub available_engines {
-    my ($self) = @_;
-    my $res = { 'command' => 'availableEngines' };
-    return $self->_execute_command($res);
-}
-
-=head2 switch_to_frame
-
- Description:
-    Change focus to another frame on the page. If the frame ID is null, the
-    server will switch to the page's default content. You can also switch to a
-    WebElement, for e.g. you can find an iframe using find_element & then
-    provide that as an input to this method. Also see e.g. 
-
- Input: 1
-    Required:
-        {STRING | NUMBER | NULL | WebElement} - ID of the frame which can be one of the three
-                                   mentioned.
-
- Usage:
-    $driver->switch_to_frame('frame_1');
-    or
-    $driver->switch_to_frame($driver->find_element('iframe', 'tag_name'));
-
-=cut
-
-sub switch_to_frame {
-    my ( $self, $id ) = @_;
-    my $json_null = JSON::null;
-    my $params;
-    $id = ( defined $id ) ? $id : $json_null;
-
-    my $res    = { 'command' => 'switchToFrame' };
-    if (ref $id eq 'Selenium::Remote::WebElement') {
-        $params = { 'id' => {'ELEMENT' => $id->{'id'}}};
-    } else {
-        $params = { 'id'      => $id };
-    }
-    return $self->_execute_command( $res, $params );
-}
-
-=head2 switch_to_window
-
- Description:
-    Change focus to another window. The window to change focus to may be
-    specified by its server assigned window handle, or by the value of its name
-    attribute.
-
- Input: 1
-    Required:
-        STRING - Window handle or the Window name
-
- Usage:
-    $driver->switch_to_window('MY Homepage');
- or
-    # close a popup window and switch back
-    my $handles = $driver->get_window_handles;
-    $driver->switch_to_window($handles->[1]);
-    $driver->close;
-    $driver->switch_to_window($handles->[0]);
-
-=cut
-
-sub switch_to_window {
-    my ( $self, $name ) = @_;
-    if ( not defined $name ) {
-        return 'Window name not provided';
-    }
-    my $res    = { 'command' => 'switchToWindow' };
-    my $params = { 'name'    => $name };
-    return $self->_execute_command( $res, $params );
-}
-
-=head2 get_speed
-
- Description:
-    Get the current user input speed. The actual input speed is still browser
-    specific and not covered by the Driver.
-
- Output:
-    STRING - One of these: SLOW, MEDIUM, FAST
-
- Usage:
-    print $driver->get_speed();
-
-=cut
-
-sub get_speed {
-    my ($self) = @_;
-    my $res = { 'command' => 'getSpeed' };
-    return $self->_execute_command($res);
-}
-
-=head2 set_speed
-
- Description:
-    Set the user input speed.
-
- Input:
-    STRING - One of these: SLOW, MEDIUM, FAST
-
- Usage:
-    $driver->set_speed('MEDIUM');
-
- Note: This function is a no-op in WebDriver (?). See
-       https://groups.google.com/d/topic/selenium-users/oX0ZnYFPuSA/discussion and
-       http://code.google.com/p/selenium/source/browse/trunk/java/client/src/org/openqa/selenium/WebDriverCommandProcessor.java
-
-=cut
-
-sub set_speed {
-    my ( $self, $speed ) = @_;
-    if ( not defined $speed ) {
-        return 'Speed not provided.';
-    }
-    my $res    = { 'command' => 'setSpeed' };
-    my $params = { 'speed'   => $speed };
-    return $self->_execute_command( $res, $params );
-}
-
-=head2 set_window_position
-
- Description:
-    Set the position (on screen) where you want your browser to be displayed.
-
- Input:
-    INT - x co-ordinate
-    INT - y co-ordinate
-    STRING - <optional> - window handle (default is 'current' window)
-
- Output:
-    BOOLEAN - Success or failure
-
- Usage:
-    $driver->set_window_position(50, 50);
-
-=cut
-
-sub set_window_position {
-    my ( $self, $x, $y, $window ) = @_;
-    $window = (defined $window)?$window:'current';
-    if (not defined $x and not defined $y){
-        return "X & Y co-ordinates are required";
-    }
-    my $res = { 'command' => 'setWindowPosition', 'window_handle' => $window };
-    my $params = { 'x' => $x, 'y' => $y };
-    my $ret = $self->_execute_command($res, $params);
-    if ($ret =~ m/204/g) {
-        return 1;
-    }
-    else { return 0; }
-}
-
-=head2 set_window_size
-
- Description:
-    Set the size of the browser window
-
- Input:
-    INT - height of the window
-    INT - width of the window
-    STRING - <optional> - window handle (default is 'current' window)
- 
- Output:
-    BOOLEAN - Success or failure
-
- Usage:
-    $driver->set_window_size(640, 480);
-
-=cut
-
-sub set_window_size {
-    my ( $self, $height, $width, $window ) = @_;
-    $window = (defined $window)?$window:'current';
-    if (not defined $height and not defined $width){
-        return "height & width of browser are required";
-    }
-    my $res = { 'command' => 'setWindowSize', 'window_handle' => $window };
-    my $params = { 'height' => $height, 'width' => $width };
-    my $ret = $self->_execute_command($res, $params);
-    if ($ret =~ m/204/g) {
-        return 1;
-    }
-    else { return 0; }
-}
-
-=head2 get_all_cookies
-
- Description:
-    Retrieve all cookies visible to the current page. Each cookie will be
-    returned as a HASH reference with the following keys & their value types:
-    
-    'name' - STRING
-    'value' - STRING
-    'path' - STRING
-    'domain' - STRING
-    'secure' - BOOLEAN
-
- Output:
-    ARRAY of HASHES - list of all the cookie hashes
-
- Usage:
-    print Dumper($driver->get_all_cookies());
-
-=cut
-
-sub get_all_cookies {
-    my ($self) = @_;
-    my $res = { 'command' => 'getAllCookies' };
-    return $self->_execute_command($res);
-}
-
-=head2 add_cookie
-
- Description:
-    Set a cookie on the domain.
-
- Input: 5 (1 optional)
-    Required:
-        'name' - STRING
-        'value' - STRING
-        'path' - STRING
-        'domain' - STRING
-    Optional:
-        'secure' - BOOLEAN - default is false.
-
- Usage:
-    $driver->add_cookie('foo', 'bar', '/', '.google.com', 0)
-
-=cut
-
-sub add_cookie {
-    my ( $self, $name, $value, $path, $domain, $secure ) = @_;
-
-    if (   ( not defined $name )
-        || ( not defined $value )
-        || ( not defined $path )
-        || ( not defined $domain ) )
-    {
-        return "Missing parameters";
-    }
-
-    my $res        = { 'command' => 'addCookie' };
-    my $json_false = JSON::false;
-    my $json_true  = JSON::true;
-    $secure = ( defined $secure ) ? $json_true : $json_false;
-
-    my $params = {
-        'cookie' => {
-            'name'   => $name,
-            'value'  => $value,
-            'path'   => $path,
-            'domain' => $domain,
-            'secure' => $secure,
-        }
-    };
-
-    return $self->_execute_command( $res, $params );
-}
-
-=head2 delete_all_cookies
-
- Description:
-    Delete all cookies visible to the current page.
-
- Usage:
-    $driver->delete_all_cookies();
-
-=cut
-
-sub delete_all_cookies {
-    my ($self) = @_;
-    my $res = { 'command' => 'deleteAllCookies' };
-    return $self->_execute_command($res);
-}
-
-=head2 delete_cookie_named
-
- Description:
-    Delete the cookie with the given name. This command will be a no-op if there
-    is no such cookie visible to the current page.
-
- Input: 1
-    Required:
-        STRING - name of cookie to delete
-
- Usage:
-    $driver->delete_cookie_named('foo');
-
-=cut
-
-sub delete_cookie_named {
-    my ( $self, $cookie_name ) = @_;
-    if ( not defined $cookie_name ) {
-        return "Cookie name not provided";
-    }
-    my $res = { 'command' => 'deleteCookieNamed', 'name' => $cookie_name };
-    return $self->_execute_command($res);
-}
-
-=head2 get_page_source
-
- Description:
-    Get the current page source.
-
- Output:
-    STRING - The page source.
-
- Usage:
-    print $driver->get_page_source();
-
-=cut
-
-sub get_page_source {
-    my ($self) = @_;
-    my $res = { 'command' => 'getPageSource' };
-    return $self->_execute_command($res);
-}
-
-=head2 find_element
-
- Description:
-    Search for an element on the page, starting from the document root. The
-    located element will be returned as a WebElement object.
-
- Input: 2 (1 optional)
-    Required:
-        STRING - The search target.
-    Optional:
-        STRING - Locator scheme to use to search the element, available schemes:
-                 {class, class_name, css, id, link, link_text, partial_link_text,
-                  tag_name, name, xpath}
-                 Defaults to 'xpath'.
-
- Output:
-    Selenium::Remote::WebElement - WebElement Object
-    
- Usage:
-    $driver->find_element("//input[\@name='q']");
-
-=cut
-
-sub find_element {
-    my ( $self, $query, $method ) = @_;
-    if ( not defined $query ) {
-        return 'Search string to find element not provided.';
-    }
-    my $using = ( defined $method ) ? FINDERS->{$method} : 'xpath';
-    if (defined $using) {
-        my $res = { 'command' => 'findElement' };
-        my $params = { 'using' => $using, 'value' => $query };
-        my $ret_data = eval { $self->_execute_command( $res, $params ); };
-        if($@) {
-          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
-            # give details on what element wasn't found
-            $@ = "$1: $query,$using";
-            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
-            croak $@;
-          } else {
-            # re throw if the exception wasn't what we expected
-            die $@;
-          }
-        }
-        return new Selenium::Remote::WebElement($ret_data->{ELEMENT}, $self);
-    }
-    else {
-        croak "Bad method, expected - class, class_name, css, id, link,
-                link_text, partial_link_text, name, tag_name, xpath";
-    }
-}
-
-=head2 find_elements
-
- Description:
-    Search for multiple elements on the page, starting from the document root.
-    The located elements will be returned as an array of WebElement object.
-
- Input: 2 (1 optional)
-    Required:
-        STRING - The search target.
-    Optional:
-        STRING - Locator scheme to use to search the element, available schemes:
-                 {class, class_name, css, id, link, link_text, partial_link_text,
-                  tag_name, name, xpath}
-                 Defaults to 'xpath'.
-
- Output:
-    ARRAY of Selenium::Remote::WebElement - Array of WebElement Objects
-    
- Usage:
-    $driver->find_elements("//input");
-
-=cut
-
-sub find_elements {
-    my ( $self, $query, $method ) = @_;
-    if ( not defined $query ) {
-        return 'Search string to find element not provided.';
-    }
-    my $using = ( defined $method ) ? FINDERS->{$method} : 'xpath';
-    if (defined $using) {
-        my $res = { 'command' => 'findElements' };
-        my $params = { 'using' => $using, 'value' => $query };
-        my $ret_data = eval {$self->_execute_command( $res, $params );};
-         if($@) {
-          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
-            # give details on what element wasn't found
-            $@ = "$1: $query,$using";
-            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
-            croak $@;
-          } else {
-            # re throw if the exception wasn't what we expected
-            die $@;
-          }
-        }
-        my $elem_obj_arr;
-        my $i = 0;
-        foreach (@$ret_data) {
-            $elem_obj_arr->[$i] = new Selenium::Remote::WebElement($_->{ELEMENT}, $self);
-            $i++;
-        }
-        return wantarray?@{$elem_obj_arr}:$elem_obj_arr;
-    }
-    else {
-        croak "Bad method, expected - class, class_name, css, id, link,
-                link_text, partial_link_text, name, tag_name, xpath";
-    }
-}
-
-=head2 find_child_element
-
- Description:
-    Search for an element on the page, starting from the identified element. The
-    located element will be returned as a WebElement object.
-
- Input: 3 (1 optional)
-    Required:
-        Selenium::Remote::WebElement - WebElement object from where you want to
-                                       start searching.
-        STRING - The search target. (Do not use a double whack('//')
-                 in an xpath to search for a child element
-                 ex: '//option[@id="something"]'
-                 instead use a dot whack ('./')
-                 ex: './option[@id="something"]')
-    Optional:
-        STRING - Locator scheme to use to search the element, available schemes:
-                 {class, class_name, css, id, link, link_text, partial_link_text,
-                  tag_name, name, xpath}
-                 Defaults to 'xpath'.
-
- Output:
-    Selenium::Remote::WebElement - WebElement Object
-    
- Usage:
-    my $elem1 = $driver->find_element("//select[\@name='ned']");
-    # note the usage of ./ when searching for a child element instead of //
-    my $child = $driver->find_child_element($elem1, "./option[\@value='es_ar']");
-
-=cut
-
-sub find_child_element {
-    my ( $self, $elem, $query, $method ) = @_;
-    if ( ( not defined $elem ) || ( not defined $query ) ) {
-        return "Missing parameters";
-    }
-    my $using = ( defined $method ) ? $method : 'xpath';
-    if (exists FINDERS->{$using}) {
-        my $res = { 'command' => 'findChildElement', 'id' => $elem->{id} };
-        my $params = { 'using' => FINDERS->{$using}, 'value' => $query };
-        my $ret_data = eval {$self->_execute_command( $res, $params );};
-        if($@) {
-          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
-            # give details on what element wasn't found
-            $@ = "$1: $query,$using";
-            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
-            croak $@;
-          } else {
-            # re throw if the exception wasn't what we expected
-            die $@;
-          }
-        }
-        return new Selenium::Remote::WebElement($ret_data->{ELEMENT}, $self);
-    }
-    else {
-        croak "Bad method, expected - class, class_name, css, id, link,
-                link_text, partial_link_text, name, tag_name, xpath";
-    }
-}
-
-=head2 find_child_elements
-
- Description:
-    Search for multiple element on the page, starting from the identified
-    element. The located elements will be returned as an array of WebElement
-    objects.
-
- Input: 3 (1 optional)
-    Required:
-        Selenium::Remote::WebElement - WebElement object from where you want to
-                                       start searching.
-        STRING - The search target.
-    Optional:
-        STRING - Locator scheme to use to search the element, available schemes:
-                 {class, class_name, css, id, link, link_text, partial_link_text,
-                  tag_name, name, xpath}
-                 Defaults to 'xpath'.
-
- Output:
-    ARRAY of Selenium::Remote::WebElement - Array of WebElement Objects.
-    
- Usage:
-    my $elem1 = $driver->find_element("//select[\@name='ned']");
-    my $child = $driver->find_child_elements($elem1, "//option");
-
-=cut
-
-sub find_child_elements {
-    my ( $self, $elem, $query, $method ) = @_;
-    if ( ( not defined $elem ) || ( not defined $query ) ) {
-        return "Missing parameters";
-    }
-    my $using = ( defined $method ) ? $method : 'xpath';
-    if (exists FINDERS->{$using}) {
-        my $res = { 'command' => 'findChildElements', 'id' => $elem->{id} };
-        my $params = { 'using' => FINDERS->{$using}, 'value' => $query };
-        my $ret_data = eval {$self->_execute_command( $res, $params );};
-        if($@) {
-          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
-            # give details on what element wasn't found
-            $@ = "$1: $query,$using";
-            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
-            croak $@;
-          } else {
-            # re throw if the exception wasn't what we expected
-            die $@;
-          }
-        }
-        my $elem_obj_arr;
-        my $i = 0;
-        foreach (@$ret_data) {
-            $elem_obj_arr->[$i] = new Selenium::Remote::WebElement($_->{ELEMENT}, $self);
-            $i++;
-        }
-        return wantarray?@{$elem_obj_arr}:$elem_obj_arr;
-    }
-    else {
-        croak "Bad method, expected - class, class_name, css, id, link,
-                link_text, partial_link_text, name, tag_name, xpath";
-    }
-}
-
-=head2 get_active_element
-
- Description:
-    Get the element on the page that currently has focus.. The located element
-    will be returned as a WebElement object.
-
- Output:
-    Selenium::Remote::WebElement - WebElement Object
-    
- Usage:
-    $driver->get_active_element();
-
-=cut
-
-sub get_active_element {
-    my ($self) = @_;
-    my $res = { 'command' => 'getActiveElement' };
-    my $ret_data = eval { $self->_execute_command($res) };
-    if ($@) {
-        croak $@;
-    } else {
-        return new Selenium::Remote::WebElement($ret_data->{ELEMENT}, $self);
-    }
-}
-
-=head2 send_modifier
-
- Description:
-    Send an event to the active element to depress or release a modifier key.
-
-  Input: 2
-    Required:
-      value - String - The modifier key event to be sent. This key must be one 'Ctrl','Shift','Alt',' or 'Command'/'Meta' as defined by the send keys command
-      isdown - Boolean/String - Whether to generate a key down or key up
-
- Usage:
-    $driver->send_modifier('Alt','down');
-    $elem->send_keys('c');
-    $driver->send_modifier('Alt','up');
-
-    or
-
-    $driver->send_modifier('Alt',1);
-    $elem->send_keys('c');
-    $driver->send_modifier('Alt',0);
-
-=cut
-
-sub send_modifier {
-  my ($self,$modifier,$isdown) = @_;
-  if($isdown =~ /(down|up)/) {
-    $isdown = $isdown =~ /down/ ? 1:0;
-  }
-  my $res = {'command' => 'sendModifier'};
-  my $params = {value => $modifier,
-                isdown => $isdown};
-  return $self->_execute_command($res,$params);
-}
-
-=head2 compare_elements
-
- Description:
-    Test if two element IDs refer to the same DOM element.
-
- Input: 2
-    Required:
-        Selenium::Remote::WebElement - WebElement Object
-        Selenium::Remote::WebElement - WebElement Object
-
- Output:
-    BOOLEAN
-    
- Usage:
-    $driver->compare_elements($elem_obj1, $elem_obj2);
-
-=cut
-
-sub compare_elements {
-    my ($self, $elem1, $elem2) = @_;
-    my $res = { 'command' => 'elementEquals',
-                'id' => $elem1->{id},
-                'other' => $elem2->{id}
-              };
-    return $self->_execute_command($res);
-}
-
-=head2 click
-
- Description:
-    Click any mouse button (at the coordinates set by the last moveto command).
-
- Input:
-    button - any one of 'LEFT'/0 'MIDDLE'/1 'RIGHT'/2
-             defaults to 'LEFT'
-
- Usage:
-    $driver->click('LEFT');
-    $driver->click(1); #MIDDLE
-    $driver->click('RIGHT');
-    $driver->click;  #Defaults to left
-
-=cut
-
-sub click {
-  my ($self,$button) = @_;
-  my $button_enum = {LEFT=>0,MIDDLE=>1,RIGHT=>2};
-  if(defined $button && $button =~ /(LEFT|MIDDLE|RIGHT)/i) {
-    $button = $button_enum->{uc $1};
-  } elsif(defined $button && $button =~ /(0|1|2)/) {
-    $button = $1;
-  } else {
-    $button = 0;
-  }
-  my $res = { 'command' => 'click' };
-  my $params = { 'button' => $button };
-  return $self->_execute_command($res,$params);
-}
-
-=head2 double_click
-
- Description:
-    Double-clicks at the current mouse coordinates (set by moveto).
-
- Usage:
-    $driver->double_click;
-
-=cut
-
-sub double_click {
-  my ($self) = @_;
-  my $res = { 'command' => 'doubleClick' };
-  return $self->_execute_command($res);
-}
-
-=head2 button_down
-
- Description:
-    Click and hold the left mouse button (at the coordinates set by the
-    last moveto command). Note that the next mouse-related command that
-    should follow is buttondown . Any other mouse command (such as click
-    or another call to buttondown) will yield undefined behaviour.
-
- Usage:
-    $self->button_down;
-
-=cut
-
-sub button_down {
-  my ($self) = @_;
-  my $res = { 'command' => 'buttonDown' };
-  return $self->_execute_command($res);
-}
-
-=head2 button_up
-
- Description:
-    Releases the mouse button previously held (where the mouse is
-    currently at). Must be called once for every buttondown command
-    issued. See the note in click and buttondown about implications of
-    out-of-order commands.
-
- Usage:
-    $self->button_up;
-
-=cut
-
-sub button_up {
-  my ($self) = @_;
-  my $res = { 'command' => 'buttonUp' };
-  return $self->_execute_command($res);
-}
-
-1;
-
-__END__
-
-=head1 SEE ALSO
-
-For more information about Selenium , visit the website at
-L<http://code.google.com/p/selenium/>.
-
-Also checkout project's wiki page at
-L<https://github.com/aivaturi/Selenium-Remote-Driver/wiki>.
-
-=head1 BUGS
-
-The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
-
-=head1 AUTHOR
-
-Perl Bindings for Selenium Remote Driver by Aditya Ivaturi C<< <ivaturi@gmail.com> >>
-
-=head1 ACKNOWLEDGEMENTS
-
-The following people have contributed to this module. (Thanks!)
-
-=over 4
-
-=item * Gordon Child
-
-=item * Phil Kania
-
-=item * Phil Mitchell
-
-=item * Allen Lew
-
-=item * Tom Hukins
-
-=back
-
-=head1 LICENSE
-
-Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+package Selenium::Remote::Driver;
+
+use strict;
+use warnings;
+
+use Carp;
+our @CARP_NOT;
+
+use Selenium::Remote::RemoteConnection;
+use Selenium::Remote::Commands;
+use Selenium::Remote::WebElement;
+
+use constant FINDERS => {
+      class             => 'class name',
+      class_name        => 'class name',
+      css               => 'css selector',
+      id                => 'id',
+      link              => 'link text',
+      link_text         => 'link text',
+      name              => 'name',
+      partial_link_text => 'partial link text',
+      tag_name          => 'tag name',
+      xpath             => 'xpath',
+};
+
+=head1 NAME
+
+Selenium::Remote::Driver - Perl Client for Selenium Remote Driver
+
+=cut
+
+=head1 SYNOPSIS
+
+    use Selenium::Remote::Driver;
+
+    my $driver = new Selenium::Remote::Driver;
+    $driver->get('http://www.google.com');
+    print $driver->get_title();
+    $driver->quit();
+
+=cut
+
+=head1 DESCRIPTION
+
+Selenium is a test tool that allows you to write
+automated web application UI tests in any programming language against
+any HTTP website using any mainstream JavaScript-enabled browser. This module is
+an implementation of the client for the Remote driver that Selenium provides.
+You can find bindings for other languages at this location:
+
+L<http://code.google.com/p/selenium/>
+
+This module sends commands directly to the Server using HTTP. Using this module
+together with the Selenium Server, you can automatically control any supported
+browser. To use this module, you need to have already downloaded and started
+the Selenium Server (Selenium Server is a Java application).
+
+=cut
+
+=head1 USAGE (read this first)
+
+=head2 Remote Driver Response
+
+Selenium::Remote::Driver uses the L<JsonWireProtocol|http://code.google.com/p/selenium/wiki/JsonWireProtocol> to communicate with the
+Selenium Server. If an error occurs while executing the command then the server
+sends back an HTTP error code with a JSON encoded reponse that indicates the
+precise L<Response Error Code|http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes>. The module will then croak with the error message
+associated with this code. If no error occurred, then the subroutine called will
+return the value sent back from the server (if a return value was sent).
+
+So a rule of thumb while invoking methods on the driver is if the method did not
+croak when called, then you can safely assume the command was successful even if
+nothing was returned by the method.
+
+=head2 WebElement
+
+Selenium Webdriver represents all the HTML elements as WebElement, which is
+in turn represented by Selenium::Remote::WebElement module. So any method that
+deals with WebElements will return and/or expect WebElement object. The POD for
+that module describes all the methods that perform various actions on the
+WebElements like click, submit etc.
+
+To interact with any WebElement you have to first "find" it, read the POD for
+find_element or find_elements for further info. Once you find the required element
+then you can perform various actions. If you don't call find_* method first, all
+your further actions will fail for that element. Finally, just remember that you
+don't have to instantiate WebElement objects at all - they will be automatically
+created when you use the find_* methods.
+
+=cut
+
+=head1 FUNCTIONS
+
+=cut
+
+=head2 new
+
+ Description:
+    Constructor for Driver. It'll instantiate the object if it can communicate
+    with the Selenium RC server.
+
+ Input: (all optional)
+    desired_capabilities - HASH - Following options are accepted:
+      Optional:
+        'remote_server_addr' - <string> - IP or FQDN of the RC server machine
+        'browser_name' - <string> - desired browser string:
+                      {iphone|firefox|internet explorer|htmlunit|iphone|chrome}
+        'version' - <string> - desired browser version number
+        'platform' - <string> - desired platform:
+                                {WINDOWS|XP|VISTA|MAC|LINUX|UNIX|ANY}
+        'javascript' - <boolean> - whether javascript should be supported
+        'accept_ssl_certs' - <boolean> - whether SSL certs should be accepted, default is true.
+        'auto_close' - <boolean> - whether driver should end session on remote
+                                   server on close.
+        'extra_capabilities' - HASH of extra capabilities
+        'proxy' - HASH - Proxy configuration with the following keys:
+            'proxyType' - <string> - REQUIRED, Possible values are:
+                direct - A direct connection - no proxy in use,
+                manual - Manual proxy settings configured, e.g. setting a proxy for HTTP, a proxy for FTP, etc,
+                pac - Proxy autoconfiguration from a URL,
+                autodetect - proxy autodetection, probably with WPAD,
+                system - Use system settings
+            'proxyAutoconfigUrl' - <string> - REQUIRED if proxyType is 'pac', ignored otherwise. Expected format: http://hostname.com:1234/pacfile.
+            'ftpProxy' - <string> - OPTIONAL, ignored if proxyType is not 'manual'. Expected format: hostname.com:1234
+            'httpProxy' - <string> - OPTIONAL, ignored if proxyType is not 'manual'. Expected format: hostname.com:1234
+            'sslProxy' - <string> - OPTIONAL, ignored if proxyType is not 'manual'. Expected format: hostname.com:1234
+            
+        If no values are provided, then these defaults will be assumed:
+            'remote_server_addr' => 'localhost'
+            'port'         => '4444'
+            'browser_name' => 'firefox'
+            'version'      => ''
+            'platform'     => 'ANY'
+            'javascript'   => 1
+            'auto_close'   => 1
+
+ Output:
+    Remote Driver object
+
+ Usage:
+    my $driver = new Selenium::Remote::Driver;
+    or
+    my $driver = new Selenium::Remote::Driver('browser_name' => 'firefox',
+                                              'platform' => 'MAC');
+    or
+    my $driver = new Selenium::Remote::Driver('remote_server_addr' => '10.10.1.1',
+                                              'port' => '2222',
+                                              auto_close => 0
+                                              );
+    or
+    my $driver = new Selenium::Remote::Driver('browser_name'       => 'chrome',
+                                              'platform'           => 'VISTA',
+                                              'extra_capabilities' => {'chrome.switches' => ["--user-data-dir=$ENV{LOCALAPPDATA}\\Google\\Chrome\\User Data"],},
+                                              );
+    or
+    my $driver = Selenium::Remote::Driver->new('proxy' => {'proxyType' => 'manual', 'httpProxy' => 'myproxy.com:1234'});
+    
+=cut
+
+sub new {
+    my ( $class, %args ) = @_;
+    my $ress = new Selenium::Remote::Commands;
+
+    # Set the defaults if user doesn't send any
+    my $self = {
+        remote_server_addr => delete $args{remote_server_addr} || 'localhost',
+        browser_name       => delete $args{browser_name}       || 'firefox',
+        platform           => delete $args{platform}           || 'ANY',
+        port               => delete $args{port}               || '4444',
+        version            => delete $args{version}            || '',
+        session_id         => undef,
+        remote_conn        => undef,
+        commands           => $ress,
+        auto_close         => 1, # by default we will close remote session on DESTROY
+        pid                => $$,
+    };
+    bless $self, $class or die "Can't bless $class: $!";
+    
+    # check for javascript 
+    if ( defined $args{javascript} ) {
+        if ( $args{javascript} ) {
+            $self->{javascript} = JSON::true;
+        }
+        else {
+            $self->{javascript} = JSON::false;
+        }
+    }
+    else {
+        $self->{javascript} = JSON::true;
+    }
+    
+    # check for acceptSslCerts
+    if ( defined $args{accept_ssl_certs} ) {
+        if ( $args{accept_ssl_certs} ) {
+            $self->{accept_ssl_certs} = JSON::true;
+        }
+        else {
+            $self->{accept_ssl_certs} = JSON::false;
+        }
+    }
+    else {
+        $self->{accept_ssl_certs} = JSON::true;
+    }
+    
+    # check for proxy
+    if ( defined $args{proxy} ) {
+        if ($args{proxy}{proxyType} eq 'pac') {
+            if (not defined $args{proxy}{proxyAutoconfigUrl}) {
+                croak "proxyAutoconfigUrl not provided\n";
+            } elsif (not ($args{proxy}{proxyAutoconfigUrl} =~ /^http/g)) {
+                croak "proxyAutoconfigUrl should be of format http://";
+            }
+        }
+        $self->{proxy} = $args{proxy};
+    }
+
+    # Connect to remote server & establish a new session
+    $self->{remote_conn} =
+      new Selenium::Remote::RemoteConnection( $self->{remote_server_addr},
+        $self->{port} );
+    $self->new_session(delete $args{extra_capabilities});
+
+    if ( !( defined $self->{session_id} ) ) {
+        croak "Could not establish a session with the remote server\n";
+    }
+
+    return $self;
+}
+
+sub DESTROY {
+    my ($self) = @_;
+    return if $$ != $self->{pid};
+    $self->quit() if ($self->{auto_close} && defined $self->{session_id});
+}
+
+# This is an internal method used the Driver & is not supposed to be used by
+# end user. This method is used by Driver to set up all the parameters
+# (url & JSON), send commands & receive processed response from the server.
+sub _execute_command {
+    my ( $self, $res, $params ) = @_;
+    $res->{'session_id'} = $self->{'session_id'};
+    my $resource = $self->{commands}->get_params($res);
+    if ($resource) {
+        my $resp = $self->{remote_conn}
+          ->request( $resource->{'method'}, $resource->{'url'}, $params );
+        if(ref($resp) eq 'HASH') {
+            if($resp->{cmd_status} eq 'OK') {
+               return $resp->{cmd_return};
+            } else {
+               my $msg = "Error while executing command";
+               if($resp->{cmd_error}) {
+                 $msg .= ": $resp->{cmd_error}" if $resp->{cmd_error};
+               } else {
+                   if(ref($resp->{cmd_return}) eq 'HASH') {
+                     $msg .= ": $resp->{cmd_return}->{error}->{msg}"
+                       if $resp->{cmd_return}->{error}->{msg};
+                     $msg .= ": $resp->{cmd_return}->{message}"
+                       if $resp->{cmd_return}->{message};
+                   } else {
+                     $msg .= ": $resp->{cmd_return}";
+                   }
+               }
+               croak $msg;
+            }
+        }
+        return $resp;
+    }
+    else {
+        croak "Couldn't retrieve command settings properly\n";
+    }
+}
+
+# A method that is used by the Driver itself. It'll be called to set the
+# desired capabilities on the server.
+sub new_session {
+    my ($self, $extra_capabilities) = @_;
+    $extra_capabilities ||= {};
+    my $args = {
+        'desiredCapabilities' => {
+            'browserName'       => $self->{browser_name},
+            'platform'          => $self->{platform},
+            'javascriptEnabled' => $self->{javascript},
+            'version'           => $self->{version},
+            'acceptSslCerts'    => $self->{accept_ssl_certs},
+            'proxy'             => $self->{proxy},
+            %$extra_capabilities,
+        },
+    };
+    my $resp =
+      $self->{remote_conn}
+      ->request( $self->{commands}->{'newSession'}->{'method'},
+        $self->{commands}->{'newSession'}->{'url'}, $args, );
+    if ( ( defined $resp->{'sessionId'} ) && $resp->{'sessionId'} ne '' ) {
+        $self->{session_id} = $resp->{'sessionId'};
+    }
+    else {
+        croak "Could not create new session";
+    }
+}
+
+=head2 debug_on
+
+  Description:
+    Turns on debugging mode and the driver will print extra info like request
+    and response to stdout. Useful, when you want to see what is being sent to
+    the server & what response you are getting back.
+
+  Usage:
+    $driver->debug_on;
+
+=cut
+
+sub debug_on {
+    my ($self) = @_;
+    $self->{'remote_conn'}->{'debug'} = 1;
+}
+
+=head2 debug_off
+
+  Description:
+    Turns off the debugging mode.
+
+  Usage:
+    $driver->debug_off;
+
+=cut
+
+sub debug_off {
+    my ($self) = @_;
+    $self->{'remote_conn'}->{'debug'} = 0;
+}
+
+=head2 get_sessions
+
+  Description:
+    Returns a list of the currently active sessions. Each session will be
+    returned as an array of Hashes with the following keys:
+    
+    'id' : The session ID
+    'capabilities: An object describing session's capabilities
+
+  Output:
+    Array of Hashes
+
+  Usage:
+    print Dumper $driver->get_sessions();
+
+=cut
+
+sub get_sessions{
+    my ($self) = @_;
+    my $res = { 'command' => 'getSessions' };
+    return $self->_execute_command($res);
+}
+
+=head2 status
+
+  Description:
+    Query the server's current status. All server implementations
+    should return two basic objects describing the server's current
+    platform and when the server was built.
+
+  Output:
+    Hash ref
+
+  Usage:
+    print Dumper $driver->status;
+
+=cut
+
+sub status {
+    my ($self) = @_;
+    my $res = { 'command' => 'status' };
+    return $self->_execute_command($res);
+}
+
+=head2 get_alert_text
+
+ Description:
+    Gets the text of the currently displayed JavaScript alert(), confirm()
+    or prompt() dialog.
+
+ Example
+    my $string = $driver->get_alert_text;
+
+=cut
+
+sub get_alert_text {
+  my ($self) = @_;
+  my $res = { 'command' => 'getAlertText' };
+  return $self->_execute_command($res);
+}
+
+=head2 send_keys_to_active_element
+
+ Description:
+    Send a sequence of key strokes to the active element. This command is
+    similar to the send keys command in every aspect except the implicit
+    termination: The modifiers are not released at the end of the call.
+    Rather, the state of the modifier keys is kept between calls, so mouse
+    interactions can be performed while modifier keys are depressed.
+
+ Input: 1
+    Required:
+        {ARRAY | STRING} - Array of strings or a string.
+
+ Usage:
+    $driver->send_keys_to_active_element('abcd', 'efg');
+    $driver->send_keys_to_active_element('hijk');
+    
+    or
+    
+    # include the WDKeys module
+    use Selenium::Remote::WDKeys;
+    .
+    .
+    $driver->send_keys_to_active_element(KEYS->{'space'}, KEYS->{'enter'});
+
+=cut
+
+sub send_keys_to_active_element {
+    my ($self, @strings) = @_;
+    my $res = { 'command' => 'sendKeysToActiveElement' };
+    my $params = {
+        'value' => \@strings,
+    };
+    return $self->_execute_command($res, $params);
+}
+
+=head2 send_keys_to_alert
+
+Synonymous with send_keys_to_prompt
+
+=cut
+
+sub send_keys_to_alert {
+  return shift->send_keys_to_prompt(@_);
+}
+
+=head2 send_keys_to_prompt
+
+ Description:
+    Sends keystrokes to a JavaScript prompt() dialog.
+
+ Input:
+    {string} keys to send
+
+ Example:
+    $driver->send_keys_to_prompt('hello world');
+  or
+    ok($driver->get_alert_text eq 'Please Input your name','prompt appears');
+    $driver->send_keys_to_alert("Larry Wall");
+    $driver->accept_alert;
+
+=cut
+
+sub send_keys_to_prompt {
+  my ($self,$keys) = @_;
+  my $res = { 'command' => 'sendKeysToPrompt' };
+  my $params = { 'text' => $keys };
+  return $self->_execute_command($res,$params);
+}
+
+=head2 accept_alert
+
+ Description:
+    Accepts the currently displayed alert dialog.  Usually, this is
+    equivalent to clicking the 'OK' button in the dialog.
+
+ Example:
+    $driver->accept_alert;
+
+=cut
+
+sub accept_alert {
+  my ($self) = @_;
+  my $res = { 'command' => 'acceptAlert' };
+  return $self->_execute_command($res);
+}
+
+=head2 dismiss_alert
+
+ Description:
+    Dismisses the currently displayed alert dialog. For comfirm()
+    and prompt() dialogs, this is equivalent to clicking the
+    'Cancel' button. For alert() dialogs, this is equivalent to
+    clicking the 'OK' button.
+
+ Example:
+    $driver->dismiss_alert;
+
+=cut
+
+sub dismiss_alert {
+  my ($self) = @_;
+  my $res = { 'command' => 'dismissAlert' };
+  return $self->_execute_command($res);
+}
+
+=head2 mouse_move_to_location
+
+ Description:
+    Move the mouse by an offset of the specificed element. If no
+    element is specified, the move is relative to the current mouse
+    cursor. If an element is provided but no offset, the mouse will be
+    moved to the center of the element. If the element is not visible,
+    it will be scrolled into view.
+
+ Output:
+    STRING - 
+
+ Usage:
+    # element - the element to move to. If not specified or is null, the offset is relative to current position of the mouse.
+    # xoffset - X offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
+    # yoffset - Y offset to move to, relative to the top-left corner of the element. If not specified, the mouse will move to the middle of the element.
+
+    print $driver->mouse_move_to_location(element => e, xoffset => x, yoffset => y);
+
+=cut
+
+sub mouse_move_to_location {
+    my ($self, %params) = @_;
+    $params{element} = $params{element}{id} if exists $params{element};
+    my $res = { 'command' => 'mouseMoveToLocation' };
+    return $self->_execute_command($res, \%params);
+}
+
+=head2 move_to
+
+Synonymous with mouse_move_to_location
+
+=cut
+
+sub move_to {
+    return shift->mouse_move_to_location(@_);
+}
+
+=head2 get_capabilities
+
+ Description:
+    Retrieve the capabilities of the specified session.
+
+ Output:
+    HASH of all the capabilities.
+
+ Usage:
+    my $capab = $driver->get_capabilities();
+    print Dumper($capab);
+
+=cut
+
+sub get_capabilities {
+    my $self = shift;
+    my $res  = {'command' => 'getCapabilities'};
+    return $self->_execute_command($res);
+}
+
+=head2 set_async_script_timeout
+
+ Description:
+    Set the amount of time, in milliseconds, that asynchronous scripts executed
+    by execute_async_script() are permitted to run before they are
+    aborted and a |Timeout| error is returned to the client.
+ 
+ Input:
+    ms - <NUMBER> - The amount of time, in milliseconds, that time-limited
+            commands are permitted to run.
+
+ Usage:
+    $driver->set_async_script_timeout(1000);
+
+=cut
+
+sub set_async_script_timeout {
+    my ($self, $ms) = @_;
+    if (not defined $ms)
+    {
+        return "Expecting timeout in ms";
+    }
+    my $res  = {'command' => 'setAsyncScriptTimeout'};
+    my $params  = {'ms' => $ms};
+    return $self->_execute_command($res, $params);
+}
+
+=head2 set_implicit_wait_timeout
+
+ Description:
+    Set the amount of time the driver should wait when searching for elements.
+    When searching for a single element, the driver will poll the page until
+    an element is found or the timeout expires, whichever occurs first.
+    When searching for multiple elements, the driver should poll the page until
+    at least one element is found or the timeout expires, at which point it
+    will return an empty list. If this method is never called, the driver will
+    default to an implicit wait of 0ms.
+ 
+ Input:
+    Time in milliseconds.
+
+ Output:
+    Server Response Hash with no data returned back from the server.
+
+ Usage:
+    $driver->set_implicit_wait_timeout(10);
+
+=cut
+
+sub set_implicit_wait_timeout {
+    my ($self, $ms) = @_;
+    my $res  = {'command' => 'setImplicitWaitTimeout'};
+    my $params  = {'ms' => $ms};
+    return $self->_execute_command($res, $params);
+}
+
+=head2 close
+
+ Description:
+    Close the current window.
+
+ Usage:
+    $driver->close();
+ or
+    #close a popup window
+    my $handles = $driver->get_window_handles;
+    $driver->switch_to_window($handles->[1]);
+    $driver->close();
+    $driver->switch_to_window($handles->[0]);
+
+=cut
+
+sub close {
+  my $self = shift;
+  my $res = { 'command' => 'close' };
+  $self->_execute_command($res);
+}
+
+=head2 quit
+
+ Description:
+    Delete the session & close open browsers.
+
+ Usage:
+    $driver->quit();
+
+=cut
+
+sub quit {
+    my $self = shift;
+    my $res = { 'command' => 'quit' };
+    $self->_execute_command($res);
+    $self->{session_id} = undef;
+}
+
+=head2 get_current_window_handle
+
+ Description:
+    Retrieve the current window handle.
+
+ Output:
+    STRING - the window handle
+
+ Usage:
+    print $driver->get_current_window_handle();
+
+=cut
+
+sub get_current_window_handle {
+    my $self = shift;
+    my $res = { 'command' => 'getCurrentWindowHandle' };
+    return $self->_execute_command($res);
+}
+
+=head2 get_window_handles
+
+ Description:
+    Retrieve the list of window handles used in the session.
+
+ Output:
+    ARRAY of STRING - list of the window handles
+
+ Usage:
+    print Dumper $driver->get_window_handles;
+ or
+    # get popup, close, then back
+    my $handles = $driver->get_window_handles;
+    $driver->switch_to_window($handles->[1]);
+    $driver->close;
+    $driver->switch_to_window($handles->[0]);
+
+=cut
+
+sub get_window_handles {
+    my $self = shift;
+    my $res = { 'command' => 'getWindowHandles' };
+    return $self->_execute_command($res);
+}
+
+=head2 get_window_size
+
+ Description:
+    Retrieve the window size
+ 
+ Input:
+    STRING - <optional> - window handle (default is 'current' window)
+
+ Output:
+    HASH - containing keys 'height' & 'width'
+
+ Usage:
+    my $window_size = $driver->get_window_size();
+    print $window_size->{'height'}, $window_size->('width');
+
+=cut
+
+sub get_window_size {
+    my ( $self, $window ) = @_;
+    $window = (defined $window)?$window:'current';
+    my $res = { 'command' => 'getWindowSize', 'window_handle' => $window };
+    return $self->_execute_command($res);
+}
+
+=head2 get_window_position
+
+ Description:
+    Retrieve the window position
+ 
+ Input:
+    STRING - <optional> - window handle (default is 'current' window)
+
+ Output:
+    HASH - containing keys 'x' & 'y'
+
+ Usage:
+    my $window_size = $driver->get_window_position();
+    print $window_size->{'x'}, $window_size->('y');
+
+=cut
+
+sub get_window_position {
+    my ( $self, $window ) = @_;
+    $window = (defined $window)?$window:'current';
+    my $res = { 'command' => 'getWindowPosition', 'window_handle' => $window };
+    return $self->_execute_command($res);
+}
+
+=head2 get_current_url
+
+ Description:
+    Retrieve the url of the current page
+
+ Output:
+    STRING - url
+
+ Usage:
+    print $driver->get_current_url();
+
+=cut
+
+sub get_current_url {
+    my $self = shift;
+    my $res = { 'command' => 'getCurrentUrl' };
+    return $self->_execute_command($res);
+}
+
+=head2 navigate
+
+ Description:
+    Navigate to a given url. This is same as get() method.
+    
+ Input:
+    STRING - url
+
+ Usage:
+    $driver->navigate('http://www.google.com');
+
+=cut
+
+sub navigate {
+    my ( $self, $url ) = @_;
+    $self->get($url);
+}
+
+=head2 get
+
+ Description:
+    Navigate to a given url
+    
+ Input:
+    STRING - url
+
+ Usage:
+    $driver->get('http://www.google.com');
+
+=cut
+
+sub get {
+    my ( $self, $url ) = @_;
+    my $res    = { 'command' => 'get' };
+    my $params = { 'url'     => $url };
+    return $self->_execute_command( $res, $params );
+}
+
+=head2 get_title
+
+ Description:
+    Get the current page title
+
+ Output:
+    STRING - Page title
+
+ Usage:
+    print $driver->get_title();
+
+=cut
+
+sub get_title {
+    my $self = shift;
+    my $res = { 'command' => 'getTitle' };
+    return $self->_execute_command($res);
+}
+
+=head2 go_back
+
+ Description:
+    Equivalent to hitting the back button on the browser.
+
+ Usage:
+    $driver->go_back();
+
+=cut
+
+sub go_back {
+    my $self = shift;
+    my $res = { 'command' => 'goBack' };
+    return $self->_execute_command($res);
+}
+
+=head2 go_forward
+
+ Description:
+    Equivalent to hitting the forward button on the browser.
+
+ Usage:
+    $driver->go_forward();
+
+=cut
+
+sub go_forward {
+    my $self = shift;
+    my $res = { 'command' => 'goForward' };
+    return $self->_execute_command($res);
+}
+
+=head2 refresh
+
+ Description:
+    Reload the current page.
+
+ Usage:
+    $driver->refresh();
+
+=cut
+
+sub refresh {
+    my $self = shift;
+    my $res = { 'command' => 'refresh' };
+    return $self->_execute_command($res);
+}
+
+=head2 javascript
+
+ Description:
+    returns true if javascript is enabled in the driver.
+
+ Usage:
+    if ($driver->javascript) { ...; }
+
+=cut
+
+sub javascript {
+    my $self = shift;
+    return $self->{javascript} == JSON::true;
+}
+
+=head2 execute_async_script
+
+ Description:
+    Inject a snippet of JavaScript into the page for execution in the context
+    of the currently selected frame. The executed script is assumed to be
+    asynchronous and must signal that is done by invoking the provided
+    callback, which is always provided as the final argument to the function.
+    The value to this callback will be returned to the client.
+
+    Asynchronous script commands may not span page loads. If an unload event
+    is fired while waiting for a script result, an error should be returned
+    to the client.
+
+ Input: 2 (1 optional)
+    Required:
+        STRING - Javascript to execute on the page
+    Optional:
+        ARRAY - list of arguments that need to be passed to the script.
+
+ Output:
+    {*} - Varied, depending on the type of result expected back from the script.
+
+ Usage:
+    my $script = q{
+        var arg1 = arguments[0];
+        var callback = arguments[arguments.length-1];
+        var elem = window.document.findElementById(arg1);
+        callback(elem);
+    };
+    my $callback = q{return arguments[0];};
+    my $elem = $driver->execute_async_script($script,'myid',$callback);
+    $elem->click;
+
+=cut
+
+sub execute_async_script {
+    my ( $self, $script, @args ) = @_;
+    if ($self->javascript) {
+        if ( not defined $script ) {
+            return 'No script provided';
+        }
+        my $res  = { 'command'    => 'executeAsyncScript' };
+
+        # Check the args array if the elem obj is provided & replace it with
+        # JSON representation
+        for (my $i=0; $i<@args; $i++) {
+            if (ref $args[$i] eq 'Selenium::Remote::WebElement') {
+                $args[$i] = {'ELEMENT' => ($args[$i])->{id}};
+            }
+        }
+
+        my $params = {'script' => $script, 'args' => \@args};
+        my $ret = $self->_execute_command($res, $params);
+
+        # replace any ELEMENTS with WebElement
+        if (ref($ret) and (ref($ret) eq 'HASH') and exists $ret->{'ELEMENT'}) {
+            $ret =
+                new Selenium::Remote::WebElement(
+                                        $ret->{ELEMENT}, $self);
+        }
+        return $ret;
+    }
+    else {
+        croak 'Javascript is not enabled on remote driver instance.';
+    }
+}
+
+=head2 execute_script
+
+ Description:
+    Inject a snippet of JavaScript into the page and return its result.
+    WebElements that should be passed to the script as an argument should be
+    specified in the arguments array as WebElement object. Likewise,
+    any WebElements in the script result will be returned as WebElement object.
+
+ Input: 2 (1 optional)
+    Required:
+        STRING - Javascript to execute on the page
+    Optional:
+        ARRAY - list of arguments that need to be passed to the script.
+
+ Output:
+    {*} - Varied, depending on the type of result expected back from the script.
+
+ Usage:
+    my $script = q{
+        var arg1 = arguments[0];
+        var elem = window.document.findElementById(arg1);
+        return elem;
+    };
+    my $elem = $driver->execute_script($script,'myid');
+    $elem->click;
+
+=cut
+
+sub execute_script {
+    my ( $self, $script, @args ) = @_;
+    if ($self->javascript) {
+        if ( not defined $script ) {
+            return 'No script provided';
+        }
+        my $res  = { 'command'    => 'executeScript' };
+        
+        # Check the args array if the elem obj is provided & replace it with
+        # JSON representation
+        for (my $i=0; $i<@args; $i++) {
+            if (ref $args[$i] eq 'Selenium::Remote::WebElement') {
+                $args[$i] = {'ELEMENT' => ($args[$i])->{id}};
+            }
+        }
+        
+        my $params = {'script' => $script, 'args' => [@args]};
+        my $ret = $self->_execute_command($res, $params);
+        
+        return $self->_convert_to_webelement($ret);
+    }
+    else {
+        croak 'Javascript is not enabled on remote driver instance.';
+    }
+}
+
+# _convert_to_webelement
+# An internal method used to traverse a data structure
+# and convert any ELEMENTS with WebElements
+
+sub _convert_to_webelement {
+    my ($self, $ret ) = @_;
+
+    if (ref($ret) and (ref($ret) eq 'HASH')) {
+        if((keys %$ret==1) and exists $ret->{'ELEMENT'}) {
+            # replace an ELEMENT with WebElement
+            return new Selenium::Remote::WebElement($ret->{ELEMENT}, $self);
+        }
+
+        my %hash;
+        foreach my $key (keys %$ret) {
+            $hash{$key}=$self->_convert_to_webelement($ret->{$key});
+        }
+        return \%hash;
+    }
+
+    if(ref($ret) and (ref($ret) eq 'ARRAY')) {
+        my @array = map {$self->_convert_to_webelement($_)} @$ret;
+        return \@array;
+    }
+
+    return $ret;
+}
+
+=head2 screenshot
+
+ Description:
+    Get a screenshot of the current page as a base64 encoded image.
+
+ Output:
+    STRING - base64 encoded image
+
+ Usage:
+    print $driver->screenshot();
+ or
+    require MIME::Base64;
+    open(FH,'>','screenshot.png');
+    binmode FH;
+    my $png_base64 = $driver->screenshot();
+    print FH MIME::Base64::decode_base64($png_base64);
+    close FH;
+
+=cut
+
+sub screenshot {
+    my ($self) = @_;
+    my $res = { 'command' => 'screenshot' };
+    return $self->_execute_command($res);
+}
+
+=head2 available_engines
+
+ Description:
+    List all available engines on the machine. To use an engine, it has to be present in this list.
+
+ Output:
+    {Array.<string>} A list of available engines
+
+ Usage:
+    print Dumper $driver->available_engines;
+
+=cut
+
+sub available_engines {
+    my ($self) = @_;
+    my $res = { 'command' => 'availableEngines' };
+    return $self->_execute_command($res);
+}
+
+=head2 switch_to_frame
+
+ Description:
+    Change focus to another frame on the page. If the frame ID is null, the
+    server will switch to the page's default content. You can also switch to a
+    WebElement, for e.g. you can find an iframe using find_element & then
+    provide that as an input to this method. Also see e.g. 
+
+ Input: 1
+    Required:
+        {STRING | NUMBER | NULL | WebElement} - ID of the frame which can be one of the three
+                                   mentioned.
+
+ Usage:
+    $driver->switch_to_frame('frame_1');
+    or
+    $driver->switch_to_frame($driver->find_element('iframe', 'tag_name'));
+
+=cut
+
+sub switch_to_frame {
+    my ( $self, $id ) = @_;
+    my $json_null = JSON::null;
+    my $params;
+    $id = ( defined $id ) ? $id : $json_null;
+
+    my $res    = { 'command' => 'switchToFrame' };
+    if (ref $id eq 'Selenium::Remote::WebElement') {
+        $params = { 'id' => {'ELEMENT' => $id->{'id'}}};
+    } else {
+        $params = { 'id'      => $id };
+    }
+    return $self->_execute_command( $res, $params );
+}
+
+=head2 switch_to_window
+
+ Description:
+    Change focus to another window. The window to change focus to may be
+    specified by its server assigned window handle, or by the value of its name
+    attribute.
+
+ Input: 1
+    Required:
+        STRING - Window handle or the Window name
+
+ Usage:
+    $driver->switch_to_window('MY Homepage');
+ or
+    # close a popup window and switch back
+    my $handles = $driver->get_window_handles;
+    $driver->switch_to_window($handles->[1]);
+    $driver->close;
+    $driver->switch_to_window($handles->[0]);
+
+=cut
+
+sub switch_to_window {
+    my ( $self, $name ) = @_;
+    if ( not defined $name ) {
+        return 'Window name not provided';
+    }
+    my $res    = { 'command' => 'switchToWindow' };
+    my $params = { 'name'    => $name };
+    return $self->_execute_command( $res, $params );
+}
+
+=head2 get_speed
+
+ Description:
+    Get the current user input speed. The actual input speed is still browser
+    specific and not covered by the Driver.
+
+ Output:
+    STRING - One of these: SLOW, MEDIUM, FAST
+
+ Usage:
+    print $driver->get_speed();
+
+=cut
+
+sub get_speed {
+    my ($self) = @_;
+    my $res = { 'command' => 'getSpeed' };
+    return $self->_execute_command($res);
+}
+
+=head2 set_speed
+
+ Description:
+    Set the user input speed.
+
+ Input:
+    STRING - One of these: SLOW, MEDIUM, FAST
+
+ Usage:
+    $driver->set_speed('MEDIUM');
+
+ Note: This function is a no-op in WebDriver (?). See
+       https://groups.google.com/d/topic/selenium-users/oX0ZnYFPuSA/discussion and
+       http://code.google.com/p/selenium/source/browse/trunk/java/client/src/org/openqa/selenium/WebDriverCommandProcessor.java
+
+=cut
+
+sub set_speed {
+    my ( $self, $speed ) = @_;
+    if ( not defined $speed ) {
+        return 'Speed not provided.';
+    }
+    my $res    = { 'command' => 'setSpeed' };
+    my $params = { 'speed'   => $speed };
+    return $self->_execute_command( $res, $params );
+}
+
+=head2 set_window_position
+
+ Description:
+    Set the position (on screen) where you want your browser to be displayed.
+
+ Input:
+    INT - x co-ordinate
+    INT - y co-ordinate
+    STRING - <optional> - window handle (default is 'current' window)
+
+ Output:
+    BOOLEAN - Success or failure
+
+ Usage:
+    $driver->set_window_position(50, 50);
+
+=cut
+
+sub set_window_position {
+    my ( $self, $x, $y, $window ) = @_;
+    $window = (defined $window)?$window:'current';
+    if (not defined $x and not defined $y){
+        return "X & Y co-ordinates are required";
+    }
+    my $res = { 'command' => 'setWindowPosition', 'window_handle' => $window };
+    my $params = { 'x' => $x, 'y' => $y };
+    my $ret = $self->_execute_command($res, $params);
+    if ($ret =~ m/204/g) {
+        return 1;
+    }
+    else { return 0; }
+}
+
+=head2 set_window_size
+
+ Description:
+    Set the size of the browser window
+
+ Input:
+    INT - height of the window
+    INT - width of the window
+    STRING - <optional> - window handle (default is 'current' window)
+ 
+ Output:
+    BOOLEAN - Success or failure
+
+ Usage:
+    $driver->set_window_size(640, 480);
+
+=cut
+
+sub set_window_size {
+    my ( $self, $height, $width, $window ) = @_;
+    $window = (defined $window)?$window:'current';
+    if (not defined $height and not defined $width){
+        return "height & width of browser are required";
+    }
+    my $res = { 'command' => 'setWindowSize', 'window_handle' => $window };
+    my $params = { 'height' => $height, 'width' => $width };
+    my $ret = $self->_execute_command($res, $params);
+    if ($ret =~ m/204/g) {
+        return 1;
+    }
+    else { return 0; }
+}
+
+=head2 get_all_cookies
+
+ Description:
+    Retrieve all cookies visible to the current page. Each cookie will be
+    returned as a HASH reference with the following keys & their value types:
+    
+    'name' - STRING
+    'value' - STRING
+    'path' - STRING
+    'domain' - STRING
+    'secure' - BOOLEAN
+
+ Output:
+    ARRAY of HASHES - list of all the cookie hashes
+
+ Usage:
+    print Dumper($driver->get_all_cookies());
+
+=cut
+
+sub get_all_cookies {
+    my ($self) = @_;
+    my $res = { 'command' => 'getAllCookies' };
+    return $self->_execute_command($res);
+}
+
+=head2 add_cookie
+
+ Description:
+    Set a cookie on the domain.
+
+ Input: 5 (1 optional)
+    Required:
+        'name' - STRING
+        'value' - STRING
+        'path' - STRING
+        'domain' - STRING
+    Optional:
+        'secure' - BOOLEAN - default is false.
+
+ Usage:
+    $driver->add_cookie('foo', 'bar', '/', '.google.com', 0)
+
+=cut
+
+sub add_cookie {
+    my ( $self, $name, $value, $path, $domain, $secure ) = @_;
+
+    if (   ( not defined $name )
+        || ( not defined $value )
+        || ( not defined $path )
+        || ( not defined $domain ) )
+    {
+        return "Missing parameters";
+    }
+
+    my $res        = { 'command' => 'addCookie' };
+    my $json_false = JSON::false;
+    my $json_true  = JSON::true;
+    $secure = ( defined $secure ) ? $json_true : $json_false;
+
+    my $params = {
+        'cookie' => {
+            'name'   => $name,
+            'value'  => $value,
+            'path'   => $path,
+            'domain' => $domain,
+            'secure' => $secure,
+        }
+    };
+
+    return $self->_execute_command( $res, $params );
+}
+
+=head2 delete_all_cookies
+
+ Description:
+    Delete all cookies visible to the current page.
+
+ Usage:
+    $driver->delete_all_cookies();
+
+=cut
+
+sub delete_all_cookies {
+    my ($self) = @_;
+    my $res = { 'command' => 'deleteAllCookies' };
+    return $self->_execute_command($res);
+}
+
+=head2 delete_cookie_named
+
+ Description:
+    Delete the cookie with the given name. This command will be a no-op if there
+    is no such cookie visible to the current page.
+
+ Input: 1
+    Required:
+        STRING - name of cookie to delete
+
+ Usage:
+    $driver->delete_cookie_named('foo');
+
+=cut
+
+sub delete_cookie_named {
+    my ( $self, $cookie_name ) = @_;
+    if ( not defined $cookie_name ) {
+        return "Cookie name not provided";
+    }
+    my $res = { 'command' => 'deleteCookieNamed', 'name' => $cookie_name };
+    return $self->_execute_command($res);
+}
+
+=head2 get_page_source
+
+ Description:
+    Get the current page source.
+
+ Output:
+    STRING - The page source.
+
+ Usage:
+    print $driver->get_page_source();
+
+=cut
+
+sub get_page_source {
+    my ($self) = @_;
+    my $res = { 'command' => 'getPageSource' };
+    return $self->_execute_command($res);
+}
+
+=head2 find_element
+
+ Description:
+    Search for an element on the page, starting from the document root. The
+    located element will be returned as a WebElement object.
+
+ Input: 2 (1 optional)
+    Required:
+        STRING - The search target.
+    Optional:
+        STRING - Locator scheme to use to search the element, available schemes:
+                 {class, class_name, css, id, link, link_text, partial_link_text,
+                  tag_name, name, xpath}
+                 Defaults to 'xpath'.
+
+ Output:
+    Selenium::Remote::WebElement - WebElement Object
+    
+ Usage:
+    $driver->find_element("//input[\@name='q']");
+
+=cut
+
+sub find_element {
+    my ( $self, $query, $method ) = @_;
+    if ( not defined $query ) {
+        return 'Search string to find element not provided.';
+    }
+    my $using = ( defined $method ) ? FINDERS->{$method} : 'xpath';
+    if (defined $using) {
+        my $res = { 'command' => 'findElement' };
+        my $params = { 'using' => $using, 'value' => $query };
+        my $ret_data = eval { $self->_execute_command( $res, $params ); };
+        if($@) {
+          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
+            # give details on what element wasn't found
+            $@ = "$1: $query,$using";
+            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
+            croak $@;
+          } else {
+            # re throw if the exception wasn't what we expected
+            die $@;
+          }
+        }
+        return new Selenium::Remote::WebElement($ret_data->{ELEMENT}, $self);
+    }
+    else {
+        croak "Bad method, expected - class, class_name, css, id, link,
+                link_text, partial_link_text, name, tag_name, xpath";
+    }
+}
+
+=head2 find_elements
+
+ Description:
+    Search for multiple elements on the page, starting from the document root.
+    The located elements will be returned as an array of WebElement object.
+
+ Input: 2 (1 optional)
+    Required:
+        STRING - The search target.
+    Optional:
+        STRING - Locator scheme to use to search the element, available schemes:
+                 {class, class_name, css, id, link, link_text, partial_link_text,
+                  tag_name, name, xpath}
+                 Defaults to 'xpath'.
+
+ Output:
+    ARRAY of Selenium::Remote::WebElement - Array of WebElement Objects
+    
+ Usage:
+    $driver->find_elements("//input");
+
+=cut
+
+sub find_elements {
+    my ( $self, $query, $method ) = @_;
+    if ( not defined $query ) {
+        return 'Search string to find element not provided.';
+    }
+    my $using = ( defined $method ) ? FINDERS->{$method} : 'xpath';
+    if (defined $using) {
+        my $res = { 'command' => 'findElements' };
+        my $params = { 'using' => $using, 'value' => $query };
+        my $ret_data = eval {$self->_execute_command( $res, $params );};
+         if($@) {
+          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
+            # give details on what element wasn't found
+            $@ = "$1: $query,$using";
+            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
+            croak $@;
+          } else {
+            # re throw if the exception wasn't what we expected
+            die $@;
+          }
+        }
+        my $elem_obj_arr;
+        my $i = 0;
+        foreach (@$ret_data) {
+            $elem_obj_arr->[$i] = new Selenium::Remote::WebElement($_->{ELEMENT}, $self);
+            $i++;
+        }
+        return wantarray?@{$elem_obj_arr}:$elem_obj_arr;
+    }
+    else {
+        croak "Bad method, expected - class, class_name, css, id, link,
+                link_text, partial_link_text, name, tag_name, xpath";
+    }
+}
+
+=head2 find_child_element
+
+ Description:
+    Search for an element on the page, starting from the identified element. The
+    located element will be returned as a WebElement object.
+
+ Input: 3 (1 optional)
+    Required:
+        Selenium::Remote::WebElement - WebElement object from where you want to
+                                       start searching.
+        STRING - The search target. (Do not use a double whack('//')
+                 in an xpath to search for a child element
+                 ex: '//option[@id="something"]'
+                 instead use a dot whack ('./')
+                 ex: './option[@id="something"]')
+    Optional:
+        STRING - Locator scheme to use to search the element, available schemes:
+                 {class, class_name, css, id, link, link_text, partial_link_text,
+                  tag_name, name, xpath}
+                 Defaults to 'xpath'.
+
+ Output:
+    Selenium::Remote::WebElement - WebElement Object
+    
+ Usage:
+    my $elem1 = $driver->find_element("//select[\@name='ned']");
+    # note the usage of ./ when searching for a child element instead of //
+    my $child = $driver->find_child_element($elem1, "./option[\@value='es_ar']");
+
+=cut
+
+sub find_child_element {
+    my ( $self, $elem, $query, $method ) = @_;
+    if ( ( not defined $elem ) || ( not defined $query ) ) {
+        return "Missing parameters";
+    }
+    my $using = ( defined $method ) ? $method : 'xpath';
+    if (exists FINDERS->{$using}) {
+        my $res = { 'command' => 'findChildElement', 'id' => $elem->{id} };
+        my $params = { 'using' => FINDERS->{$using}, 'value' => $query };
+        my $ret_data = eval {$self->_execute_command( $res, $params );};
+        if($@) {
+          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
+            # give details on what element wasn't found
+            $@ = "$1: $query,$using";
+            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
+            croak $@;
+          } else {
+            # re throw if the exception wasn't what we expected
+            die $@;
+          }
+        }
+        return new Selenium::Remote::WebElement($ret_data->{ELEMENT}, $self);
+    }
+    else {
+        croak "Bad method, expected - class, class_name, css, id, link,
+                link_text, partial_link_text, name, tag_name, xpath";
+    }
+}
+
+=head2 find_child_elements
+
+ Description:
+    Search for multiple element on the page, starting from the identified
+    element. The located elements will be returned as an array of WebElement
+    objects.
+
+ Input: 3 (1 optional)
+    Required:
+        Selenium::Remote::WebElement - WebElement object from where you want to
+                                       start searching.
+        STRING - The search target.
+    Optional:
+        STRING - Locator scheme to use to search the element, available schemes:
+                 {class, class_name, css, id, link, link_text, partial_link_text,
+                  tag_name, name, xpath}
+                 Defaults to 'xpath'.
+
+ Output:
+    ARRAY of Selenium::Remote::WebElement - Array of WebElement Objects.
+    
+ Usage:
+    my $elem1 = $driver->find_element("//select[\@name='ned']");
+    my $child = $driver->find_child_elements($elem1, "//option");
+
+=cut
+
+sub find_child_elements {
+    my ( $self, $elem, $query, $method ) = @_;
+    if ( ( not defined $elem ) || ( not defined $query ) ) {
+        return "Missing parameters";
+    }
+    my $using = ( defined $method ) ? $method : 'xpath';
+    if (exists FINDERS->{$using}) {
+        my $res = { 'command' => 'findChildElements', 'id' => $elem->{id} };
+        my $params = { 'using' => FINDERS->{$using}, 'value' => $query };
+        my $ret_data = eval {$self->_execute_command( $res, $params );};
+        if($@) {
+          if($@ =~ /(An element could not be located on the page using the given search parameters)/) {
+            # give details on what element wasn't found
+            $@ = "$1: $query,$using";
+            local @CARP_NOT = ("Selenium::Remote::Driver",@CARP_NOT);
+            croak $@;
+          } else {
+            # re throw if the exception wasn't what we expected
+            die $@;
+          }
+        }
+        my $elem_obj_arr;
+        my $i = 0;
+        foreach (@$ret_data) {
+            $elem_obj_arr->[$i] = new Selenium::Remote::WebElement($_->{ELEMENT}, $self);
+            $i++;
+        }
+        return wantarray?@{$elem_obj_arr}:$elem_obj_arr;
+    }
+    else {
+        croak "Bad method, expected - class, class_name, css, id, link,
+                link_text, partial_link_text, name, tag_name, xpath";
+    }
+}
+
+=head2 get_active_element
+
+ Description:
+    Get the element on the page that currently has focus.. The located element
+    will be returned as a WebElement object.
+
+ Output:
+    Selenium::Remote::WebElement - WebElement Object
+    
+ Usage:
+    $driver->get_active_element();
+
+=cut
+
+sub get_active_element {
+    my ($self) = @_;
+    my $res = { 'command' => 'getActiveElement' };
+    my $ret_data = eval { $self->_execute_command($res) };
+    if ($@) {
+        croak $@;
+    } else {
+        return new Selenium::Remote::WebElement($ret_data->{ELEMENT}, $self);
+    }
+}
+
+=head2 send_modifier
+
+ Description:
+    Send an event to the active element to depress or release a modifier key.
+
+  Input: 2
+    Required:
+      value - String - The modifier key event to be sent. This key must be one 'Ctrl','Shift','Alt',' or 'Command'/'Meta' as defined by the send keys command
+      isdown - Boolean/String - Whether to generate a key down or key up
+
+ Usage:
+    $driver->send_modifier('Alt','down');
+    $elem->send_keys('c');
+    $driver->send_modifier('Alt','up');
+
+    or
+
+    $driver->send_modifier('Alt',1);
+    $elem->send_keys('c');
+    $driver->send_modifier('Alt',0);
+
+=cut
+
+sub send_modifier {
+  my ($self,$modifier,$isdown) = @_;
+  if($isdown =~ /(down|up)/) {
+    $isdown = $isdown =~ /down/ ? 1:0;
+  }
+  my $res = {'command' => 'sendModifier'};
+  my $params = {value => $modifier,
+                isdown => $isdown};
+  return $self->_execute_command($res,$params);
+}
+
+=head2 compare_elements
+
+ Description:
+    Test if two element IDs refer to the same DOM element.
+
+ Input: 2
+    Required:
+        Selenium::Remote::WebElement - WebElement Object
+        Selenium::Remote::WebElement - WebElement Object
+
+ Output:
+    BOOLEAN
+    
+ Usage:
+    $driver->compare_elements($elem_obj1, $elem_obj2);
+
+=cut
+
+sub compare_elements {
+    my ($self, $elem1, $elem2) = @_;
+    my $res = { 'command' => 'elementEquals',
+                'id' => $elem1->{id},
+                'other' => $elem2->{id}
+              };
+    return $self->_execute_command($res);
+}
+
+=head2 click
+
+ Description:
+    Click any mouse button (at the coordinates set by the last moveto command).
+
+ Input:
+    button - any one of 'LEFT'/0 'MIDDLE'/1 'RIGHT'/2
+             defaults to 'LEFT'
+
+ Usage:
+    $driver->click('LEFT');
+    $driver->click(1); #MIDDLE
+    $driver->click('RIGHT');
+    $driver->click;  #Defaults to left
+
+=cut
+
+sub click {
+  my ($self,$button) = @_;
+  my $button_enum = {LEFT=>0,MIDDLE=>1,RIGHT=>2};
+  if(defined $button && $button =~ /(LEFT|MIDDLE|RIGHT)/i) {
+    $button = $button_enum->{uc $1};
+  } elsif(defined $button && $button =~ /(0|1|2)/) {
+    $button = $1;
+  } else {
+    $button = 0;
+  }
+  my $res = { 'command' => 'click' };
+  my $params = { 'button' => $button };
+  return $self->_execute_command($res,$params);
+}
+
+=head2 double_click
+
+ Description:
+    Double-clicks at the current mouse coordinates (set by moveto).
+
+ Usage:
+    $driver->double_click;
+
+=cut
+
+sub double_click {
+  my ($self) = @_;
+  my $res = { 'command' => 'doubleClick' };
+  return $self->_execute_command($res);
+}
+
+=head2 button_down
+
+ Description:
+    Click and hold the left mouse button (at the coordinates set by the
+    last moveto command). Note that the next mouse-related command that
+    should follow is buttondown . Any other mouse command (such as click
+    or another call to buttondown) will yield undefined behaviour.
+
+ Usage:
+    $self->button_down;
+
+=cut
+
+sub button_down {
+  my ($self) = @_;
+  my $res = { 'command' => 'buttonDown' };
+  return $self->_execute_command($res);
+}
+
+=head2 button_up
+
+ Description:
+    Releases the mouse button previously held (where the mouse is
+    currently at). Must be called once for every buttondown command
+    issued. See the note in click and buttondown about implications of
+    out-of-order commands.
+
+ Usage:
+    $self->button_up;
+
+=cut
+
+sub button_up {
+  my ($self) = @_;
+  my $res = { 'command' => 'buttonUp' };
+  return $self->_execute_command($res);
+}
+
+1;
+
+__END__
+
+=head1 SEE ALSO
+
+For more information about Selenium , visit the website at
+L<http://code.google.com/p/selenium/>.
+
+Also checkout project's wiki page at
+L<https://github.com/aivaturi/Selenium-Remote-Driver/wiki>.
+
+=head1 BUGS
+
+The Selenium issue tracking system is available online at
+L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+
+=head1 AUTHOR
+
+Perl Bindings for Selenium Remote Driver by Aditya Ivaturi C<< <ivaturi@gmail.com> >>
+
+=head1 ACKNOWLEDGEMENTS
+
+The following people have contributed to this module. (Thanks!)
+
+=over 4
+
+=item * Gordon Child
+
+=item * Phil Kania
+
+=item * Phil Mitchell
+
+=item * Allen Lew
+
+=item * Tom Hukins
+
+=back
+
+=head1 LICENSE
+
+Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 170 - 170
lib/Selenium/Remote/RemoteConnection.pm

@@ -1,170 +1,170 @@
-package Selenium::Remote::RemoteConnection;
-
-use strict;
-use warnings;
-
-use LWP::UserAgent;
-use HTTP::Headers;
-use HTTP::Request;
-use Net::Ping;
-use Carp qw(croak);
-use JSON;
-use Data::Dumper;
-
-use Selenium::Remote::ErrorHandler;
-
-sub new {
-    my ($class, $remote_srvr, $port) = @_;
-    
-    my $self = {
-                 remote_server_addr => $remote_srvr,
-                 port               => $port,
-                 debug              => 0,
-    };
-    bless $self, $class or die "Can't bless $class: $!";
-    my $status = eval {$self->request('GET','status');};
-    croak "Could not connect to SeleniumWebDriver" if($@);
-    if($status->{cmd_status} ne 'OK') {
-        # Could be grid, see if we can talk to it
-        $status = undef;
-        $status = $self->request('GET', 'grid/api/testsession');
-    }
-    if($status->{cmd_status} eq 'OK') {
-        return $self;
-    } else {
-        croak "Selenium server did not return proper status";
-    }
-}
-
-# This request method is tailored for Selenium RC server
-sub request {
-    my ($self, $method, $url, $params) = @_;
-    my $content = '';
-    my $fullurl = '';
-
-    # Construct full url.
-    if ($url =~ m/^http/g) {
-        $fullurl = $url;
-    }
-    elsif ($url =~ m/grid/g) {
-        $fullurl =
-            "http://"
-          . $self->{remote_server_addr} . ":"
-          . $self->{port}
-          . "/$url";
-    }
-    else {
-        $fullurl =
-            "http://"
-          . $self->{remote_server_addr} . ":"
-          . $self->{port}
-          . "/wd/hub/$url";
-    }
-
-    if ((defined $params) && $params ne '') {
-        my $json = new JSON;
-        $json->allow_blessed;
-        $content = $json->allow_nonref->utf8->encode($params);
-    }
-    
-    print "REQ: $url, $content\n" if $self->{debug};
-
-    # HTTP request
-    my $ua = LWP::UserAgent->new;
-    my $header =
-      HTTP::Headers->new(Content_Type => 'application/json; charset=utf-8');
-    $header->header('Accept' => 'application/json');
-    my $request = HTTP::Request->new($method, $fullurl, $header, $content);
-    my $response = $ua->request($request);
-
-    return $self->_process_response($response);
-}
-
-sub _process_response {
-    my ($self, $response) = @_;
-    my $data; # server response 'value' that'll be returned to the user
-    my $json = new JSON;
-
-    if ($response->is_redirect) {
-        return $self->request('GET', $response->header('location'));
-    }
-    else {
-        my $decoded_json = undef;
-        print "RES: ".$response->decoded_content."\n\n" if $self->{debug};
-        if (($response->message ne 'No Content') && ($response->content ne '')) {
-	    if ($response->content =~ m/^<html>/i) {
-		$data->{'cmd_return'} = 'Server returned error message '.$response->content.' instead of data';
-		return $data;
-	    }
-            $decoded_json = $json->allow_nonref(1)->utf8(1)->decode($response->content);
-            $data->{'sessionId'} = $decoded_json->{'sessionId'};
-        }
-        
-        if ($response->is_error) {
-            my $error_handler = new Selenium::Remote::ErrorHandler;
-            $data->{'cmd_status'} = 'NOTOK';
-            if (defined $decoded_json) {
-                $data->{'cmd_return'} = $error_handler->process_error($decoded_json);
-            }
-            else {
-                $data->{'cmd_return'} = 'Server returned error code '.$response->code.' and no data';          
-            }
-            return $data;
-        }
-        elsif ($response->is_success) {
-            $data->{'cmd_status'} = 'OK';
-            if (defined $decoded_json) {
-                $data->{'cmd_return'} = $decoded_json->{'value'};
-            }
-            else {
-                $data->{'cmd_return'} = 'Server returned status code '.$response->code.' but no data';          
-            }
-            return $data;
-        }
-        else {
-            # No idea what the server is telling me, must be high
-            $data->{'cmd_status'} = 'NOTOK';
-            $data->{'cmd_return'} = 'Server returned status code '.$response->code.' which I don\'t understand';
-            return $data;
-        }
-    }
-}
-
-
-1;
-
-__END__
-
-=head1 SEE ALSO
-
-For more information about Selenium , visit the website at
-L<http://code.google.com/p/selenium/>.
-
-=head1 BUGS
-
-The Selenium issue tracking system is available online at
-L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
-
-=head1 CURRENT MAINTAINER
-
-Gordon Child C<< <gchild@gordonchild.com> >>
-
-=head1 AUTHOR
-
-Perl Bindings for Selenium Remote Driver by Aditya Ivaturi C<< <ivaturi@gmail.com> >>
-
-=head1 LICENSE
-
-Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-     http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+package Selenium::Remote::RemoteConnection;
+
+use strict;
+use warnings;
+
+use LWP::UserAgent;
+use HTTP::Headers;
+use HTTP::Request;
+use Net::Ping;
+use Carp qw(croak);
+use JSON;
+use Data::Dumper;
+
+use Selenium::Remote::ErrorHandler;
+
+sub new {
+    my ($class, $remote_srvr, $port) = @_;
+    
+    my $self = {
+                 remote_server_addr => $remote_srvr,
+                 port               => $port,
+                 debug              => 0,
+    };
+    bless $self, $class or die "Can't bless $class: $!";
+    my $status = eval {$self->request('GET','status');};
+    croak "Could not connect to SeleniumWebDriver" if($@);
+    if($status->{cmd_status} ne 'OK') {
+        # Could be grid, see if we can talk to it
+        $status = undef;
+        $status = $self->request('GET', 'grid/api/testsession');
+    }
+    if($status->{cmd_status} eq 'OK') {
+        return $self;
+    } else {
+        croak "Selenium server did not return proper status";
+    }
+}
+
+# This request method is tailored for Selenium RC server
+sub request {
+    my ($self, $method, $url, $params) = @_;
+    my $content = '';
+    my $fullurl = '';
+
+    # Construct full url.
+    if ($url =~ m/^http/g) {
+        $fullurl = $url;
+    }
+    elsif ($url =~ m/grid/g) {
+        $fullurl =
+            "http://"
+          . $self->{remote_server_addr} . ":"
+          . $self->{port}
+          . "/$url";
+    }
+    else {
+        $fullurl =
+            "http://"
+          . $self->{remote_server_addr} . ":"
+          . $self->{port}
+          . "/wd/hub/$url";
+    }
+
+    if ((defined $params) && $params ne '') {
+        my $json = new JSON;
+        $json->allow_blessed;
+        $content = $json->allow_nonref->utf8->encode($params);
+    }
+    
+    print "REQ: $url, $content\n" if $self->{debug};
+
+    # HTTP request
+    my $ua = LWP::UserAgent->new;
+    my $header =
+      HTTP::Headers->new(Content_Type => 'application/json; charset=utf-8');
+    $header->header('Accept' => 'application/json');
+    my $request = HTTP::Request->new($method, $fullurl, $header, $content);
+    my $response = $ua->request($request);
+
+    return $self->_process_response($response);
+}
+
+sub _process_response {
+    my ($self, $response) = @_;
+    my $data; # server response 'value' that'll be returned to the user
+    my $json = new JSON;
+
+    if ($response->is_redirect) {
+        return $self->request('GET', $response->header('location'));
+    }
+    else {
+        my $decoded_json = undef;
+        print "RES: ".$response->decoded_content."\n\n" if $self->{debug};
+        if (($response->message ne 'No Content') && ($response->content ne '')) {
+	    if ($response->content =~ m/^<html>/i) {
+		$data->{'cmd_return'} = 'Server returned error message '.$response->content.' instead of data';
+		return $data;
+	    }
+            $decoded_json = $json->allow_nonref(1)->utf8(1)->decode($response->content);
+            $data->{'sessionId'} = $decoded_json->{'sessionId'};
+        }
+        
+        if ($response->is_error) {
+            my $error_handler = new Selenium::Remote::ErrorHandler;
+            $data->{'cmd_status'} = 'NOTOK';
+            if (defined $decoded_json) {
+                $data->{'cmd_return'} = $error_handler->process_error($decoded_json);
+            }
+            else {
+                $data->{'cmd_return'} = 'Server returned error code '.$response->code.' and no data';          
+            }
+            return $data;
+        }
+        elsif ($response->is_success) {
+            $data->{'cmd_status'} = 'OK';
+            if (defined $decoded_json) {
+                $data->{'cmd_return'} = $decoded_json->{'value'};
+            }
+            else {
+                $data->{'cmd_return'} = 'Server returned status code '.$response->code.' but no data';          
+            }
+            return $data;
+        }
+        else {
+            # No idea what the server is telling me, must be high
+            $data->{'cmd_status'} = 'NOTOK';
+            $data->{'cmd_return'} = 'Server returned status code '.$response->code.' which I don\'t understand';
+            return $data;
+        }
+    }
+}
+
+
+1;
+
+__END__
+
+=head1 SEE ALSO
+
+For more information about Selenium , visit the website at
+L<http://code.google.com/p/selenium/>.
+
+=head1 BUGS
+
+The Selenium issue tracking system is available online at
+L<http://github.com/aivaturi/Selenium-Remote-Driver/issues>.
+
+=head1 CURRENT MAINTAINER
+
+Gordon Child C<< <gchild@gordonchild.com> >>
+
+=head1 AUTHOR
+
+Perl Bindings for Selenium Remote Driver by Aditya Ivaturi C<< <ivaturi@gmail.com> >>
+
+=head1 LICENSE
+
+Copyright (c) 2010-2011 Aditya Ivaturi, Gordon Child
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.

+ 68 - 68
lib/Selenium/Remote/WDKeys.pm

@@ -1,4 +1,4 @@
-package Selenium::Remote::WDKeys;
+package Selenium::Remote::WDKeys;
 
 =head1 NAME
 
@@ -12,71 +12,71 @@ The constant KEYS is defined here.
 
 =cut
 
-
-use strict;
-use warnings;
-
-use base 'Exporter';
-
-# http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
-use constant KEYS => {
-    'null'	 => "\N{U+E000}",
-    'cancel'	 => "\N{U+E001}",
-    'help'	 => "\N{U+E002}",
-    'backspace'	 => "\N{U+E003}",
-    'tab'	 => "\N{U+E004}",
-    'clear'	 => "\N{U+E005}",
-    'return'	 => "\N{U+E006}",
-    'enter'	 => "\N{U+E007}",
-    'shift'	 => "\N{U+E008}",
-    'control'	 => "\N{U+E009}",
-    'alt'	 => "\N{U+E00A}",
-    'pause'	 => "\N{U+E00B}",
-    'escape'	 => "\N{U+E00C}",
-    'space'	 => "\N{U+E00D}",
-    'page_up'	 => "\N{U+E00E}",
-    'page_down'	 => "\N{U+E00f}",
-    'end'	 => "\N{U+E010}",
-    'home'	 => "\N{U+E011}",
-    'left_arrow'	 => "\N{U+E012}",
-    'up_arrow'	 => "\N{U+E013}",
-    'right_arrow'	 => "\N{U+E014}",
-    'down_arrow'	 => "\N{U+E015}",
-    'insert'	 => "\N{U+E016}",
-    'delete'	 => "\N{U+E017}",
-    'semicolon'	 => "\N{U+E018}",
-    'equals'	 => "\N{U+E019}",
-    'numpad_0'	 => "\N{U+E01A}",
-    'numpad_1'	 => "\N{U+E01B}",
-    'numpad_2'	 => "\N{U+E01C}",
-    'numpad_3'	 => "\N{U+E01D}",
-    'numpad_4'	 => "\N{U+E01E}",
-    'numpad_5'	 => "\N{U+E01f}",
-    'numpad_6'	 => "\N{U+E020}",
-    'numpad_7'	 => "\N{U+E021}",
-    'numpad_8'	 => "\N{U+E022}",
-    'numpad_9'	 => "\N{U+E023}",
-    'multiply'	 => "\N{U+E024}",
-    'add'	 => "\N{U+E025}",
-    'separator'	 => "\N{U+E026}",
-    'subtract'	 => "\N{U+E027}",
-    'decimal'	 => "\N{U+E028}",
-    'divide'	 => "\N{U+E029}",
-    'f1'	 => "\N{U+E031}",
-    'f2'	 => "\N{U+E032}",
-    'f3'	 => "\N{U+E033}",
-    'f4'	 => "\N{U+E034}",
-    'f5'	 => "\N{U+E035}",
-    'f6'	 => "\N{U+E036}",
-    'f7'	 => "\N{U+E037}",
-    'f8'	 => "\N{U+E038}",
-    'f9'	 => "\N{U+E039}",
-    'f10'	 => "\N{U+E03A}",
-    'f11'	 => "\N{U+E03B}",
-    'f12'	 => "\N{U+E03C}",
-    'command_meta'  => "\N{U+E03D}",
-};
-
-our @EXPORT = ('KEYS');
-
+
+use strict;
+use warnings;
+
+use base 'Exporter';
+
+# http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
+use constant KEYS => {
+    'null'	 => "\N{U+E000}",
+    'cancel'	 => "\N{U+E001}",
+    'help'	 => "\N{U+E002}",
+    'backspace'	 => "\N{U+E003}",
+    'tab'	 => "\N{U+E004}",
+    'clear'	 => "\N{U+E005}",
+    'return'	 => "\N{U+E006}",
+    'enter'	 => "\N{U+E007}",
+    'shift'	 => "\N{U+E008}",
+    'control'	 => "\N{U+E009}",
+    'alt'	 => "\N{U+E00A}",
+    'pause'	 => "\N{U+E00B}",
+    'escape'	 => "\N{U+E00C}",
+    'space'	 => "\N{U+E00D}",
+    'page_up'	 => "\N{U+E00E}",
+    'page_down'	 => "\N{U+E00f}",
+    'end'	 => "\N{U+E010}",
+    'home'	 => "\N{U+E011}",
+    'left_arrow'	 => "\N{U+E012}",
+    'up_arrow'	 => "\N{U+E013}",
+    'right_arrow'	 => "\N{U+E014}",
+    'down_arrow'	 => "\N{U+E015}",
+    'insert'	 => "\N{U+E016}",
+    'delete'	 => "\N{U+E017}",
+    'semicolon'	 => "\N{U+E018}",
+    'equals'	 => "\N{U+E019}",
+    'numpad_0'	 => "\N{U+E01A}",
+    'numpad_1'	 => "\N{U+E01B}",
+    'numpad_2'	 => "\N{U+E01C}",
+    'numpad_3'	 => "\N{U+E01D}",
+    'numpad_4'	 => "\N{U+E01E}",
+    'numpad_5'	 => "\N{U+E01f}",
+    'numpad_6'	 => "\N{U+E020}",
+    'numpad_7'	 => "\N{U+E021}",
+    'numpad_8'	 => "\N{U+E022}",
+    'numpad_9'	 => "\N{U+E023}",
+    'multiply'	 => "\N{U+E024}",
+    'add'	 => "\N{U+E025}",
+    'separator'	 => "\N{U+E026}",
+    'subtract'	 => "\N{U+E027}",
+    'decimal'	 => "\N{U+E028}",
+    'divide'	 => "\N{U+E029}",
+    'f1'	 => "\N{U+E031}",
+    'f2'	 => "\N{U+E032}",
+    'f3'	 => "\N{U+E033}",
+    'f4'	 => "\N{U+E034}",
+    'f5'	 => "\N{U+E035}",
+    'f6'	 => "\N{U+E036}",
+    'f7'	 => "\N{U+E037}",
+    'f8'	 => "\N{U+E038}",
+    'f9'	 => "\N{U+E039}",
+    'f10'	 => "\N{U+E03A}",
+    'f11'	 => "\N{U+E03B}",
+    'f12'	 => "\N{U+E03C}",
+    'command_meta'  => "\N{U+E03D}",
+};
+
+our @EXPORT = ('KEYS');
+
 1;

+ 15 - 15
t/www/cookies.html

@@ -1,16 +1,16 @@
-<html>
-<head>
-    <title>Testing cookies</title>
-
-    <script type="text/javascript">
-        function setCookie(NameOfCookie, value, expiredays) {
-		var ExpireDate = new Date ();
-		ExpireDate.setTime(ExpireDate.getTime() + (expiredays * 24 * 3600 * 1000));
-
-		  document.cookie = NameOfCookie + "=" + escape(value) + 
-		  ((expiredays == null) ? "" : "; expires=" + ExpireDate.toGMTString());
-		}
-    </script>
-</head>
-<body onload="setCookie('foo1', 'bar1', 365); setCookie('foo2', 'bar2', 365);" />
+<html>
+<head>
+    <title>Testing cookies</title>
+
+    <script type="text/javascript">
+        function setCookie(NameOfCookie, value, expiredays) {
+		var ExpireDate = new Date ();
+		ExpireDate.setTime(ExpireDate.getTime() + (expiredays * 24 * 3600 * 1000));
+
+		  document.cookie = NameOfCookie + "=" + escape(value) + 
+		  ((expiredays == null) ? "" : "; expires=" + ExpireDate.toGMTString());
+		}
+    </script>
+</head>
+<body onload="setCookie('foo1', 'bar1', 365); setCookie('foo2', 'bar2', 365);" />
 </html>

+ 98 - 98
t/www/formPage.html

@@ -1,98 +1,98 @@
-<html>
-<head>
-    <title>We Leave From Here</title>
-
-    <script type="text/javascript">
-        function changePage() {
-            var newLocation = '/common/page/3';
-            window.location = newLocation;
-        }
-    </script>
-</head>
-<body>
-<a href="/index.html">Home Page Link</a><br/><br/>
-
-There should be a form here:
-
-<form method="get" action="resultPage.html" name="login">
-    <input type="submit" id="submitButton" value="Hello there"/>
-</form>
-
-<form method="get" action="resultPage.html" name="image">
-    <input type="image" id="imageButton" alt="click me!" src="images/button.gif"/>
-</form>
-
-<form method="get" action="resultPage.html" name="optional" style="display: block">
-    Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
-    <select name="selectomatic">
-        <option selected="selected">One</option>
-        <option>Two</option>
-        <option>Four</option>
-        <option>Still learning how to count, apparently</option>
-    </select>
-
-    <select name="multi" id="multi" multiple="multiple">
-        <option selected="selected">Eggs</option>
-        <option>Ham</option>
-        <option selected="selected">Sausages</option>
-        <option>Onion gravy</option>
-    </select>
-
-    <select name="no-select" disabled="disabled">
-      <option value="foo">Foo</option>
-    </select>
-
-    <br/>
-
-    <input type="radio" id="cheese" name="snack" value="cheese"/>Cheese<br/>
-    <input type="radio" id="peas" name="snack" value="peas"/>Peas<br/>
-    <input type="radio" id="cheese_and_peas" name="snack" value="cheese and peas" checked/>Cheese and peas<br/>
-    <input type="radio" id="nothing" name="snack" value="nowt" disabled="disabled"/>Not a sausage
-
-    <input type="hidden" name="hidden" value="fromage" />
-
-    <p id="cheeseLiker">I like cheese</p>
-    <input type="submit" value="Click!"/>
-</form>
-
-<form method="get" action="resultPage.html" name="disable">
-    <input type="text" id="working"/>
-    <input type="text" id="notWorking" disabled="true"/>
-
-    <textarea id="notWorkingArea" disabled="disabled" cols="5" rows="5"></textarea>
-
-    <textarea id="withText" rows="5" cols="5">Example text</textarea>
-    <textarea id="emptyTextArea" rows="5" cols="5"></textarea>
-</form>
-
-<form method="post" action="resultPage.html">
-    <select id="redirect" onchange="javascript:changePage();">
-        <option selected="selected">One</option>
-        <option id="changeme">Two</option>
-    </select>
-
-    <input id="no-type" />
-    <input type="file" id="upload" onchange="document.getElementById('fileResults').innerHTML = 'changed';" />
-    <span id="fileResults"></span>
-</form>
-
-<form method="get" action="resultPage.html">
-  <input type="text" value="name" name="id-name1"/>
-  <input type="text" value="id"   id="id-name1"/>
-
-  <!-- Reverse the ordering -->
-  <input type="text" value="id"   id="id-name2"/>
-  <input type="text" value="name" name="id-name2"/>
-  <input name="readonly" readonly="readonly" />
-</form>
-
-<!-- form with nested children -->
-<form method="get" action="resultPage.html" id="nested_form">
-  <div>
-    <input type="text" value="name" name="x"/>
-  </div>
-  <input type="submit" />
-</form>  
-
-</body>
-</html>
+<html>
+<head>
+    <title>We Leave From Here</title>
+
+    <script type="text/javascript">
+        function changePage() {
+            var newLocation = '/common/page/3';
+            window.location = newLocation;
+        }
+    </script>
+</head>
+<body>
+<a href="/index.html">Home Page Link</a><br/><br/>
+
+There should be a form here:
+
+<form method="get" action="resultPage.html" name="login">
+    <input type="submit" id="submitButton" value="Hello there"/>
+</form>
+
+<form method="get" action="resultPage.html" name="image">
+    <input type="image" id="imageButton" alt="click me!" src="images/button.gif"/>
+</form>
+
+<form method="get" action="resultPage.html" name="optional" style="display: block">
+    Here's a checkbox: <input type="checkbox" id="checky" name="checky" value="furrfu"/><br/>
+    <select name="selectomatic">
+        <option selected="selected">One</option>
+        <option>Two</option>
+        <option>Four</option>
+        <option>Still learning how to count, apparently</option>
+    </select>
+
+    <select name="multi" id="multi" multiple="multiple">
+        <option selected="selected">Eggs</option>
+        <option>Ham</option>
+        <option selected="selected">Sausages</option>
+        <option>Onion gravy</option>
+    </select>
+
+    <select name="no-select" disabled="disabled">
+      <option value="foo">Foo</option>
+    </select>
+
+    <br/>
+
+    <input type="radio" id="cheese" name="snack" value="cheese"/>Cheese<br/>
+    <input type="radio" id="peas" name="snack" value="peas"/>Peas<br/>
+    <input type="radio" id="cheese_and_peas" name="snack" value="cheese and peas" checked/>Cheese and peas<br/>
+    <input type="radio" id="nothing" name="snack" value="nowt" disabled="disabled"/>Not a sausage
+
+    <input type="hidden" name="hidden" value="fromage" />
+
+    <p id="cheeseLiker">I like cheese</p>
+    <input type="submit" value="Click!"/>
+</form>
+
+<form method="get" action="resultPage.html" name="disable">
+    <input type="text" id="working"/>
+    <input type="text" id="notWorking" disabled="true"/>
+
+    <textarea id="notWorkingArea" disabled="disabled" cols="5" rows="5"></textarea>
+
+    <textarea id="withText" rows="5" cols="5">Example text</textarea>
+    <textarea id="emptyTextArea" rows="5" cols="5"></textarea>
+</form>
+
+<form method="post" action="resultPage.html">
+    <select id="redirect" onchange="javascript:changePage();">
+        <option selected="selected">One</option>
+        <option id="changeme">Two</option>
+    </select>
+
+    <input id="no-type" />
+    <input type="file" id="upload" onchange="document.getElementById('fileResults').innerHTML = 'changed';" />
+    <span id="fileResults"></span>
+</form>
+
+<form method="get" action="resultPage.html">
+  <input type="text" value="name" name="id-name1"/>
+  <input type="text" value="id"   id="id-name1"/>
+
+  <!-- Reverse the ordering -->
+  <input type="text" value="id"   id="id-name2"/>
+  <input type="text" value="name" name="id-name2"/>
+  <input name="readonly" readonly="readonly" />
+</form>
+
+<!-- form with nested children -->
+<form method="get" action="resultPage.html" id="nested_form">
+  <div>
+    <input type="text" value="name" name="x"/>
+  </div>
+  <input type="submit" />
+</form>  
+
+</body>
+</html>

+ 253 - 253
t/www/javascriptPage.html

@@ -1,253 +1,253 @@
-<?xml version="1.0"?>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-    <title>Testing Javascript</title>
-    <script type="text/javascript">
-        var seen = {};
-
-        function updateContent(input) {
-            document.getElementById('result').innerHTML = "<p>" + input.value + "</p>";
-        }
-
-        function displayMessage(message) {
-            document.getElementById('result').innerHTML = "<p>" + message + "</p>";
-        }
-
-        function appendMessage(message) {
-            document.getElementById('result').innerHTML += message + " ";
-        }
-
-        function register(message) {
-          if (!seen[message]) {
-            appendMessage(message);
-            seen[message] = true;
-          }
-        }
-
-        function delayedShowHide(delay, show) {
-          var blackBox = document.getElementById('clickToHide');
-          window.setTimeout(function() {
-            blackBox.style.display = show ? '' : 'none';
-          }, delay);
-        }
-    </script>
-    <script type="text/javascript">
-        var startList = function() {
-            // Ugh. Let's hope no-one is faking their user agent when running the tests
-            if (navigator.userAgent.indexOf("MSIE") != -1) {
-                var navRoot = document.getElementById("nav");
-                for (var i = 0; i < navRoot.childNodes.length; i++) {
-                    var node = navRoot.childNodes[i];
-                    if (node.nodeName == "LI") {
-                        node.onmouseover = function() {
-                            this.className += " over";
-                        };
-                        node.onmouseout = function() {
-                            this.className = this.className.replace(" over", "");
-                        };
-                    }
-                }
-            }
-        };
-        window.onload=startList;
-    </script>
-    <style type="text/css">
-        #nav {
-            padding: 0; margin: 0; list-style: none;
-        }
-        #nav li {
-            float: left; position: relative; width: 10em;
-        }
-        #nav li ul {
-            display: none; position: absolute; top: 1em; left: 0;
-        }
-        #nav li > ul { top: auto; left: auto; }
-        #nav li:hover ul, #nav li.over ul{ display: block; background: white; }
-    </style>
-</head>
-<body>
-<h1>Type Stuff</h1>
-
-<div>
-    <ul id="nav">
-        <li id="menu1">Menu 1
-            <ul>
-                <li id="item1" onclick="displayMessage('item 1');">Item 1</li>
-                <li>Item 2</li>
-            </ul>
-        </li>
-    </ul>
-</div>
-
-<div id="result">
-	&nbsp;
-</div>
-                                                                                         
-<div id="formageddon">
-    <form action="#">
-        Key Up: <input type="text" id="keyUp" onkeyup="javascript:updateContent(this)"/><br/>
-        Key Down: <input type="text" id="keyDown" onkeydown="javascript:updateContent(this)"/><br/>
-        Key Press: <input type="text" id="keyPress" onkeypress="javascript:updateContent(this)"/><br/>
-        Change: <input type="text" id="change" onkeypress="javascript:displayMessage('change')"/><br/>
-        <textarea id="keyDownArea" onkeydown="javascript:updateContent(this)" rows="2" cols="15"></textarea>
-        <textarea id="keyPressArea" onkeypress="javascript:updateContent(this)" rows="2" cols="15"></textarea>
-        <textarea id="keyUpArea" onkeyup="javascript:updateContent(this)" rows="2" cols="15"></textarea>
-        <select id="selector" onchange="javascript:updateContent(this)">
-            <option value="foo">Foo</option>
-            <option value="bar">Bar</option>
-        </select>
-        <input type="checkbox" id="checkbox" value="checkbox thing" onchange="javascript:updateContent(this)"/>
-        <input id="clickField" type="text" onclick="document.getElementById('clickField').value='Clicked';" value="Hello"/>
-        <input id="clearMe" value="Something" onchange="displayMessage('Cleared')"/>
-    </form>
-</div>
-
-<div>
-    <p><a href="#" onclick="javascript:document.title='Changed'">Change the page title!</a></p>
-
-    <p><a onclick="javascript:document.title='Changed'" id="nohref">No href</a></p>
-
-    <p><a id="updatediv" href="#" onclick="javascript:document.getElementById('dynamo').innerHTML = 'Fish and chips!';">Update a
-        div</a></p>
-</div>
-
-<div id="dynamo">What's for dinner?</div>
-
-<div id="mousedown" onmousedown="javascript:displayMessage('mouse down');">
-    <p>Click for the mouse down event</p>
-    <span><p id="child">Here's some text</p></span>
-</div>
-
-<div id="mouseup" onmouseup="javascript:displayMessage('mouse up');">
-    <p>Click for the mouse up event</p>
-</div>
-
-<div id="mouseclick" onclick="javascript:displayMessage('mouse click');">
-    <p>Click for the mouse click event</p>
-</div>
-
-<div id="error" onclick="document.getElementById('doesnotexist').innerHTML = 'cheese';">
-    Clicking this causes a JS exception in the click handler
-</div>
-
-<div>
-  <form action="resultPage.html" id="on-form">
-    <input id="theworks"
-           onfocus="appendMessage('focus')"
-           onkeydown="appendMessage('keydown')"
-           onkeypress="appendMessage('keypress')"
-           onkeyup="appendMessage('keyup')"
-           onblur="appendMessage('blur')"
-           onchange="appendMessage('change')"
-           />
-
-    <input id="changeable" name="changeable" onfocus="appendMessage('focus')" onchange="appendMessage('change')" onblur="appendMessage('blur')"/>
-           
-    <button type="button" id="plainButton" 
-    		onfocus="appendMessage('focus')"
-           	onkeydown="appendMessage('keydown')"
-           	onkeypress="appendMessage('keypress')"
-           	onkeyup="appendMessage('keyup')"
-           	onblur="appendMessage('blur')"
-           	onclick="appendMessage('click')"
-           	onmousedown="appendMessage('mousedown ')" 
-           	onmouseup="appendMessage('mouseup ')"
-            onmouseover="register('mouseover ')"
-            onmousemove="register('mousemove ')"
-    >
-        <b>Go somewhere</b>
-    </button>
-    <button type="submit" id="submittingButton"><emph>submit</emph></button>
-    <button type="button" id="jsSubmitButton" onclick="javascript:document.getElementById('on-form').submit();">Submitomatic</button>
-
-    <button type="button" id="switchFocus" onclick="document.getElementById('theworks').focus();">Switch focus</button>
-    <button type="button" onclick="var element = document.getElementById('switchFocus'); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, null, 0, 0, 0, 0, 0,false, false, false, false, 0, element);element.dispatchEvent(clickEvent);">Do magic</button>
-  </form>
-  
-  <form action="javascriptPage.html" id="submitListeningForm" onsubmit="appendMessage('form-onsubmit '); return false;">
-    <p>
-      <input id="submitListeningForm-text" type="text" onsubmit="appendMessage('text-onsubmit ')" onclick="appendMessage('text-onclick ');" />
-      <input id="submitListeningForm-submit" type="submit" onsubmit="appendMessage('submit-onsubmit ')" onclick="appendMessage('submit-onclick ');" />
-    </p>
-  </form>
-</div>
-
-<p id="suppressedParagraph" style="display: none">A paragraph suppressed using CSS display=none</p>
-
-<div>
-    <p id="displayed">Displayed</p>
-
-    <form action="#"><input type="hidden" name="hidden" /> </form>
-
-    <p id="none" style="display: none;">Display set to none</p>
-
-    <p id="hidden" style="visibility: hidden;">Hidden</p>
-
-    <div id="hiddenparent" style="height: 2em; display: none;">
-      <div id="hiddenchild">
-        <a href="#" id="hiddenlink">ok</a>
-      </div>
-    </div>
-    
-    <div style="visibility: hidden;">
-      <span>
-        <input id="unclickable" />
-        <input type="checkbox" id="untogglable" checked="checked" />Check box you can't see
-      </span>
-    </div>
-
-    <p id="outer" style="visibility: hidden">A <b id="visibleSubElement" style="visibility: visible">sub-element that is explicitly visible</b> using CSS visibility=visible</p>
-</div>
-
-<div>
-    <form>
-        <input type="text" id="keyReporter" size="80"
-               onkeyup="appendMessage('up: ' + event.keyCode)"
-               onkeypress="appendMessage('press: ' + event.keyCode)"
-               onkeydown="displayMessage(''); appendMessage('down: ' + event.keyCode)" />
-        <input name="suppress" onkeydown="if (event.preventDefault) event.preventDefault(); event.returnValue = false; return false;" onkeypress="appendMessage('press');"/>
-    </form>
-</div>
-
-<!-- Used for testing styles -->
-<div style="background-color: green;" id="green-parent">
-  <p id="style1">This should be greenish</p>
-    <ul>
-      <li id="green-item">So should this</li>
-      <li id="red-item" style="background-color: red;">But this is red</li>
-    </ul>
-</div>
-
-<p id="zeroheight" style="height:0px;">This is a foo</p>
-
-<p id="zerowidth" style="width:0;">This is a bar</p>
-
-<a href="#" id="close" onclick="window.close();">Close window</a>
-
-<div id="delete" onclick="var d = document.getElementById('deleted'); this.removeChild(d);">
-    <p id="deleted">I should be deleted when you click my containing div</p>
-    <p>Whereas, I should not</p>
-</div>
-
-<div>
-    <span id="hideMe" onclick="this.style.display = 'none';">Click to hide me.</span>
-</div>
-
-<div style="margin-top: 10px;">
-  Click actions delayed by 3000ms:
-  <div id="clickToShow" onclick="delayedShowHide(3000, true);"
-       style="float: left;width: 100px;height:100px;border: 1px solid black;">
-    Click to show black box
-  </div>
-  <div id="clickToHide" onclick="delayedShowHide(3000, false);"
-       style="float: left;width: 100px;height:100px;border: 1px solid black;
-              background-color: black; color: white; display: none;">
-    Click to hide black box
-  </div>
-  <div style="clear: both"></div>
-</div>
-
-</body>
-</html>
-
-
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+    <title>Testing Javascript</title>
+    <script type="text/javascript">
+        var seen = {};
+
+        function updateContent(input) {
+            document.getElementById('result').innerHTML = "<p>" + input.value + "</p>";
+        }
+
+        function displayMessage(message) {
+            document.getElementById('result').innerHTML = "<p>" + message + "</p>";
+        }
+
+        function appendMessage(message) {
+            document.getElementById('result').innerHTML += message + " ";
+        }
+
+        function register(message) {
+          if (!seen[message]) {
+            appendMessage(message);
+            seen[message] = true;
+          }
+        }
+
+        function delayedShowHide(delay, show) {
+          var blackBox = document.getElementById('clickToHide');
+          window.setTimeout(function() {
+            blackBox.style.display = show ? '' : 'none';
+          }, delay);
+        }
+    </script>
+    <script type="text/javascript">
+        var startList = function() {
+            // Ugh. Let's hope no-one is faking their user agent when running the tests
+            if (navigator.userAgent.indexOf("MSIE") != -1) {
+                var navRoot = document.getElementById("nav");
+                for (var i = 0; i < navRoot.childNodes.length; i++) {
+                    var node = navRoot.childNodes[i];
+                    if (node.nodeName == "LI") {
+                        node.onmouseover = function() {
+                            this.className += " over";
+                        };
+                        node.onmouseout = function() {
+                            this.className = this.className.replace(" over", "");
+                        };
+                    }
+                }
+            }
+        };
+        window.onload=startList;
+    </script>
+    <style type="text/css">
+        #nav {
+            padding: 0; margin: 0; list-style: none;
+        }
+        #nav li {
+            float: left; position: relative; width: 10em;
+        }
+        #nav li ul {
+            display: none; position: absolute; top: 1em; left: 0;
+        }
+        #nav li > ul { top: auto; left: auto; }
+        #nav li:hover ul, #nav li.over ul{ display: block; background: white; }
+    </style>
+</head>
+<body>
+<h1>Type Stuff</h1>
+
+<div>
+    <ul id="nav">
+        <li id="menu1">Menu 1
+            <ul>
+                <li id="item1" onclick="displayMessage('item 1');">Item 1</li>
+                <li>Item 2</li>
+            </ul>
+        </li>
+    </ul>
+</div>
+
+<div id="result">
+	&nbsp;
+</div>
+                                                                                         
+<div id="formageddon">
+    <form action="#">
+        Key Up: <input type="text" id="keyUp" onkeyup="javascript:updateContent(this)"/><br/>
+        Key Down: <input type="text" id="keyDown" onkeydown="javascript:updateContent(this)"/><br/>
+        Key Press: <input type="text" id="keyPress" onkeypress="javascript:updateContent(this)"/><br/>
+        Change: <input type="text" id="change" onkeypress="javascript:displayMessage('change')"/><br/>
+        <textarea id="keyDownArea" onkeydown="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+        <textarea id="keyPressArea" onkeypress="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+        <textarea id="keyUpArea" onkeyup="javascript:updateContent(this)" rows="2" cols="15"></textarea>
+        <select id="selector" onchange="javascript:updateContent(this)">
+            <option value="foo">Foo</option>
+            <option value="bar">Bar</option>
+        </select>
+        <input type="checkbox" id="checkbox" value="checkbox thing" onchange="javascript:updateContent(this)"/>
+        <input id="clickField" type="text" onclick="document.getElementById('clickField').value='Clicked';" value="Hello"/>
+        <input id="clearMe" value="Something" onchange="displayMessage('Cleared')"/>
+    </form>
+</div>
+
+<div>
+    <p><a href="#" onclick="javascript:document.title='Changed'">Change the page title!</a></p>
+
+    <p><a onclick="javascript:document.title='Changed'" id="nohref">No href</a></p>
+
+    <p><a id="updatediv" href="#" onclick="javascript:document.getElementById('dynamo').innerHTML = 'Fish and chips!';">Update a
+        div</a></p>
+</div>
+
+<div id="dynamo">What's for dinner?</div>
+
+<div id="mousedown" onmousedown="javascript:displayMessage('mouse down');">
+    <p>Click for the mouse down event</p>
+    <span><p id="child">Here's some text</p></span>
+</div>
+
+<div id="mouseup" onmouseup="javascript:displayMessage('mouse up');">
+    <p>Click for the mouse up event</p>
+</div>
+
+<div id="mouseclick" onclick="javascript:displayMessage('mouse click');">
+    <p>Click for the mouse click event</p>
+</div>
+
+<div id="error" onclick="document.getElementById('doesnotexist').innerHTML = 'cheese';">
+    Clicking this causes a JS exception in the click handler
+</div>
+
+<div>
+  <form action="resultPage.html" id="on-form">
+    <input id="theworks"
+           onfocus="appendMessage('focus')"
+           onkeydown="appendMessage('keydown')"
+           onkeypress="appendMessage('keypress')"
+           onkeyup="appendMessage('keyup')"
+           onblur="appendMessage('blur')"
+           onchange="appendMessage('change')"
+           />
+
+    <input id="changeable" name="changeable" onfocus="appendMessage('focus')" onchange="appendMessage('change')" onblur="appendMessage('blur')"/>
+           
+    <button type="button" id="plainButton" 
+    		onfocus="appendMessage('focus')"
+           	onkeydown="appendMessage('keydown')"
+           	onkeypress="appendMessage('keypress')"
+           	onkeyup="appendMessage('keyup')"
+           	onblur="appendMessage('blur')"
+           	onclick="appendMessage('click')"
+           	onmousedown="appendMessage('mousedown ')" 
+           	onmouseup="appendMessage('mouseup ')"
+            onmouseover="register('mouseover ')"
+            onmousemove="register('mousemove ')"
+    >
+        <b>Go somewhere</b>
+    </button>
+    <button type="submit" id="submittingButton"><emph>submit</emph></button>
+    <button type="button" id="jsSubmitButton" onclick="javascript:document.getElementById('on-form').submit();">Submitomatic</button>
+
+    <button type="button" id="switchFocus" onclick="document.getElementById('theworks').focus();">Switch focus</button>
+    <button type="button" onclick="var element = document.getElementById('switchFocus'); var clickEvent = document.createEvent('MouseEvents'); clickEvent.initMouseEvent('click', true, true, null, 0, 0, 0, 0, 0,false, false, false, false, 0, element);element.dispatchEvent(clickEvent);">Do magic</button>
+  </form>
+  
+  <form action="javascriptPage.html" id="submitListeningForm" onsubmit="appendMessage('form-onsubmit '); return false;">
+    <p>
+      <input id="submitListeningForm-text" type="text" onsubmit="appendMessage('text-onsubmit ')" onclick="appendMessage('text-onclick ');" />
+      <input id="submitListeningForm-submit" type="submit" onsubmit="appendMessage('submit-onsubmit ')" onclick="appendMessage('submit-onclick ');" />
+    </p>
+  </form>
+</div>
+
+<p id="suppressedParagraph" style="display: none">A paragraph suppressed using CSS display=none</p>
+
+<div>
+    <p id="displayed">Displayed</p>
+
+    <form action="#"><input type="hidden" name="hidden" /> </form>
+
+    <p id="none" style="display: none;">Display set to none</p>
+
+    <p id="hidden" style="visibility: hidden;">Hidden</p>
+
+    <div id="hiddenparent" style="height: 2em; display: none;">
+      <div id="hiddenchild">
+        <a href="#" id="hiddenlink">ok</a>
+      </div>
+    </div>
+    
+    <div style="visibility: hidden;">
+      <span>
+        <input id="unclickable" />
+        <input type="checkbox" id="untogglable" checked="checked" />Check box you can't see
+      </span>
+    </div>
+
+    <p id="outer" style="visibility: hidden">A <b id="visibleSubElement" style="visibility: visible">sub-element that is explicitly visible</b> using CSS visibility=visible</p>
+</div>
+
+<div>
+    <form>
+        <input type="text" id="keyReporter" size="80"
+               onkeyup="appendMessage('up: ' + event.keyCode)"
+               onkeypress="appendMessage('press: ' + event.keyCode)"
+               onkeydown="displayMessage(''); appendMessage('down: ' + event.keyCode)" />
+        <input name="suppress" onkeydown="if (event.preventDefault) event.preventDefault(); event.returnValue = false; return false;" onkeypress="appendMessage('press');"/>
+    </form>
+</div>
+
+<!-- Used for testing styles -->
+<div style="background-color: green;" id="green-parent">
+  <p id="style1">This should be greenish</p>
+    <ul>
+      <li id="green-item">So should this</li>
+      <li id="red-item" style="background-color: red;">But this is red</li>
+    </ul>
+</div>
+
+<p id="zeroheight" style="height:0px;">This is a foo</p>
+
+<p id="zerowidth" style="width:0;">This is a bar</p>
+
+<a href="#" id="close" onclick="window.close();">Close window</a>
+
+<div id="delete" onclick="var d = document.getElementById('deleted'); this.removeChild(d);">
+    <p id="deleted">I should be deleted when you click my containing div</p>
+    <p>Whereas, I should not</p>
+</div>
+
+<div>
+    <span id="hideMe" onclick="this.style.display = 'none';">Click to hide me.</span>
+</div>
+
+<div style="margin-top: 10px;">
+  Click actions delayed by 3000ms:
+  <div id="clickToShow" onclick="delayedShowHide(3000, true);"
+       style="float: left;width: 100px;height:100px;border: 1px solid black;">
+    Click to show black box
+  </div>
+  <div id="clickToHide" onclick="delayedShowHide(3000, false);"
+       style="float: left;width: 100px;height:100px;border: 1px solid black;
+              background-color: black; color: white; display: none;">
+    Click to hide black box
+  </div>
+  <div style="clear: both"></div>
+</div>
+
+</body>
+</html>
+
+

+ 144 - 144
t/www/jquery-1.3.2.js

@@ -1266,150 +1266,150 @@ jQuery.each({
 function num(elem, prop) {
 	return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
 }
-var expando = "jQuery" + now(), uuid = 0, windowData = {};
-
-jQuery.extend({
-	cache: {},
-
-	data: function( elem, name, data ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// Compute a unique ID for the element
-		if ( !id )
-			id = elem[ expando ] = ++uuid;
-
-		// Only generate the data cache if we're
-		// trying to access or manipulate it
-		if ( name && !jQuery.cache[ id ] )
-			jQuery.cache[ id ] = {};
-
-		// Prevent overriding the named cache with undefined values
-		if ( data !== undefined )
-			jQuery.cache[ id ][ name ] = data;
-
-		// Return the named cache data, or the ID for the element
-		return name ?
-			jQuery.cache[ id ][ name ] :
-			id;
-	},
-
-	removeData: function( elem, name ) {
-		elem = elem == window ?
-			windowData :
-			elem;
-
-		var id = elem[ expando ];
-
-		// If we want to remove a specific section of the element's data
-		if ( name ) {
-			if ( jQuery.cache[ id ] ) {
-				// Remove the section of cache data
-				delete jQuery.cache[ id ][ name ];
-
-				// If we've removed all the data, remove the element's cache
-				name = "";
-
-				for ( name in jQuery.cache[ id ] )
-					break;
-
-				if ( !name )
-					jQuery.removeData( elem );
-			}
-
-		// Otherwise, we want to remove all of the element's data
-		} else {
-			// Clean up the element expando
-			try {
-				delete elem[ expando ];
-			} catch(e){
-				// IE has trouble directly removing the expando
-				// but it's ok with using removeAttribute
-				if ( elem.removeAttribute )
-					elem.removeAttribute( expando );
-			}
-
-			// Completely remove the data cache
-			delete jQuery.cache[ id ];
-		}
-	},
-	queue: function( elem, type, data ) {
-		if ( elem ){
-	
-			type = (type || "fx") + "queue";
-	
-			var q = jQuery.data( elem, type );
-	
-			if ( !q || jQuery.isArray(data) )
-				q = jQuery.data( elem, type, jQuery.makeArray(data) );
-			else if( data )
-				q.push( data );
-	
-		}
-		return q;
-	},
-
-	dequeue: function( elem, type ){
-		var queue = jQuery.queue( elem, type ),
-			fn = queue.shift();
-		
-		if( !type || type === "fx" )
-			fn = queue[0];
-			
-		if( fn !== undefined )
-			fn.call(elem);
-	}
-});
-
-jQuery.fn.extend({
-	data: function( key, value ){
-		var parts = key.split(".");
-		parts[1] = parts[1] ? "." + parts[1] : "";
-
-		if ( value === undefined ) {
-			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
-
-			if ( data === undefined && this.length )
-				data = jQuery.data( this[0], key );
-
-			return data === undefined && parts[1] ?
-				this.data( parts[0] ) :
-				data;
-		} else
-			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
-				jQuery.data( this, key, value );
-			});
-	},
-
-	removeData: function( key ){
-		return this.each(function(){
-			jQuery.removeData( this, key );
-		});
-	},
-	queue: function(type, data){
-		if ( typeof type !== "string" ) {
-			data = type;
-			type = "fx";
-		}
-
-		if ( data === undefined )
-			return jQuery.queue( this[0], type );
-
-		return this.each(function(){
-			var queue = jQuery.queue( this, type, data );
-			
-			 if( type == "fx" && queue.length == 1 )
-				queue[0].call(this);
-		});
-	},
-	dequeue: function(type){
-		return this.each(function(){
-			jQuery.dequeue( this, type );
-		});
-	}
+var expando = "jQuery" + now(), uuid = 0, windowData = {};
+
+jQuery.extend({
+	cache: {},
+
+	data: function( elem, name, data ) {
+		elem = elem == window ?
+			windowData :
+			elem;
+
+		var id = elem[ expando ];
+
+		// Compute a unique ID for the element
+		if ( !id )
+			id = elem[ expando ] = ++uuid;
+
+		// Only generate the data cache if we're
+		// trying to access or manipulate it
+		if ( name && !jQuery.cache[ id ] )
+			jQuery.cache[ id ] = {};
+
+		// Prevent overriding the named cache with undefined values
+		if ( data !== undefined )
+			jQuery.cache[ id ][ name ] = data;
+
+		// Return the named cache data, or the ID for the element
+		return name ?
+			jQuery.cache[ id ][ name ] :
+			id;
+	},
+
+	removeData: function( elem, name ) {
+		elem = elem == window ?
+			windowData :
+			elem;
+
+		var id = elem[ expando ];
+
+		// If we want to remove a specific section of the element's data
+		if ( name ) {
+			if ( jQuery.cache[ id ] ) {
+				// Remove the section of cache data
+				delete jQuery.cache[ id ][ name ];
+
+				// If we've removed all the data, remove the element's cache
+				name = "";
+
+				for ( name in jQuery.cache[ id ] )
+					break;
+
+				if ( !name )
+					jQuery.removeData( elem );
+			}
+
+		// Otherwise, we want to remove all of the element's data
+		} else {
+			// Clean up the element expando
+			try {
+				delete elem[ expando ];
+			} catch(e){
+				// IE has trouble directly removing the expando
+				// but it's ok with using removeAttribute
+				if ( elem.removeAttribute )
+					elem.removeAttribute( expando );
+			}
+
+			// Completely remove the data cache
+			delete jQuery.cache[ id ];
+		}
+	},
+	queue: function( elem, type, data ) {
+		if ( elem ){
+	
+			type = (type || "fx") + "queue";
+	
+			var q = jQuery.data( elem, type );
+	
+			if ( !q || jQuery.isArray(data) )
+				q = jQuery.data( elem, type, jQuery.makeArray(data) );
+			else if( data )
+				q.push( data );
+	
+		}
+		return q;
+	},
+
+	dequeue: function( elem, type ){
+		var queue = jQuery.queue( elem, type ),
+			fn = queue.shift();
+		
+		if( !type || type === "fx" )
+			fn = queue[0];
+			
+		if( fn !== undefined )
+			fn.call(elem);
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ){
+		var parts = key.split(".");
+		parts[1] = parts[1] ? "." + parts[1] : "";
+
+		if ( value === undefined ) {
+			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
+
+			if ( data === undefined && this.length )
+				data = jQuery.data( this[0], key );
+
+			return data === undefined && parts[1] ?
+				this.data( parts[0] ) :
+				data;
+		} else
+			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
+				jQuery.data( this, key, value );
+			});
+	},
+
+	removeData: function( key ){
+		return this.each(function(){
+			jQuery.removeData( this, key );
+		});
+	},
+	queue: function(type, data){
+		if ( typeof type !== "string" ) {
+			data = type;
+			type = "fx";
+		}
+
+		if ( data === undefined )
+			return jQuery.queue( this[0], type );
+
+		return this.each(function(){
+			var queue = jQuery.queue( this, type, data );
+			
+			 if( type == "fx" && queue.length == 1 )
+				queue[0].call(this);
+		});
+	},
+	dequeue: function(type){
+		return this.each(function(){
+			jQuery.dequeue( this, type );
+		});
+	}
 });/*!
  * Sizzle CSS Selector Engine - v0.9.3
  *  Copyright 2009, The Dojo Foundation