[OpenLayers-Commits] r12374 - sandbox/marcjansen/testsuite/tests

commits-20090109 at openlayers.org commits-20090109 at openlayers.org
Fri Sep 16 20:52:05 EDT 2011


Author: marcjansen
Date: 2011-09-16 17:52:04 -0700 (Fri, 16 Sep 2011)
New Revision: 12374

Added:
   sandbox/marcjansen/testsuite/tests/Test.AnotherWay.baseadditions.js
   sandbox/marcjansen/testsuite/tests/Test.AnotherWay.css
   sandbox/marcjansen/testsuite/tests/Test.AnotherWay.geom_eq.js
   sandbox/marcjansen/testsuite/tests/Test.AnotherWay.js
   sandbox/marcjansen/testsuite/tests/Test.AnotherWay.xml_eq.js
Removed:
   sandbox/marcjansen/testsuite/tests/geom_eq.js
   sandbox/marcjansen/testsuite/tests/xml_eq.js
Modified:
   sandbox/marcjansen/testsuite/tests/run-tests.html
Log:
enhancements to the testsuite
  - split resources (CSS, HTML, JavaScript)
  - dedicated files for our changes to the suite
  - CSS enhancements
  - adds a simple quicksearch
  - adds links to the testfiles to easily bypass browser cache
  - adds running time info panel
  - adds the total number of testcase to info panel 
  

Added: sandbox/marcjansen/testsuite/tests/Test.AnotherWay.baseadditions.js
===================================================================
--- sandbox/marcjansen/testsuite/tests/Test.AnotherWay.baseadditions.js	                        (rev 0)
+++ sandbox/marcjansen/testsuite/tests/Test.AnotherWay.baseadditions.js	2011-09-17 00:52:04 UTC (rev 12374)
@@ -0,0 +1,177 @@
+// total counters
+Test.AnotherWay._openlayers_sum_total_detail_ok=0;
+Test.AnotherWay._openlayers_sum_total_detail_fail=0;
+Test.AnotherWay._startTime = null;
+
+// method overwrites
+//
+// behaviour (timing)
+Test.AnotherWay._old_run_all_onclick = Test.AnotherWay._run_all_onclick;
+Test.AnotherWay._run_all_onclick = function(){
+    Test.AnotherWay._startTime = (new Date()).getTime();
+    Test.AnotherWay.reset_running_time();
+    Test.AnotherWay._old_run_all_onclick.apply(this, arguments);
+};
+
+Test.AnotherWay._old_run_selected_onclick = Test.AnotherWay._run_selected_onclick;
+Test.AnotherWay._run_selected_onclick = function(){
+    Test.AnotherWay._startTime = (new Date()).getTime();
+    Test.AnotherWay.reset_running_time();
+    Test.AnotherWay._old_run_selected_onclick.apply(this, arguments);
+};
+
+Test.AnotherWay._old_run_one_onclick = Test.AnotherWay._run_one_onclick;
+Test.AnotherWay._run_one_onclick = function(){
+    Test.AnotherWay._startTime = (new Date()).getTime();
+    Test.AnotherWay.reset_running_time();
+    Test.AnotherWay._old_run_one_onclick.apply(this, arguments);
+};
+
+// test page loading
+Test.AnotherWay.old_load_next_page = Test.AnotherWay._load_next_page;
+Test.AnotherWay._load_next_page = function(){
+    Test.AnotherWay.update_running_time();    
+    Test.AnotherWay.old_load_next_page.apply(this, arguments);
+};
+
+
+Test.AnotherWay._add_test_page_url = function(test_url, convention){
+    var table = document.getElementById("testtable");
+    var record_select = document.getElementById("record_select");
+    var index = Test.AnotherWay._g_test_page_urls.length;
+    
+    // trim spaces.
+    if (test_url.match("^(\\s*)(.*\\S)(\\s*)$")) {
+        test_url = RegExp.$2;
+    }
+    
+    Test.AnotherWay._g_test_page_urls[index] = {
+        url: test_url,
+        convention: convention
+    };
+    var row = table.insertRow(-1);
+    
+    var cell;
+    var cell_child;
+    var link;
+    
+    cell = row.insertCell(-1);
+    cell_child = document.createElement("input");
+    cell_child.type = "checkbox";
+    cell_child.id = "checkbox" + index;
+    cell_child.checked = 'checked';
+    cell_child.defaultChecked = 'checked';
+    cell.appendChild(cell_child);
+    
+    cell = row.insertCell(-1);
+    cell.setAttribute("width", "75%");
+     
+    // make the URL a clickable link that opens in a new window
+    // start changes 
+    link = document.createElement("a");
+    link.href=test_url;
+    link.target='_blank';
+    link.title='Opens testfile in a new window.';
+    link.appendChild(document.createTextNode(test_url));    
+    cell.appendChild(link);
+    // end changes
+    
+    cell = row.insertCell(-1);
+    cell_child = document.createElement("input");
+    cell_child.type = "button";
+    cell_child.id = "test" + index;
+    cell_child.value = " run ";
+    cell_child.onclick = Test.AnotherWay._run_one_onclick;
+    cell.appendChild(cell_child);
+    
+    cell = row.insertCell(-1);
+    cell.setAttribute("width", "8em");
+    cell_child = document.createElement("span");
+    cell.appendChild(cell_child);
+    
+    var option = document.createElement("option");
+    option.appendChild(document.createTextNode(test_url));
+    record_select.appendChild(option);
+};
+
+
+// new methods
+Test.AnotherWay.update_running_time = function() {
+    var now = (new Date()).getTime();
+    var floor = Math.floor;
+    var elapsed = now - Test.AnotherWay._startTime;
+    var zeroPad = function(num, length){
+        var len = -1 * (length || 2);
+        return ('00000' + num).slice(len);  
+    };
+    var ms = zeroPad(elapsed%1000, 3);
+    var seconds=zeroPad(floor((elapsed/1000)%60));
+    var minutes=zeroPad(floor((elapsed/60000)%60));
+    
+    document.getElementById('running-time').innerHTML = 'Elapsed time ' + minutes + ':' + seconds + ':' + ms +' (m:s:ms).';
+};
+
+Test.AnotherWay.reset_running_time = function(){
+    document.getElementById('running-time').innerHTML = '';
+};
+
+// quickfilter
+Test.AnotherWay.bindQuicksearchListener = function(){
+    var input = document.getElementById('quickfilter');
+    if (input.addEventListener) {
+        input.addEventListener('keyup', Test.AnotherWay.quicksearch);
+    } else if (input.attachEvent) {
+        input.attachEvent('onkeyup', Test.AnotherWay.quicksearch);
+    } else {
+        // remove the input field
+        input.parentNode.removeChild(input);
+    }
+};
+Test.AnotherWay.quicksearchThrottleTimeOut = null;
+Test.AnotherWay.quicksearch = function(){
+    if (Test.AnotherWay.quicksearchThrottleTimeOut) {
+        window.clearTimeout(Test.AnotherWay.quicksearchThrottleTimeOut);
+    }
+    Test.AnotherWay.quicksearchThrottleTimeOut = window.setTimeout(function(){
+        var input = document.getElementById('quickfilter');
+        Test.AnotherWay.filterTestList(input.value);
+    }, 300);
+};
+
+Test.AnotherWay.filterTestList = function(str){
+    Test.AnotherWay.unfilterTestList();
+    var re = new RegExp(str, 'i');
+    var candidates  = document.querySelectorAll('#testtable tr td:nth-child(2) a');
+    for (var idx = 0, len = candidates.length; idx<len; idx++) {
+        var tr = candidates[idx].parentNode.parentNode;
+        var html = candidates[idx].innerHTML;
+        if (re.test(html)) {
+            tr.className = 'isShown';
+        } else {
+            tr.className = 'isHidden';
+        }
+    }
+    
+};
+
+Test.AnotherWay.unfilterTestList = function() {
+    if ( document.querySelectorAll ) {
+        var hidden = document.querySelectorAll('.isHidden');
+        for (var idx = 0, len = hidden.length; idx < len; idx++) {
+            hidden[idx].className = 'isShown';
+        }
+    }
+};
+
+// bind our quicksearch init method to body onload.
+(function(win){
+    if (win.addEventListener) {
+        win.addEventListener('load', Test.AnotherWay.bindQuicksearchListener);
+    } else if (input.attachEvent) {
+        win.attachEvent('onload', Test.AnotherWay.bindQuicksearchListener);
+    } else {
+        win.onload = function(){
+            Test.AnotherWay.bindQuicksearchListener();
+        };
+    }
+})(window);

Added: sandbox/marcjansen/testsuite/tests/Test.AnotherWay.css
===================================================================
--- sandbox/marcjansen/testsuite/tests/Test.AnotherWay.css	                        (rev 0)
+++ sandbox/marcjansen/testsuite/tests/Test.AnotherWay.css	2011-09-17 00:52:04 UTC (rev 12374)
@@ -0,0 +1,243 @@
+/**
+ *  Test.AnotherWay version 0.5
+ *  
+ *  Copyright (c) 2005 Artem Khodush, http://straytree.org
+ *  
+ *  Permission is hereby granted, free of charge, to any person obtaining
+ *  a copy of this software and associated documentation files (the
+ *  "Software"), to deal in the Software without restriction, including
+ *  without limitation the rights to use, copy, modify, merge, publish,
+ *  distribute, sublicense, and/or sell copies of the Software, and to
+ *  permit persons to whom the Software is furnished to do so, subject to
+ *  the following conditions:
+ *  
+ *  The above copyright notice and this permission notice shall be
+ *  included in all copies or substantial portions of the Software.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *  
+ */
+
+* {
+    padding: 0;
+    margin: 0;
+}
+
+html {
+    height: 99%;
+}
+
+body {
+    height: 98%;
+    font: normal normal 10pt sans-serif
+}
+
+#col1 {
+    float: left;
+    width: 27em;
+    margin: 0 0 0 1em;
+    overflow: visible;
+}
+
+#col2 {
+    position: relative;
+    height: 98%;
+    margin: 0 0.5em 0 28em;
+}
+
+#col1_header {
+    margin-top: 0.5em;
+}
+
+#scroller {
+    height: 400px;
+    overflow: auto;
+}
+
+#testtable {
+    margin: 0 0 2em 0;
+    width: 97%;
+    font-size: 1em;
+    border-collapse: collapse;
+}
+#testtable input {
+    cursor: pointer;
+}
+#testtable td {
+    line-height: 2em;
+    padding: 0;
+    margin: 0;
+    border-top: 1px solid #ccc;
+    border-bottom: 1px solid #ccc;
+}
+#testtable tr:hover td {
+    background-color: #ededed;
+}
+#testtable tr.isHidden {
+    display: none;
+}
+#testtable tr.isShown {
+    display: table-row;
+}
+
+#run_buttons, #running-time {
+    margin-bottom: 1em;
+}
+
+#right_header {
+    padding-top: 0.8em;
+}
+
+#results_count {
+    float: left;
+}
+
+#results > p:hover {
+    background-color: #ededed;
+}
+
+.active_tab {
+    float: right;
+    padding: 0 1em 0.2em 1em;
+    background: #0af;
+    border: 1px solid #048;
+    border-bottom: none;
+    cursor: pointer;
+    cursor: hand;
+    position: relative;
+    top: -0.2em;
+}
+
+.inactive_tab {
+    float: right;
+    padding: 0 1em 0 1em;
+    background: #9bb;
+    color: #444;
+    border: 1px solid #9bb;
+    border-bottom: none;
+    cursor: pointer;
+    cursor: hand;
+}
+
+.inactive_mouseover_tab {
+    float: right;
+    padding: 0 1em 0 1em;
+    background: #9bb;
+    color: #062;
+    border: 1px solid #062;
+    border-bottom: none;
+    cursor: pointer;
+    cursor: hand;
+}
+
+#right_frame {
+    overflow: auto;
+    position: relative;
+    top: -0.2em;
+    clear: right;
+    height: 95%;
+    border: 1px solid #048;
+}
+
+#debug {
+    display: none;
+}
+
+#debug p {
+    margin: 2px 0 0 5em;
+    text-indent: -4.8em;
+}
+
+#error {
+    display: none;
+    color: #c22;
+}
+
+#results p {
+    margin: 0 0 2px 0;
+}
+
+/* cursor indicating that detailed results may be expanded/contracted */
+#results p.badtest {
+    cursor: text;
+}
+
+#results p.ok, #results p.fail {
+    cursor: pointer;
+    cursor: hand;
+}
+
+/* colored squares in the results window at the left of test page names */
+#results p.ok .bullet {
+    background: #6d6;
+}
+
+#results p.fail .bullet {
+    background: #d46;
+}
+
+#results p.badtest .bullet {
+    background: #ea3;
+}
+
+#results p.loading .bullet {
+    background: #48f;
+}
+
+#results p.running .bullet {
+    background: #26e;
+}
+
+#results p.waiting .bullet {
+    background: #04d;
+}
+
+/* highlight in the results line */
+#results p .warning {
+    background: #ffc;
+}
+
+/* layout of the detailed results */
+.result_detail {
+    padding-left: 3em;
+}
+
+.result_exception_detail {
+    padding-left: 4em;
+}
+
+.result_exception_stack_detail {
+    padding-left: 5em;
+}
+
+.result_micro_detail {
+    padding-left: 6em;
+}
+
+/* colouring in the detailed results */
+.result_detail .fail, .result_exception_detail .fail, .result_micro_detail .fail {
+    background: #ffd8d8;
+}
+
+/* "start recording" controls*/
+#record_div {
+    margin-top: 3em;
+}
+
+#record_div p {
+    margin-bottom: 0.5em;
+}
+
+#record_select {
+    width: 88%;
+}
+
+#record_input {
+    width: 53%;
+}
\ No newline at end of file

Added: sandbox/marcjansen/testsuite/tests/Test.AnotherWay.geom_eq.js
===================================================================
--- sandbox/marcjansen/testsuite/tests/Test.AnotherWay.geom_eq.js	                        (rev 0)
+++ sandbox/marcjansen/testsuite/tests/Test.AnotherWay.geom_eq.js	2011-09-17 00:52:04 UTC (rev 12374)
@@ -0,0 +1,110 @@
+/**
+ * File: Test.AnotherWay.geom_eq.js
+ * Adds a xml_eq method to AnotherWay test objects.
+ *
+ */
+
+(function() {
+    
+    /**
+     * Function assertEqual
+     * Test two objects for equivalence (based on ==).  Throw an exception
+     *     if not equivalent.
+     * 
+     * Parameters:
+     * got - {Object}
+     * expected - {Object}
+     * msg - {String} The message to be thrown.  This message will be appended
+     *     with ": got {got} but expected {expected}" where got and expected are
+     *     replaced with string representations of the above arguments.
+     */
+    function assertEqual(got, expected, msg) {
+        if(got === undefined) {
+            got = "undefined";
+        } else if (got === null) {
+            got = "null";
+        }
+        if(expected === undefined) {
+            expected = "undefined";
+        } else if (expected === null) {
+            expected = "null";
+        }
+        if(got != expected) {
+            throw msg + ": got '" + got + "' but expected '" + expected + "'";
+        }
+    }
+    
+    /**
+     * Function assertGeometryEqual
+     * Test two geometries for equivalence.  Geometries are considered
+     *     equivalent if they are of the same class, and given component
+     *     geometries, if all components are equivalent. Throws a message as
+     *     exception if not equivalent.
+     * 
+     * Parameters:
+     * got - {OpenLayers.Geometry}
+     * expected - {OpenLayers.Geometry}
+     * options - {Object} Optional object for configuring test options.
+     */
+    function assertGeometryEqual(got, expected, options) {
+        
+        var OpenLayers = Test.AnotherWay._g_test_iframe.OpenLayers;
+
+        // compare types
+        assertEqual(typeof got, typeof expected, "Object types mismatch");
+        
+        // compare classes
+        assertEqual(got.CLASS_NAME, expected.CLASS_NAME, "Object class mismatch");
+        
+        if(got instanceof OpenLayers.Geometry.Point) {
+            // compare points
+            assertEqual(got.x, expected.x, "x mismatch");
+            assertEqual(got.y, expected.y, "y mismatch");
+            assertEqual(got.z, expected.z, "z mismatch");
+        } else {
+            // compare components
+            assertEqual(
+                got.components.length, expected.components.length,
+                "Component length mismatch for " + got.CLASS_NAME
+            );
+            for(var i=0; i<got.components.length; ++i) {
+                try {
+                    assertGeometryEqual(
+                        got.components[i], expected.components[i], options
+                    );
+                } catch(err) {
+                    throw "Bad component " + i + " for " + got.CLASS_NAME + ": " + err;
+                }
+            }
+        }
+        return true;
+    }
+    
+    /**
+     * Function: Test.AnotherWay._test_object_t.geom_eq
+     * Test if two geometry objects are equivalent.  Tests for same geometry
+     *     class, same number of components (if any), equivalent component
+     *     geometries, and same coordinates.
+     *
+     * (code)
+     * t.geom_eq(got, expected, message);
+     * (end)
+     * 
+     * Parameters:
+     * got - {OpenLayers.Geometry} Any geometry instance.
+     * expected - {OpenLayers.Geometry} The expected geometry.
+     * msg - {String} A message to print with test output.
+     * options - {Object} Optional object for configuring test options.
+     */
+    var proto = Test.AnotherWay._test_object_t.prototype;
+    proto.geom_eq = function(got, expected, msg, options) {        
+        // test geometries for equivalence
+        try {
+            assertGeometryEqual(got, expected, options);
+            this.ok(true, msg);
+        } catch(err) {
+            this.fail(msg + ": " + err);
+        }
+    }
+    
+})();
\ No newline at end of file

Added: sandbox/marcjansen/testsuite/tests/Test.AnotherWay.js
===================================================================
--- sandbox/marcjansen/testsuite/tests/Test.AnotherWay.js	                        (rev 0)
+++ sandbox/marcjansen/testsuite/tests/Test.AnotherWay.js	2011-09-17 00:52:04 UTC (rev 12374)
@@ -0,0 +1,2498 @@
+/**
+ *  Test.AnotherWay version 0.5
+ *  
+ *  Copyright (c) 2005 Artem Khodush, http://straytree.org
+ *  
+ *  Permission is hereby granted, free of charge, to any person obtaining
+ *  a copy of this software and associated documentation files (the
+ *  "Software"), to deal in the Software without restriction, including
+ *  without limitation the rights to use, copy, modify, merge, publish,
+ *  distribute, sublicense, and/or sell copies of the Software, and to
+ *  permit persons to whom the Software is furnished to do so, subject to
+ *  the following conditions:
+ *  
+ *  The above copyright notice and this permission notice shall be
+ *  included in all copies or substantial portions of the Software.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ *  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ *  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ *  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ *  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *  
+ */
+
+if (typeof(Test) == "undefined") {
+    Test = {};
+}
+Test.AnotherWay = {};
+
+Test.AnotherWay._g_test_iframe = null; // frame where to load test pages
+Test.AnotherWay._g_test_frame_no_clear = false; // true - leave last page displayed after tests end
+Test.AnotherWay._g_test_page_urls = []; // array of: { url: url, convention: "anotherway" or "jsan" }
+Test.AnotherWay._g_test_object_for_jsan = null; // test object for filling by tests that adhere to jsan Test.Simple calling convention
+Test.AnotherWay._g_pages_to_run = null; // list of pages to run automatically after loading
+Test.AnotherWay._g_run_on_main_load = false; // special handling for run_pages_to_run when it might be called before onload or before list of test pages is known.
+Test.AnotherWay._g_run_on_list_load = false;
+Test.AnotherWay._g_main_loaded = false;
+
+Test.AnotherWay._run_pages_to_run = function(called_from_outside){
+    if (!Test.AnotherWay._g_main_loaded) {
+        Test.AnotherWay._g_run_on_main_load = true;
+    }
+    else {
+        var a_pages = Test.AnotherWay._g_pages_to_run;
+        if (a_pages == "all") {
+            for (var i = 0; i < Test.AnotherWay._g_test_page_urls.length; ++i) {
+                Test.AnotherWay._run_test_page("test" + i);
+            }
+        }
+        else 
+            if (a_pages != null) {
+                for (var run_i = 0; run_i < a_pages.length; ++run_i) {
+                    var run_page = a_pages[run_i];
+                    var found = false;
+                    for (var all_i = 0; all_i < Test.AnotherWay._g_test_page_urls.length; ++all_i) {
+                        if (run_page == Test.AnotherWay._g_test_page_urls[all_i].url) {
+                            Test.AnotherWay._run_test_page("test" + all_i, called_from_outside);
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        Test.AnotherWay._show_error("page specified to run is not found in the page list: " + run_page);
+                        break;
+                    }
+                }
+            }
+    }
+};
+
+Test.AnotherWay._add_test_page_url = function(test_url, convention){
+    var table = document.getElementById("testtable");
+    var record_select = document.getElementById("record_select");
+    var index = Test.AnotherWay._g_test_page_urls.length;
+    
+    // trim spaces.
+    if (test_url.match("^(\\s*)(.*\\S)(\\s*)$")) {
+        test_url = RegExp.$2;
+    }
+    
+    Test.AnotherWay._g_test_page_urls[index] = {
+        url: test_url,
+        convention: convention
+    };
+    var row = table.insertRow(-1);
+    
+    var cell;
+    var cell_child;
+    var link;
+    
+    cell = row.insertCell(-1);
+    cell_child = document.createElement("input");
+    cell_child.type = "checkbox";
+    cell_child.id = "checkbox" + index;
+    cell_child.checked = 'checked';
+    cell_child.defaultChecked = 'checked';
+    cell.appendChild(cell_child);
+    
+    cell = row.insertCell(-1);
+    cell.setAttribute("width", "75%");
+    
+    cell.appendChild(document.createTextNode(test_url));    
+    
+    cell = row.insertCell(-1);
+    cell_child = document.createElement("input");
+    cell_child.type = "button";
+    cell_child.id = "test" + index;
+    cell_child.value = " run ";
+    cell_child.onclick = Test.AnotherWay._run_one_onclick;
+    cell.appendChild(cell_child);
+    
+    cell = row.insertCell(-1);
+    cell.setAttribute("width", "8em");
+    cell_child = document.createElement("span");
+    cell.appendChild(cell_child);
+    
+    var option = document.createElement("option");
+    option.appendChild(document.createTextNode(test_url));
+    record_select.appendChild(option);
+};
+Test.AnotherWay._show_error = function(msg){
+    var error_div = document.getElementById("error");
+    error_div.innerHTML = "";
+    error_div.appendChild(document.createTextNode(msg));
+    error_div.style.display = "block";
+};
+
+// read urls from the list in the html file inside the list_iframe
+// fill on-screen list with urls and "run" buttons, and fill the g_test_page_urls object.
+Test.AnotherWay._list_iframe_onload = function(){
+    if (window.frames.list_iframe != null && window.frames.list_iframe.location != "" && window.frames.list_iframe.location != "about:blank") {
+        var list_doc = window.frames.list_iframe.document;
+        var list = list_doc.getElementById("testlist");
+        if (list != null) {
+            for (var i = 0; i < list.childNodes.length; ++i) {
+                var item = list.childNodes[i];
+                if (item.nodeName == "LI" || item.nodeName == "li") {
+                    var convention = "anotherway";
+                    if (Test.AnotherWay._get_css_class(item) == "jsan") {
+                        convention = "jsan";
+                    }
+                    Test.AnotherWay._add_test_page_url(item.innerHTML, convention);
+                }
+            }
+            if (Test.AnotherWay._g_run_on_list_load) {
+                Test.AnotherWay._g_run_on_list_load = false;
+                Test.AnotherWay._run_pages_to_run();
+            }
+        }
+        else {
+            Test.AnotherWay._show_error("no list with id 'testlist' in a list file " + window.frames.list_iframe.location);
+        }
+    }
+};
+
+Test.AnotherWay._map_checkboxes = function(f){
+    var table = document.getElementById("testtable");
+    var checks = table.getElementsByTagName("INPUT");
+    for (var i = 0; i < checks.length; ++i) {
+        if (checks[i].type == "checkbox" && checks[i].id.match(/^checkbox(\d+)$/)) {
+            f(checks[i], RegExp.$1);
+        }
+    }
+};
+
+Test.AnotherWay._run_all_onclick = function(){
+    Test.AnotherWay._map_checkboxes(function(c, id){
+        Test.AnotherWay._run_test_page("test" + id);
+    });
+};
+Test.AnotherWay._run_selected_onclick = function(){
+    Test.AnotherWay._map_checkboxes(function(c, id){
+        if (c.checked) {
+            Test.AnotherWay._run_test_page("test" + id);
+        }
+    });
+};
+
+Test.AnotherWay._unselect_all_onclick = function(){
+    Test.AnotherWay._map_checkboxes(function(c, id){
+        c.checked = false;
+    });
+};
+
+Test.AnotherWay._run_one_onclick = function(){
+    Test.AnotherWay._run_test_page(this.id);
+};
+
+// construct an object that will gather results of running one test function
+Test.AnotherWay._test_object_t = function(fun_name){
+    this.name = fun_name; // name of the test function
+    this.n_plan = null; // planned number of assertions
+    this.n_ok = 0; // # of ok assertions
+    this.n_fail = 0; // # of failed assertions
+    this.exception = ""; // if the function throwed an exception, it's its message
+    this.exception_stack = []; // strings: function call stack from the exception
+    this.assertions = []; // assertion results: array of { ok: 1 or 0, name: string }
+    this.wait_result_milliseconds = 0; // how long to wait before collecting results from the test
+    this.second_wait_msg = null; // <p> status message (in addition to the page wait_msg)
+    this.delay_actions = []; // array of actions to be perfomed after the test function returns
+    //  action : { acton_kind: "call" | "window" | "replay"
+    //              when "call":        { call_fn call_delay_milliseconds } call_fn takes nothing
+    //              when "window" :     { wnd_url wnd_wnd wnd_fn wnd_timeout_milliseconds wnd_dont_close } wnd_fn takes wnd
+    //              wnen "replay" :     { replay_wnd replay_events replay_event_i replay_checkpoints } checkpoint_fn takes this, wnd
+    //  }
+    this.delay_action_i = null; // index of delay action currently being performed
+    this.delay_prev_timer_time = 0; // for counting time while performing delay_actions
+    this.delay_current_milliseconds_left = 0; // time left before the next action, runs down
+    this.delay_total_milliseconds_left = 0; // for indication: total estimated time for all actions, runs up and down
+};
+
+Test.AnotherWay._test_object_t.prototype.ok = function(cond, name){
+    if (cond) {
+        ++this.n_ok;
+        cond = 1;
+    }
+    else {
+        ++this.n_fail;
+        cond = 0;
+    }
+    this.assertions.push({
+        ok: cond,
+        name: name
+    });
+};
+Test.AnotherWay._test_object_t.prototype.fail = function(name){
+    this.ok(false, name);
+};
+Test.AnotherWay._test_object_t.prototype.plan = function(n){
+    this.n_plan = n;
+};
+Test.AnotherWay._test_object_t.prototype.wait_result = function(seconds){
+    this.wait_result_milliseconds = 1000 * seconds;
+};
+Test.AnotherWay._eq_fail_msg = function(path, what, expected, got){
+    return "eq: " + path + " " + what + " differ: got " + got + ", but expected " + expected;
+};
+Test.AnotherWay._array_eq = function(expected, got, path, msg){
+    if (expected.length != got.length) {
+        msg.msg = Test.AnotherWay._eq_fail_msg(path, "array length", expected.length, got.length);
+        return false;
+    }
+    for (var i = 0; i < expected.length; ++i) {
+        if (!Test.AnotherWay._thing_eq(expected[i], got[i], path + "[" + i + "]", msg)) {
+            return false;
+        }
+    }
+    return true;
+};
+Test.AnotherWay._object_eq = function(expected, got, path, msg){
+    var v;
+    for (v in expected) {
+        if (!(v in got)) {
+            msg.msg = Test.AnotherWay._eq_fail_msg(path + "." + v, "properties", expected[v], "undefined");
+            return false;
+        }
+        if (!Test.AnotherWay._thing_eq(expected[v], got[v], path + "." + v, msg)) {
+            return false;
+        }
+    }
+    for (v in got) {
+        if (!(v in expected)) {
+            msg.msg = Test.AnotherWay._eq_fail_msg(path + "." + v, "properties", "undefined", got[v]);
+            return false;
+        }
+    }
+    return true;
+};
+
+Test.AnotherWay._constructor_name = function(x){
+    if (x == null) {
+        return "";
+    }
+    var s = "unknown";
+    try {
+        s = typeof(x.constructor);
+        if (s != "unknown") {
+            s = x.constructor.toString();
+        }
+    } 
+    catch (e) {
+        s = "unknown";
+    }
+    if (s == "unknown") {
+        // hackish attempt to guess a type
+        var is_array = true;
+        var index = 0;
+        for (i in x) {
+            if (i != index) {
+                is_array = false;
+            }
+            ++index;
+        }
+        return is_array ? "Array" : "Object"; // for empty arrays/objects, this will be wrong half the time
+    }
+    else 
+        if (s.match(/^\s*function\s+(\w+)\s*\(/)) {
+            return RegExp.$1;
+        }
+        else {
+            var c = '';
+            switch (typeof x) {
+                case 'string':
+                    c = 'String';
+                    break;
+                case 'object':
+                    c = 'Object';
+                    break;
+                default:
+                    c = '';
+            }
+            return c;
+        }
+};
+Test.AnotherWay._is_array = function(x){
+    return Test.AnotherWay._constructor_name(x) == "Array";
+};
+
+Test.AnotherWay._is_value_type = function(x){
+    cn = Test.AnotherWay._constructor_name(x);
+    return cn == "Number" || cn == "String" || cn == "Boolean" || cn == "Date";
+};
+
+Test.AnotherWay._thing_eq = function(expected, got, path, msg){
+    if (expected == null && got == null) {
+        return true;
+    }
+    else 
+        if ((expected == null && got != null) || (expected != null && got == null)) {
+            msg.msg = Test.AnotherWay._eq_fail_msg(path, "values", expected, got);
+            return false;
+        }
+        else {
+            var expected_cn = Test.AnotherWay._constructor_name(expected);
+            var got_cn = Test.AnotherWay._constructor_name(got);
+            if (expected_cn != got_cn) {
+                msg.msg = Test.AnotherWay._eq_fail_msg(path, "types", expected_cn, got_cn);
+                return false;
+            }
+            else {
+                if (Test.AnotherWay._is_array(expected)) {
+                    return Test.AnotherWay._array_eq(expected, got, path, msg);
+                }
+                else 
+                    if (Test.AnotherWay._is_value_type(expected)) {
+                        if (expected != got) {
+                            msg.msg = Test.AnotherWay._eq_fail_msg(path, "values", expected, got);
+                            return false;
+                        }
+                        else {
+                            return true;
+                        }
+                    }
+                    else { // just a plain object
+                        return Test.AnotherWay._object_eq(expected, got, path, msg);
+                    }
+            }
+        }
+};
+
+Test.AnotherWay._test_object_t.prototype.eq = function(got, expected, name){
+    var msg = {};
+    if (Test.AnotherWay._thing_eq(expected, got, "", msg)) {
+        this.ok(1, name);
+    }
+    else {
+        this.fail(name + ". " + msg.msg);
+    }
+};
+
+Test.AnotherWay._test_object_t.prototype.like = function(got, expected, name){
+    if (got.match(expected) != null) {
+        this.ok(1, name);
+    }
+    else {
+        this.fail(name + ": got " + got + ", but expected it to match: " + expected);
+    }
+};
+
+Test.AnotherWay._g_html_eq_span = null;
+Test.AnotherWay._html_eq_string_to_node = function(string_or_node, what, msg){
+    if (string_or_node.nodeType != null) {
+        string_or_node = Test.AnotherWay._html_eq_node_to_string(string_or_node); // double trip - to make properties assigned in scripts available as html node attributes
+    }
+    if (Test.AnotherWay._g_html_eq_span == null) {
+        Test.AnotherWay._g_html_eq_span = document.createElement("span");
+    }
+    Test.AnotherWay._g_html_eq_span.innerHTML = string_or_node;
+    if (Test.AnotherWay._g_html_eq_span.childNodes.length != 1) {
+        msg.msg = "bad " + what + " html string given (should contain exactly one outermost element): " + string_or_node;
+    }
+    return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode(true);
+};
+
+Test.AnotherWay._html_eq_node_to_string = function(node){
+    if (Test.AnotherWay._g_html_eq_span == null) {
+        Test.AnotherWay._g_html_eq_span = document.createElement("span");
+    }
+    Test.AnotherWay._g_html_eq_span.innerHTML = "";
+    if (node.outerHTML != null) {
+        Test.AnotherWay._g_html_eq_span.innerHTML = node.outerHTML;
+    }
+    else {
+        var clone = node.cloneNode(true);
+        var node = Test.AnotherWay._g_html_eq_span;
+        if (node.ownerDocument && node.ownerDocument.importNode) {
+            if (node.ownerDocument != clone.ownerDocument) {
+                clone = node.ownerDocument.importNode(clone, true);
+            }
+        }
+        node.appendChild(clone);
+    }
+    return Test.AnotherWay._g_html_eq_span.innerHTML;
+};
+
+Test.AnotherWay._html_eq_path_msg = function(path){
+    var msg = "";
+    for (var i = 0; i < path.length; ++i) {
+        msg += " [node " + path[i].node;
+        if (path[i].id != null && path[i].id != "") {
+            msg += " id " + path[i].id;
+        }
+        else 
+            if (path[i].index != null) {
+                msg += " at index " + path[i].index;
+            }
+        msg += "] ";
+    }
+    return msg;
+};
+
+Test.AnotherWay._html_eq_fail_msg = function(path, what, expected, got){
+    return Test.AnotherWay._html_eq_path_msg(path) + ": " + what + " differ: got " + got + ", but expected " + expected;
+};
+
+Test.AnotherWay._html_eq_remove_blank = function(text){
+    if (text == null) {
+        return "";
+    }
+    else 
+        if (text.match("^(\\s*)(.*\\S)(\\s*)$")) {
+            return RegExp.$2;
+        }
+        else 
+            if (text.match("\s*")) {
+                return "";
+            }
+    return text;
+};
+
+Test.AnotherWay._html_eq_remove_blank_nodes = function(node){
+    var to_remove = [];
+    for (var child = node.firstChild; child != null; child = child.nextSibling) {
+        if (child.nodeType == 3) {
+            var value = Test.AnotherWay._html_eq_remove_blank(child.nodeValue);
+            if (value == "") {
+                to_remove.push(child);
+            }
+            else {
+                child.nodeValue = value;
+            }
+        }
+    }
+    for (var i = 0; i < to_remove.length; ++i) {
+        node.removeChild(to_remove[i]);
+    }
+};
+
+Test.AnotherWay._html_node_type_text = function(node_type){
+    if (node_type == 1) {
+        return "1 (html element)";
+    }
+    else 
+        if (node_type == 3) {
+            return "3 (text)";
+        }
+        else {
+            return node_type;
+        }
+};
+
+Test.AnotherWay._html_eq_node = function(expected, got, path, msg, expected_loc_base, got_loc_base){
+    if (expected.nodeType != got.nodeType) {
+        msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "node types", Test.AnotherWay._html_node_type_text(expected.nodeType), Test.AnotherWay._html_node_type_text(got.nodeType));
+        return false;
+    }
+    else 
+        if (expected.nodeType == 3) {
+            if (expected.nodeValue != got.nodeValue) {
+                msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "text", expected.nodeValue, got.nodeValue);
+                return false;
+            }
+        }
+        else 
+            if (expected.nodeType == 1) {
+                if (expected.nodeName != got.nodeName) {
+                    msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "node names", expected.nodeName, got.nodeName);
+                    return false;
+                }
+                // compare attributes
+                var expected_attrs = {};
+                var got_attrs = {};
+                var i;
+                var a;
+                for (i = 0; i < expected.attributes.length; ++i) {
+                    a = expected.attributes[i];
+                    if (a.specified) {
+                        expected_attrs[a.name] = 1;
+                    }
+                }
+                for (i = 0; i < got.attributes.length; ++i) {
+                    a = got.attributes[i];
+                    if (a.specified) {
+                        got_attrs[a.name] = 1;
+                    }
+                }
+                for (a in expected_attrs) {
+                    if (!(a in got_attrs)) {
+                        msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": attribute sets differ: expected attribute " + a + " is missing";
+                        return false;
+                    }
+                }
+                for (a in got_attrs) {
+                    if (!(a in expected_attrs)) {
+                        msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": attribute sets differ: got extra attribute " + a;
+                        return false;
+                    }
+                }
+                for (a in expected_attrs) {
+                    var expected_value = expected.getAttribute(a);
+                    var got_value = got.getAttribute(a);
+                    if (typeof(expected_value) == "string" && typeof(got_value) == "string") {
+                        expected_value = Test.AnotherWay._html_eq_remove_blank(expected_value);
+                        got_value = Test.AnotherWay._html_eq_remove_blank(got_value);
+                        var ok = expected_value == got_value;
+                        if (!ok && (a == "href" || a == "HREF")) { // try relative hrefs
+                            var expected_relative_value = expected_value;
+                            if (expected_loc_base != null && expected_value.substring(0, expected_loc_base.length) == expected_loc_base) {
+                                expected_relative_value = expected_value.substring(expected_loc_base.length);
+                            }
+                            var got_relative_value = got_value;
+                            if (got_loc_base != null && got_value.substring(0, got_loc_base.length) == got_loc_base) {
+                                got_relative_value = got_value.substring(got_loc_base.length);
+                            }
+                            ok = expected_relative_value == got_relative_value;
+                        }
+                        if (!ok) {
+                            msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "attribute " + a + " values", expected_value, got_value);
+                            return false;
+                        }
+                    }
+                    else 
+                        if (typeof(expected_value) == "function" && typeof(got_value) == "function") {
+                            expected_value = expected_value.toString();
+                            got_value = got_value.toString();
+                            if (expected_value != got_value) {
+                                msg.msg = Test.AnotherWay._html_eq_fail_msg(path, "attribute " + a + " values", expected_value, got_value);
+                                return false;
+                            }
+                        }
+                        else {
+                            var value_msg = {};
+                            if (!Test.AnotherWay._thing_eq(expected_value, got_value, "", value_msg)) {
+                                msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": attribute " + a + " values differ: " + value_msg.msg;
+                                return false;
+                            }
+                        }
+                }
+                // compare child nodes
+                Test.AnotherWay._html_eq_remove_blank_nodes(expected);
+                Test.AnotherWay._html_eq_remove_blank_nodes(got);
+                var expected_length = expected.childNodes.length;
+                var got_length = got.childNodes.length;
+                if (expected_length < got_length) {
+                    msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": got " + (got_length - expected_length) + " extra child nodes";
+                    return false;
+                }
+                else 
+                    if (expected_length > got_length) {
+                        msg.msg = Test.AnotherWay._html_eq_path_msg(path) + ": expected " + (expected_length - got_length) + " more child nodes";
+                        return false;
+                    }
+                    else {
+                        for (i = 0; i < expected_length; ++i) {
+                            var expected_node = expected.childNodes[i];
+                            path.push({
+                                node: expected_node.nodeName,
+                                id: expected_node.id,
+                                index: i
+                            });
+                            var eq = Test.AnotherWay._html_eq_node(expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base);
+                            path.pop();
+                            if (!eq) {
+                                return false;
+                            }
+                        }
+                    }
+            }
+    return true;
+};
+
+Test.AnotherWay._html_eq_get_loc_base = function(node){
+    var loc_base = document.location;
+    if (node.ownerDocument != null) {
+        loc_base = node.ownerDocument.location;
+    }
+    if (loc_base != null) {
+        loc_base = loc_base.href;
+        var slash_pos = loc_base.lastIndexOf("/");
+        if (slash_pos != -1) {
+            loc_base = loc_base.substring(0, slash_pos + 1);
+        }
+    }
+    return loc_base;
+};
+
+Test.AnotherWay._test_object_t.prototype.html_eq = function(got, expected, name){
+    var msg = {};
+    var expected_node = Test.AnotherWay._html_eq_string_to_node(expected, "expected", msg);
+    if (msg.msg != null) {
+        this.fail(name + " html_eq: " + msg.msg);
+    }
+    else {
+        var got_node = Test.AnotherWay._html_eq_string_to_node(got, "got", msg);
+        if (msg.msg != null) {
+            this.fail(name + " html_eq: " + msg.msg);
+        }
+        else {
+            var expected_loc_base = Test.AnotherWay._html_eq_get_loc_base(expected);
+            var got_loc_base = Test.AnotherWay._html_eq_get_loc_base(got);
+            if (Test.AnotherWay._html_eq_node(expected_node, got_node, [], msg, expected_loc_base, got_loc_base)) {
+                this.ok(1, name);
+            }
+            else {
+                var msg = name + " html_eq " + msg.msg;
+                var expected_str = Test.AnotherWay._html_eq_node_to_string(expected_node);
+                var got_str = Test.AnotherWay._html_eq_node_to_string(got_node);
+                msg += ".\n got html: " + got_str;
+                msg += ".\n expected html: " + expected_str;
+                this.fail(msg);
+            }
+        }
+    }
+};
+
+Test.AnotherWay._debug_pane_print = function(msg){
+    var d = new Date();
+    var p = document.createElement("p");
+    p.appendChild(document.createTextNode(d.toLocaleTimeString() + " " + msg));
+    var debug_pane = document.getElementById("debug");
+    debug_pane.appendChild(p);
+    var debug_tab = document.getElementById("debug_tab");
+    var results_tab = document.getElementById("results_tab");
+    debug_tab.style.visibility = "visible";
+    results_tab.style.visibility = "visible";
+};
+
+Test.AnotherWay._test_object_t.prototype.debug_print = function(msg){
+    Test.AnotherWay._debug_pane_print(this.name + ": " + msg);
+};
+
+Test.AnotherWay._test_object_t.prototype.delay_call = function(){
+    var timeout_ms = 200;
+    for (var i = 0; i < arguments.length; ++i) {
+        if (typeof(arguments[i]) != "function") {
+            timeout_ms = 1000 * arguments[i];
+        }
+        else {
+            var action = {
+                action_kind: "call",
+                call_delay_milliseconds: timeout_ms,
+                call_fn: arguments[i]
+            };
+            this.delay_total_milliseconds_left += Test.AnotherWay._action_estimate_milliseconds(action);
+            this.delay_actions.push(action);
+        }
+    }
+};
+
+Test.AnotherWay._test_object_t.prototype.open_window = function(url, fn, timeout_seconds){
+    if (timeout_seconds == null) {
+        timeout_seconds = 4;
+    }
+    var no_close = document.getElementById("dont_close_test_windows");
+    var action = {
+        action_kind: "window",
+        wnd_url: url.toString() + (window.location.search || ""),
+        wnd_wnd: null,
+        wnd_fn: fn,
+        wnd_timeout_milliseconds: timeout_seconds * 1000,
+        wnd_no_close: no_close.checked
+    };
+    this.delay_total_milliseconds_left += Test.AnotherWay._action_estimate_milliseconds(action);
+    this.delay_actions.push(action);
+};
+
+Test.AnotherWay._test_object_t.prototype.replay_events = function(wnd, events){
+    if (Test.AnotherWay._g_no_record_msg != null) {
+        this.fail("replay_events: " + Test.AnotherWay._g_no_record_msg);
+    }
+    else {
+        var action = {
+            action_kind: "replay",
+            replay_wnd: wnd,
+            replay_events: events.events,
+            replay_event_i: null,
+            replay_checkpoints: events.checkpoints
+        };
+        this.delay_total_milliseconds_left += Test.AnotherWay._action_estimate_milliseconds(action);
+        this.delay_actions.push(action);
+    }
+};
+
+Test.AnotherWay._action_estimate_milliseconds = function(action){
+    var ms = 0;
+    if (action.action_kind == "call") {
+        ms = action.call_delay_milliseconds;
+    }
+    else 
+        if (action.action_kind == "window") {
+            ms = 0;
+        }
+        else 
+            if (action.action_kind == "replay") {
+                ms = 0;
+                for (var i = 0; i < action.replay_events.length; ++i) {
+                    ms += action.replay_events[i]["time"] - 0;
+                }
+            }
+    return ms;
+};
+
+Test.AnotherWay._g_timeout_granularity = 200;
+Test.AnotherWay._g_tests_queue = []; // vector of { url: string, test_objects : array of test_object_t, test_object_i: int, wait_msg: <p> object, loading_timeout_milliseconds: int, timeout_id: id }
+// load one html page, schedule further processing
+Test.AnotherWay._run_test_page = function(id, called_from_outside){
+    if (id.match(/^test(\d+)/)) {
+        id = RegExp.$1;
+        Test.AnotherWay._g_tests_queue.push({
+            url: Test.AnotherWay._g_test_page_urls[id].url,
+            convention: Test.AnotherWay._g_test_page_urls[id].convention,
+            test_objects: []
+        });
+        if (Test.AnotherWay._g_tests_queue.length == 1) {
+            if (!called_from_outside) {
+                // Crap. Be careful stepping around.
+                // For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
+                // but the test pages are started from within it (by "run" buttons), then:
+                // depending on whether the page is the first one loaded into the test frame or not,
+                // the base url for relative test pages differs.
+                // Crap, like I said.
+                Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction = true;
+            }
+            Test.AnotherWay._start_loading_page();
+        }
+    }
+};
+
+Test.AnotherWay._load_next_page = function(){
+    Test.AnotherWay._g_tests_queue.splice(0, 1);
+    if (Test.AnotherWay._g_tests_queue.length > 0) {
+        Test.AnotherWay._start_loading_page();
+    }
+    else {
+        if (!Test.AnotherWay._g_test_frame_no_clear) {
+            Test.AnotherWay._g_test_iframe.location.replace("about:blank");
+        }
+    }
+};
+
+Test.AnotherWay._g_opera_path_correction = null; // ugly wart to support opera
+Test.AnotherWay._g_outside_path_correction = null; // ugly wart to accomodate Opera and Mozilla, where relative url relates to the directory where the page that calls this function is located
+Test.AnotherWay._set_iframe_location = function(iframe, loc, outside_path_correction){
+    // allow to load only locations with the same origin
+    var proto_end = loc.indexOf("://");
+    if (proto_end != -1) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
+        var main_loc = window.location.href;
+        var host_end = loc.substring(proto_end + 3).indexOf("/");
+        var ok = false;
+        if (host_end != -1) {
+            var loc_origin = loc.substring(0, proto_end + 3 + host_end + 1);
+            if (main_loc.length >= loc_origin.length && main_loc.substring(0, loc_origin.length) == loc_origin) {
+                ok = true;
+            }
+        }
+        if (!ok) {
+            return {
+                msg: "test pages may have only urls with the same origin as " + main_loc
+            };
+        }
+    }
+    // opera cannot handle urls relative to file:// without assistance
+    if (window.opera != null && window.location.protocol == "file:" && loc.indexOf(":") == -1) {
+        var base = window.location.href;
+        var q_pos = base.indexOf("?");
+        if (q_pos != -1) {
+            base = base.substring(0, q_pos);
+        }
+        var slash_pos = base.lastIndexOf("/");
+        if (slash_pos != -1) {
+            base = base.substring(0, slash_pos + 1);
+            Test.AnotherWay._g_opera_path_correction = base;
+            loc = base + loc;
+        }
+    }
+    // if this function is called from another page, and if that page is in another directory, correction is needed
+    if (outside_path_correction != null) {
+        var pos = loc.indexOf(outside_path_correction);
+        if (pos == 0) {
+            loc = loc.substring(outside_path_correction.length + 1);
+        }
+    }
+    if (iframe.location != null) {
+        iframe.location.replace(loc);
+    }
+    else {
+        iframe.src = loc;
+    }
+    return {};
+};
+
+Test.AnotherWay._start_loading_page = function(){
+    var test_page = Test.AnotherWay._g_tests_queue[0];
+    test_page.loading_timeout_milliseconds = 12000;
+    test_page.timeout_id = setTimeout(Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity);
+    test_page.wait_msg = Test.AnotherWay._print_counter_result(test_page.url, "loading...", test_page.loading_timeout_milliseconds, "loading");
+    if (test_page.convention == "jsan") {
+        // the tests in that page will run when it's loading, so the test object must be ready
+        Test.AnotherWay._g_test_object_for_jsan = new Test.AnotherWay._test_object_t(test_page.url);
+    }
+    var outside_path_correction = null;
+    if (Test.AnotherWay._g_outside_path_correction != null && !test_page.suppress_outside_path_correction) {
+        outside_path_correction = Test.AnotherWay._g_outside_path_correction;
+    }
+    var result = Test.AnotherWay._set_iframe_location(Test.AnotherWay._g_test_iframe, test_page.url, outside_path_correction);
+    if (result.msg != null) {
+        Test.AnotherWay._unprint_result(test_page.wait_msg);
+        Test.AnotherWay._print_result(test_page.url, result.msg, "badtest", null);
+        Test.AnotherWay._load_next_page();
+    }
+};
+
+Test.AnotherWay._loading_timeout = function(){
+    var test_page = Test.AnotherWay._g_tests_queue[0];
+    test_page.loading_timeout_milliseconds -= Test.AnotherWay._g_timeout_granularity;
+    if (test_page.loading_timeout_milliseconds > 0) {
+        Test.AnotherWay._update_msg_counter(test_page.wait_msg, (test_page.loading_timeout_milliseconds / 1000).toFixed());
+        test_page.timeout_id = setTimeout(Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity);
+    }
+    else {
+        Test.AnotherWay._unprint_result(test_page.wait_msg);
+        Test.AnotherWay._print_result(test_page.url, "Unable to load test page. Timeout expired", "badtest", null);
+        Test.AnotherWay._load_next_page();
+    }
+};
+
+Test.AnotherWay._strip_query_and_hash = function(s){
+    var i = s.lastIndexOf("#");
+    if (i != -1) {
+        s = s.substring(0, i);
+    }
+    i = s.lastIndexOf("?");
+    if (i != -1) {
+        s = s.substring(0, i);
+    }
+    return s;
+};
+
+Test.AnotherWay._is_url_loaded = function(url, wnd){
+    var loaded = false;
+    if (wnd != null && wnd.location != null) {
+        // after some popup blocker interference, location may behave strange..
+        var location_s = "";
+        location_s += wnd.location;
+        if (location_s != "") {
+            var pathname = wnd.location.pathname;
+            var expected_url = url;
+            var i = expected_url.lastIndexOf("#");
+            if (i != -1) {
+                expected_url = expected_url.substring(0, i);
+            }
+            i = expected_url.lastIndexOf("?");
+            if (i != -1) {
+                expected_url = expected_url.substring(0, i);
+            }
+            i = expected_url.lastIndexOf("/");
+            if (i != -1 && i != expected_url.length - 1) {
+                expected_url = expected_url.substring(i + 1);
+            }
+            i = pathname.indexOf(expected_url);
+            if (wnd.location.href == url || (i != -1 && i == pathname.length - expected_url.length)) {
+                if ( /*window.opera==null*/wnd.document.readyState == null || wnd.document.readyState == "complete") { // for opera (and IE?), getElementById does not work until..
+                    loaded = true;
+                }
+            }
+        }
+    }
+    return loaded;
+};
+// find and run all test functions in the g_cur_page html page.
+Test.AnotherWay._test_page_onload = function(){
+    if (Test.AnotherWay._g_tests_queue.length == 0) {
+        return;
+    }
+    var test_page = Test.AnotherWay._g_tests_queue[0];
+    if (!Test.AnotherWay._is_url_loaded(test_page.url, Test.AnotherWay._g_test_iframe)) {
+        return;
+    }
+    clearTimeout(test_page.timeout_id);
+    Test.AnotherWay._unprint_result(test_page.wait_msg);
+    
+    if (test_page.convention == "anotherway") {
+        // get test function names (those beginning with "test")
+        if (typeof(Test.AnotherWay._g_test_iframe.document.scripts) != 'undefined') { // IE
+            for (var i = 0; i < Test.AnotherWay._g_test_iframe.document.scripts.length; ++i) {
+                var script_text = Test.AnotherWay._g_test_iframe.document.scripts[i].text;
+                var fun_sig = "function test";
+                var fun_start = script_text.indexOf(fun_sig);
+                
+                while (fun_start != -1) {
+                    script_text = script_text.substring(fun_start, script_text.length);
+                    var fun_end = script_text.indexOf('(');
+                    var fun_name = script_text.substring("function ".length, fun_end);
+                    var whitespace = fun_name.indexOf(' ');
+                    if (whitespace >= 0) {
+                        fun_name = fun_name.substring(0, whitespace);
+                    }
+                    test_page.test_objects.push(new Test.AnotherWay._test_object_t(fun_name));
+                    script_text = script_text.substring(fun_end, script_text.length);
+                    fun_start = script_text.indexOf(fun_sig);
+                }
+            }
+        }
+        else { // otherwise (not IE) it ought to work like this
+            for (var i in Test.AnotherWay._g_test_iframe) {
+                // Hack to prevent failure in FF3.0b1 (innerWidth/innerHeight) and FF>=3.5 (sessionStorage)
+                if (i == "innerWidth" || i == "innerHeight" || i == "sessionStorage") {
+                    continue;
+                }
+                if (typeof(Test.AnotherWay._g_test_iframe[i]) == 'function') {
+                    if (i.substring(0, 4) == "test") {
+                        test_page.test_objects.push(new Test.AnotherWay._test_object_t(i));
+                    }
+                }
+            }
+        }
+    }
+    else 
+        if (test_page.convention == "jsan") {
+            // the test object is already filled with results
+            test_page.test_objects.push(Test.AnotherWay._g_test_object_for_jsan);
+        }
+    
+    if (test_page.test_objects.length == 0) {
+        Test.AnotherWay._print_result(test_page.url, "No test functions defined in the page", "badtest", null);
+        Test.AnotherWay._load_next_page();
+        return;
+    }
+    
+    test_page.wait_msg = Test.AnotherWay._print_result(test_page.url, "running tests..<span class=\"counter\">" + test_page.test_objects.length + "</span>", "running", null);
+    
+    test_page.test_object_i = 0;
+    Test.AnotherWay._run_more_tests();
+};
+
+Test.AnotherWay._handle_exception = function(o, e, title){
+    var s = title + ": " + typeof(e) + ": ";
+    if (e.message != null) {
+        s += e.message;
+    }
+    else 
+        if (e.description != null) {
+            s += e.description;
+        }
+        else {
+            s += e.toString();
+        }
+    //  if( e.location!=null ) {  // XXX figure out how to display exception location if it's present (like in mozilla)
+    //      s+=" location: "+e.location.toString();
+    //  }
+    o.exception = s;
+    s = [];
+    if (e.stack) {
+        var lines = e.stack.split("\n");
+        for (var i = 0; i < lines.length; ++i) {
+            // format of the line: func_name(args)@file_name:line_no
+            if (lines[i].match(/(\w*)\(([^\)]*)\)@(.*):([^:]*)$/)) {
+                var func_name = RegExp.$1;
+                if (func_name.length == 0) {
+                    func_name = "<anonymous>";
+                }
+                s.push("in " + func_name + "( " + RegExp.$2 + ") at " + RegExp.$3 + " line " + RegExp.$4 + "\n");
+            }
+        }
+    }
+    o.exception_stack = s;
+};
+
+Test.AnotherWay._run_more_tests = function(){
+    var test_page = Test.AnotherWay._g_tests_queue[0];
+    while (test_page.test_object_i < test_page.test_objects.length) {
+        Test.AnotherWay._update_msg_counter(test_page.wait_msg, (1 + test_page.test_object_i) + "/" + test_page.test_objects.length);
+        var o = test_page.test_objects[test_page.test_object_i];
+        if (test_page.convention == "anotherway") {
+            try {
+                Test.AnotherWay._g_test_iframe[o.name](o);
+            } 
+            catch (e) {
+                Test.AnotherWay._handle_exception(o, e, "");
+            }
+        } // for "jsan" convention, test has run already
+        if (o.delay_actions.length > 0 || o.wait_result_milliseconds > 0) {
+            o.delay_total_milliseconds_left += o.wait_result_milliseconds;
+            Test.AnotherWay._delay_actions_timeout();
+            return;
+        }
+        ++test_page.test_object_i;
+    }
+    Test.AnotherWay._unprint_result(test_page.wait_msg);
+    Test.AnotherWay._print_result(test_page.url, null, null, test_page.test_objects);
+    Test.AnotherWay._load_next_page();
+};
+
+Test.AnotherWay._delay_actions_timeout = function(){
+    var test_page = Test.AnotherWay._g_tests_queue[0];
+    var test_object = test_page.test_objects[test_page.test_object_i];
+    var finished = true;
+    if (test_object.delay_action_i == null) {
+        // set up to start first action
+        test_object.delay_action_i = -1;
+    }
+    else {
+        // perform current action
+        var milliseconds_passed = (new Date()).getTime() - test_object.delay_prev_timer_time;
+        test_object.delay_current_milliseconds_left -= milliseconds_passed;
+        test_object.delay_total_milliseconds_left -= milliseconds_passed;
+        finished = Test.AnotherWay._delay_continue_action(test_object, milliseconds_passed);
+    }
+    while (finished && test_object.delay_action_i < test_object.delay_actions.length) {
+        ++test_object.delay_action_i; // start next action
+        finished = Test.AnotherWay._delay_start_action(test_object);
+    }
+    if (test_object.delay_action_i <= test_object.delay_actions.length) { // any more actions left ?
+        test_object.delay_prev_timer_time = (new Date()).getTime();
+        var next_timeout = Test.AnotherWay._g_timeout_granularity;
+        if (test_object.delay_current_milliseconds_left < next_timeout) {
+            next_timeout = test_object.delay_current_milliseconds_left;
+        }
+        if (test_object.second_wait_msg != null) {
+            Test.AnotherWay._update_msg_counter(test_object.second_wait_msg, (test_object.delay_total_milliseconds_left / 1000).toFixed());
+        }
+        setTimeout(Test.AnotherWay._delay_actions_timeout, next_timeout);
+    }
+    else { // no more actions left. run the next test.
+        if (test_object.second_wait_msg != null) {
+            Test.AnotherWay._unprint_result(test_object.second_wait_msg);
+            test_object.second_wait_msg = null;
+        }
+        ++test_page.test_object_i;
+        Test.AnotherWay._run_more_tests();
+    }
+};
+
+Test.AnotherWay._delay_start_action = function(test_object){
+    var finished = false;
+    var wait_msg = "";
+    if (test_object.delay_action_i == test_object.delay_actions.length) {
+        if (test_object.wait_result_milliseconds > 0) {
+            test_object.delay_current_milliseconds_left = test_object.wait_result_milliseconds; // wait for result
+            wait_msg = "waiting for results..";
+        }
+        else {
+            ++test_object.delay_action_i; // dont wait for result
+        }
+    }
+    else {
+        var action = test_object.delay_actions[test_object.delay_action_i];
+        if (action.action_kind == "call") {
+            test_object.delay_current_milliseconds_left = action.call_delay_milliseconds;
+            wait_msg = "performing delayed calls..";
+        }
+        else 
+            if (action.action_kind == "window") {
+                if (Test.AnotherWay._g_opera_path_correction != null && action.wnd_url.indexOf(":") == -1) {
+                    action.wnd_url = Test.AnotherWay._g_opera_path_correction + action.wnd_url;
+                }
+                action.wnd_wnd = window.open(action.wnd_url, "_blank");
+                if (action.wnd_wnd == null) {
+                    finished = true;
+                    test_object.fail("unable to open window for " + action.wnd_url);
+                }
+                else {
+                    test_object.delay_current_milliseconds_left = action.wnd_timeout_milliseconds;
+                    wait_msg = "opening window..";
+                }
+            }
+            else 
+                if (action.action_kind == "replay") {
+                    if (action.replay_events.length == 0) {
+                        finished = true;
+                    }
+                    else {
+                        action.replay_event_i = 0;
+                        test_object.delay_current_milliseconds_left = action.replay_events[0]["time"];
+                        wait_msg = "replaying events..";
+                    }
+                }
+    }
+    if (test_object.second_wait_msg != null) {
+        Test.AnotherWay._unprint_result(test_object.second_wait_msg);
+    }
+    if (wait_msg != "") {
+        var test_page = Test.AnotherWay._g_tests_queue[0];
+        test_object.second_wait_msg = Test.AnotherWay._print_counter_result(test_page.url, wait_msg, test_object.delay_total_milliseconds_left, "waiting");
+    }
+    else {
+        test_object.second_wait_msg = null;
+    }
+    return finished;
+};
+Test.AnotherWay._delay_continue_action = function(test_object, milliseconds_passed){
+    var finished = test_object.delay_current_milliseconds_left <= 0;
+    if (test_object.delay_action_i == test_object.delay_actions.length) { // action is "waiting for results"
+        if (test_object.n_plan != null && test_object.n_plan == test_object.n_ok + test_object.n_fail) {
+            finished = true; // if all assertions results are recorded, don't wait any more
+        }
+        if (finished) {
+            ++test_object.delay_action_i; // move on to the next test
+        }
+    }
+    else {
+        var action = test_object.delay_actions[test_object.delay_action_i];
+        if (action.action_kind == "call") {
+            if (finished) {
+                try {
+                    action.call_fn();
+                } 
+                catch (e) {
+                    Test.AnotherWay._handle_exception(test_object, e, "in delay_call");
+                }
+            }
+        }
+        else 
+            if (action.action_kind == "window") {
+                test_object.delay_total_milliseconds_left += milliseconds_passed; // for "window", the countdown is suspended since it's unknown how long it will take
+                if (Test.AnotherWay._is_url_loaded(action.wnd_url, action.wnd_wnd)) {
+                    try {
+                        action.wnd_fn(action.wnd_wnd);
+                    } 
+                    catch (e) {
+                        Test.AnotherWay._handle_exception(test_object, e, "in open_window function call");
+                    }
+                    finished = true;
+                }
+                else 
+                    if (finished) {
+                        test_object.fail("unable to open window for url '" + action.wnd_url + "'. timeout expired");
+                    }
+            }
+            else 
+                if (action.action_kind == "replay") {
+                    if (finished) {
+                        //              try {
+                        Test.AnotherWay._delay_replay_event(test_object, action.replay_wnd, action.replay_events[action.replay_event_i], action.replay_checkpoints);
+                        //              }catch( e ) { // disabled, until I know how to gel location info from an exception
+                        //                  Test.AnotherWay._handle_exception( test_object, e, "while replaying event" );
+                        //              }
+                        ++action.replay_event_i;
+                        finished = action.replay_event_i == action.replay_events.length;
+                        if (!finished) {
+                            test_object.delay_current_milliseconds_left = action.replay_events[action.replay_event_i]["time"];
+                        }
+                    }
+                }
+    }
+    return finished;
+};
+
+Test.AnotherWay._delay_replay_event = function(test_object, wnd, event, checkpoints){
+    if (event.type == "_checkpoint") {
+        var checkpoint_n = event.which;
+        var prev_n_fail = test_object.n_fail;
+        checkpoints[checkpoint_n](test_object, wnd);
+        var flash_color = prev_n_fail == test_object.n_fail ? "#2f2" : "#f22";
+        Test.AnotherWay._record_flash_border(flash_color);
+    }
+    else 
+        if (event.type == "click" || event.type == "mouseover" || event.type == "mouseout" || event.type == "mousemove" || event.type == "mousedown" || event.type == "mouseup") {
+            var target = Test.AnotherWay._record_node_path_to_node(event["target"], wnd.document);
+            if (target != null) {
+                Test.AnotherWay._record_control_update_highlight(target, "ball", event);
+                var e = wnd.document.createEvent("MouseEvents");
+                var related_target = Test.AnotherWay._record_node_path_to_node(event["relatedTarget"], wnd.document);
+                e.initMouseEvent(event["type"], event["cancelable"], event["bubbles"], wnd.document.defaultView, event["detail"], event["screenX"], event["screenY"], event["clientX"], event["clientY"], event["ctrlKey"], event["altKey"], event["shiftKey"], event["metaKey"], event["button"], Test.AnotherWay._record_node_path_to_node(event["relatedTarget"], wnd.document));
+                // Firefox 1.0.6 somehow loses relatedTarget somewhere on the way. Pass through our own, for those who choose to care.
+                e.passThroughRelatedTarget = related_target;
+                target.dispatchEvent(e);
+            }
+        }
+        else 
+            if (event.type == "keyup" || event.type == "keydown" || event.type == "keypress") {
+                var e = wnd.document.createEvent("KeyboardEvents"); // forget it. Apparently it's not supported neither by mozilla nor by opera.
+                e.initKeyboardEvent(event["type"], event["cancelable"], event["bubbles"], wnd.document.defaultView, event["which"], event["which"], event["ctrlKey"], event["altKey"], event["shiftKey"], event["metaKey"], false);
+                wnd.document.dispatchEvent(e);
+            }
+};
+
+Test.AnotherWay._print_counter_result = function(url, msg, milliseconds, style){
+    return Test.AnotherWay._print_result(url, msg + "<span class=\"counter\">" + (milliseconds / 1000).toFixed() + "</span>", style, null);
+};
+
+Test.AnotherWay._g_result_count = 0; // for assigning unique ids to result paragraphs
+// number of pages tested
+Test.AnotherWay._g_ok_pages = 0;
+Test.AnotherWay._g_fail_pages = 0;
+
+Test.AnotherWay._print_result = function(url, msg, style, test_objects){
+    var results = document.getElementById("results");
+    var r = results.appendChild(document.createElement("p"));
+    r.id = "result" + Test.AnotherWay._g_result_count;
+    ++Test.AnotherWay._g_result_count;
+    r.onclick = Test.AnotherWay._toggle_detail;
+    var text = "<span class=\"bullet\">&nbsp;&nbsp;&nbsp;</span>&nbsp;";
+    if (url != "") {
+        text += url + ":  ";
+    }
+    if (msg != null) {
+        text += msg;
+    }
+    if (test_objects != null) {
+        // compose summary and detail texts
+        var total_ok = 0;
+        var total_detail_ok = 0;
+        var total_fail = 0;
+        var total_detail_fail = 0;
+        var no_plan = 0;
+        
+        var detail = results.appendChild(document.createElement("div"));
+        
+        if (r.id.match(/^result(\d+)$/)) {
+            detail.id = "result_detail" + RegExp.$1;
+        }
+        
+        for (var i = 0; i < test_objects.length; ++i) {
+            var o = test_objects[i];
+            var p;
+            var p_text;
+            p = document.createElement("P");
+            Test.AnotherWay._set_css_class(p, "result_detail");
+            p_text = o.name;
+            if (o.n_fail > 0 || o.exception || (o.n_plan != null && o.n_plan != o.n_ok + o.n_fail) || (o.n_plan == null && o.n_ok == 0 && o.n_fail == 0)) {
+                ++total_fail;
+                p_text += " <span class=\"fail\">";
+                if (o.n_plan != null && o.n_plan != o.n_ok + o.n_fail) {
+                    p_text += "planned " + o.n_plan + " assertions but got " + (o.n_ok + o.n_fail) + "; ";
+                }
+                if (o.n_plan == null && o.n_ok == 0 && o.n_fail == 0) {
+                    p_text += "test did not output anything";
+                }
+                else {
+                    p_text += " fail " + o.n_fail;
+                }
+                p_text += "</span>";
+            }
+            else {
+                ++total_ok;
+            }
+            p_text += " ok " + o.n_ok;
+            if (o.n_plan == null) {
+                no_plan = 1;
+                p_text += " <span class=\"warning\">no plan</span>";
+            }
+            p.innerHTML = p_text;
+            detail.appendChild(p);
+            if (o.exception) {
+                p = document.createElement("P");
+                Test.AnotherWay._set_css_class(p, "result_exception_detail");
+                p.innerHTML = "<span class=\"fail\">exception:</span> " + o.exception;
+                detail.appendChild(p);
+                p = document.createElement("P");
+                Test.AnotherWay._set_css_class(p, "result_exception_stack_detail");
+                p.innerHTML = o.exception_stack.join("<br/>");
+                detail.appendChild(p);
+            }
+            for (var ii = 0; ii < o.assertions.length; ++ii) {
+                var oo = o.assertions[ii];
+                var status = oo.ok ? "ok" : "<span class=\"fail\">fail</span>";
+                p = document.createElement("P");
+                Test.AnotherWay._set_css_class(p, "result_micro_detail");
+                p.innerHTML = status;
+                p.appendChild(document.createTextNode(" " + oo.name));
+                detail.appendChild(p);
+            }
+            total_detail_ok += o.n_ok;
+            total_detail_fail += o.n_fail;
+        }
+        if (total_fail || total_detail_fail) {
+            text += " fail " + total_fail;
+        }
+        text += " ok " + total_ok + " (detailed:";
+        if (total_fail || total_detail_fail) {
+            text += " fail " + total_detail_fail;
+        }
+        text += " ok " + total_detail_ok + ")";
+        if (no_plan) {
+            text += " <span class=\"warning\">no plan</span>";
+        }
+        style = total_fail == 0 ? "ok" : "fail";
+        detail.style.display = style == "fail" ? "block" : "none";
+        detail.style.cursor = "text";
+    }
+    if (style != null) {
+        Test.AnotherWay._set_css_class(r, style);
+        if (style == "ok") {
+            ++Test.AnotherWay._g_ok_pages;
+        }
+        else 
+            if (style == "fail" || style == "badtest") {
+                ++Test.AnotherWay._g_fail_pages;
+            }
+        var pages_total = "";
+        if (Test.AnotherWay._g_fail_pages > 0) {
+            pages_total += " fail " + Test.AnotherWay._g_fail_pages;
+        }
+        pages_total += " ok " + Test.AnotherWay._g_ok_pages;
+        
+        // also count out the total number of tests in fail and ok
+        Test.AnotherWay._openlayers_sum_total_detail_ok  = Test.AnotherWay._openlayers_sum_total_detail_ok || 0;
+        Test.AnotherWay._openlayers_sum_total_detail_ok += (total_detail_ok||0);
+        
+        Test.AnotherWay._openlayers_sum_total_detail_fail  = Test.AnotherWay._openlayers_sum_total_detail_fail || 0;
+        Test.AnotherWay._openlayers_sum_total_detail_fail += (total_detail_fail||0);
+        
+        pages_total+=" (detailed: fail " + Test.AnotherWay._openlayers_sum_total_detail_fail + " | ok " + Test.AnotherWay._openlayers_sum_total_detail_ok + ")";
+        
+        Test.AnotherWay._update_results_total(pages_total);
+    }
+    r.innerHTML = text;
+    if (results.scrollHeight != null && results.scrollTop != null && results.offsetHeight != null) {
+        results.scrollTop = results.scrollHeight - results.offsetHeight;
+    }
+    // when test_objects is not null, the results are final - good time to clean up
+    if (test_objects != null) {
+        for (var i = 0; i < test_objects.length; ++i) {
+            var actions = test_objects[i].delay_actions;
+            for (var action_i = 0; action_i < actions.length; ++action_i) {
+                var action = actions[action_i];
+                if (action.action_kind == "window" && action.wnd_wnd != null && !action.wnd_no_close) {
+                    action.wnd_wnd.close();
+                    action.wnd_wnd = null;
+                }
+            }
+        }
+    }
+    return r;
+};
+
+Test.AnotherWay._unprint_result = function(child){
+    var results = document.getElementById("results");
+    results.removeChild(child);
+};
+
+Test.AnotherWay._toggle_detail = function(){
+    if (this.id.match(/^result(\d+)$/)) {
+        var detail = document.getElementById("result_detail" + RegExp.$1);
+        if (detail != null) {
+            if (detail.style.display == "none") {
+                detail.style.display = "block";
+            }
+            else 
+                if (detail.style.display == "block") {
+                    detail.style.display = "none";
+                }
+        }
+    }
+};
+
+Test.AnotherWay._update_msg_counter = function(msg, text){
+    for (var i = 0; i < msg.childNodes.length; ++i) {
+        var item = msg.childNodes[i];
+        if (item.nodeName == "SPAN" && Test.AnotherWay._get_css_class(item) == "counter") {
+            item.innerHTML = text;
+        }
+    }
+};
+
+Test.AnotherWay._update_results_total = function(msg){
+    var total = document.getElementById("total");
+    if (total) {
+        total.innerHTML = msg;
+    }
+};
+
+Test.AnotherWay._results_clear_onclick = function(){
+    var results = document.getElementById("results");
+    results.innerHTML = "";
+    Test.AnotherWay._update_results_total("");
+    Test.AnotherWay._g_ok_pages = 0;
+    Test.AnotherWay._g_fail_pages = 0;
+    Test.AnotherWay._openlayers_sum_total_detail_ok=0;
+    Test.AnotherWay._openlayers_sum_total_detail_fail=0;
+    var debug = document.getElementById("debug");
+    debug.innerHTML = "";
+    Test.AnotherWay.reset_running_time();
+};
+
+Test.AnotherWay._get_css_class = function(o){
+    var c = o.getAttribute("className");
+    if (c == null || c == "") {
+        c = o.getAttribute("class");
+    }
+    return c;
+};
+
+Test.AnotherWay._set_css_class = function(o, css_class){
+    o.setAttribute("className", css_class);
+    o.setAttribute("class", css_class);
+};
+
+Test.AnotherWay._tab_onclick = function(){
+    var tab = this;
+    var tabs = [document.getElementById("debug_tab"), document.getElementById("results_tab")];
+    var panes = [document.getElementById("debug"), document.getElementById("results")];
+    for (var i = 0; i < tabs.length; ++i) {
+        if (tab == tabs[i]) {
+            Test.AnotherWay._set_css_class(tabs[i], "active_tab");
+            panes[i].style.display = "block";
+        }
+        else {
+            Test.AnotherWay._set_css_class(tabs[i], "inactive_tab");
+            panes[i].style.display = "none";
+        }
+    }
+};
+Test.AnotherWay._tab_mouseover = function(){
+    if (Test.AnotherWay._get_css_class(this) == "inactive_tab") {
+        Test.AnotherWay._set_css_class(this, "inactive_mouseover_tab");
+    }
+};
+Test.AnotherWay._tab_mouseout = function(){
+    if (Test.AnotherWay._get_css_class(this) == "inactive_mouseover_tab") {
+        Test.AnotherWay._set_css_class(this, "inactive_tab");
+    }
+};
+
+// recording mouse input
+Test.AnotherWay._record_check_onfocus = function(){
+    var o = this;
+    var check_select = o.type != "text";
+    var div = document.getElementById("record_div");
+    var inputs = div.getElementsByTagName("input");
+    for (var i = 0; i < inputs.length; ++i) {
+        var input = inputs[i];
+        if (input.type == "radio") {
+            if (input.value == "select") {
+                input.checked = check_select;
+            }
+            else 
+                if (input.value == "input") {
+                    input.checked = !check_select;
+                }
+        }
+    }
+};
+
+Test.AnotherWay._g_no_record_msg = null; // not null - recording is unavailable
+Test.AnotherWay._g_record_timeout_cnt = 0; // opening window for a page for recording
+Test.AnotherWay._g_record_url = null;
+Test.AnotherWay._g_record_wnd = null;
+Test.AnotherWay._g_record_random_id = null; // added to element ids of record_control div so that they do not clash with ids already in the page for which input is recorded
+Test.AnotherWay._g_record_keydown = null; // recording control - which key is down
+Test.AnotherWay._g_record_ctrl_keydown = false;
+Test.AnotherWay._g_record_shift_keydown = false;
+Test.AnotherWay._g_record_control_visible = true; // recording control ui state
+Test.AnotherWay._g_record_started;
+Test.AnotherWay._g_record_paused;
+Test.AnotherWay._g_record_include_mousemove = false;
+Test.AnotherWay._g_record_start_time; // for time references
+Test.AnotherWay._g_record_pause_start_time;
+Test.AnotherWay._g_record_update_time_interval; // showing time in the control ui
+Test.AnotherWay._g_record_waiting_for_results = false; // waiting for results window to open
+Test.AnotherWay._g_record_events; // recorded events
+Test.AnotherWay._g_record_under_cursor; // track element under cursor
+Test.AnotherWay._g_record_checkpoint_count; // for checkpoint numbering
+Test.AnotherWay._g_record_mouse_over_record_control; // for avoiding record control highlight on mouseover
+Test.AnotherWay._g_record_highlighted_element = {
+    element: null,
+    x: null,
+    y: null
+};
+
+Test.AnotherWay._record_control_get_element = function(id){
+    if (Test.AnotherWay._g_record_wnd != null && Test.AnotherWay._g_record_wnd.document != null) {
+        return Test.AnotherWay._g_record_wnd.document.getElementById(id + Test.AnotherWay._g_record_random_id);
+    }
+    else {
+        return null;
+    }
+};
+Test.AnotherWay._record_start_onclick = function() // "record" button on the run_tests.html: open a window for a page for which input is recorded
+{
+    if (Test.AnotherWay._g_no_record_msg != null) {
+        alert(Test.AnotherWay._g_no_record_msg);
+        return;
+    }
+    if (Test.AnotherWay._g_record_timeout_cnt > 0 ||
+    (Test.AnotherWay._g_record_wnd != null && (Test.AnotherWay._g_record_wnd.closed != null && !Test.AnotherWay._g_record_wnd.closed))) { // in opera, closed is null.
+        alert("there is already window opened for recording input for a page " + Test.AnotherWay._g_record_url);
+        return;
+    }
+    var div = document.getElementById("record_div");
+    var inputs = div.getElementsByTagName("input");
+    var url = null;
+    for (var i = 0; i < inputs.length; ++i) {
+        var input = inputs[i];
+        if (input.type == "radio") {
+            if (input.value == "select" && input.checked) {
+                var index = document.getElementById("record_select").selectedIndex;
+                if (index > 0) {
+                    url = Test.AnotherWay._g_test_page_urls[index - 1].url;
+                }
+            }
+            else 
+                if (input.value == "input" && input.checked) {
+                    url = document.getElementById("record_input").value;
+                }
+        }
+    }
+    if (url != null) {
+        Test.AnotherWay._g_record_url = url;
+        Test.AnotherWay._g_record_wnd = window.open(url, "_blank");
+        if (Test.AnotherWay._g_record_wnd == null) {
+            alert("unable to open new window for a page: " + url);
+        }
+        else {
+            Test.AnotherWay._g_record_timeout_cnt = 50;
+            setTimeout(Test.AnotherWay._record_window_timeout, 100);
+        }
+    }
+};
+Test.AnotherWay._record_window_timeout = function(){
+    if (Test.AnotherWay._is_url_loaded(Test.AnotherWay._g_record_url, Test.AnotherWay._g_record_wnd)) {
+        Test.AnotherWay._record_window_setup(Test.AnotherWay._g_record_wnd);
+    }
+    else {
+        if (--Test.AnotherWay._g_record_timeout_cnt > 0) {
+            setTimeout(Test.AnotherWay._record_window_timeout, 100);
+        }
+        else {
+            alert("timeout expired while opening new window for a page: " + Test.AnotherWay._g_record_url);
+            Test.AnotherWay._g_record_wnd = null;
+            Test.AnotherWay._g_record_url = null;
+            Test.AnotherWay._g_record_timeout_cnt = 0;
+        }
+    }
+};
+Test.AnotherWay._record_control_randomize_id = function(e, r){
+    if (e.id != "") {
+        e.id = e.id + r;
+    }
+    for (var c = e.firstChild; c != null; c = c.nextSibling) {
+        Test.AnotherWay._record_control_randomize_id(c, r);
+    }
+};
+Test.AnotherWay._record_window_setup = function(wnd) // insert recording control into the page for which input is recorded
+{
+    Test.AnotherWay._g_record_timeout_cnt = 0;
+    var this_div = document.getElementById("record_control");
+    var record_control = wnd.document.importNode(this_div, true);
+    Test.AnotherWay._g_record_random_id = (1000 * Math.random()).toFixed();
+    Test.AnotherWay._record_control_randomize_id(record_control, Test.AnotherWay._g_record_random_id);
+    Test.AnotherWay._g_record_control_visible = true;
+    Test.AnotherWay._g_record_started = false;
+    Test.AnotherWay._g_record_paused = false;
+    Test.AnotherWay._g_record_checkpoint_count = 0;
+    Test.AnotherWay._g_record_mouse_over_record_control = false;
+    var doc = wnd.document;
+    doc.body.appendChild(record_control);
+    // opera sans-serif font is different
+    if (window.opera) {
+        cursor_over_indicator = Test.AnotherWay._record_control_get_element("record_cursor_over");
+        cursor_over_indicator.style.width = "18em";
+        cursor_over_indicator.style.height = "2em";
+        cursor_over_indicator.style.fontSize = "7pt";
+    }
+    doc.addEventListener("keydown", Test.AnotherWay._record_control_keydown, true);
+    doc.addEventListener("keyup", Test.AnotherWay._record_control_keyup, true);
+    //  doc.addEventListener( "keypress", Test.AnotherWay._record_event, true ); // replaying is not supported by any known browser
+    
+    doc.body.addEventListener("mousemove", Test.AnotherWay._record_on_mousemove, true);
+    doc.body.addEventListener("click", Test.AnotherWay._record_event, true);
+    doc.body.addEventListener("mouseover", Test.AnotherWay._record_event, true);
+    doc.body.addEventListener("mouseout", Test.AnotherWay._record_event, true);
+    doc.body.addEventListener("mousedown", Test.AnotherWay._record_event, true);
+    doc.body.addEventListener("mouseup", Test.AnotherWay._record_event, true);
+};
+Test.AnotherWay._record_control_key_disabled = function(k){
+    if (k == "c") {
+        return !Test.AnotherWay._g_record_started;
+    }
+    else 
+        if (k == "p") {
+            return !Test.AnotherWay._g_record_started;
+        }
+        else 
+            if (k == "s") {
+                return Test.AnotherWay._g_record_waiting_for_results;
+            }
+            else {
+                return false;
+            }
+};
+
+Test.AnotherWay._record_control_update_ui = function(){
+    var keydown_color = "#fff";
+    var disabled_color = "#aaa";
+    var button_color = "#adf";
+    var active_color = "#fdf";
+    
+    var display = {};
+    display[false] = "none";
+    display[true] = "inline";
+    
+    var s_button = Test.AnotherWay._record_control_get_element("record_s");
+    var record_on = Test.AnotherWay._record_control_get_element("record_on");
+    var record_off = Test.AnotherWay._record_control_get_element("record_off");
+    
+    s_button.style.backgroundColor = Test.AnotherWay._record_control_key_disabled("s") ? disabled_color : Test.AnotherWay._g_record_keydown == "s" ? keydown_color : Test.AnotherWay._g_record_started ? active_color : button_color;
+    record_on.style.display = display[!Test.AnotherWay._g_record_started];
+    record_off.style.display = display[Test.AnotherWay._g_record_started];
+    
+    var h_button = Test.AnotherWay._record_control_get_element("record_h");
+    h_button.style.backgroundColor = Test.AnotherWay._g_record_keydown == "h" ? keydown_color : button_color;
+    
+    var p_button = Test.AnotherWay._record_control_get_element("record_p");
+    var record_pause_on = Test.AnotherWay._record_control_get_element("record_pause_on");
+    var record_pause_off = Test.AnotherWay._record_control_get_element("record_pause_off");
+    p_button.style.backgroundColor = Test.AnotherWay._record_control_key_disabled("p") ? disabled_color : Test.AnotherWay._g_record_keydown == "p" ? keydown_color : Test.AnotherWay._g_record_paused ? active_color : button_color;
+    record_pause_on.style.display = display[!Test.AnotherWay._g_record_paused];
+    record_pause_off.style.display = display[Test.AnotherWay._g_record_paused];
+    
+    var m_button = Test.AnotherWay._record_control_get_element("record_m");
+    var record_include_mousemove = Test.AnotherWay._record_control_get_element("record_include_mousemove");
+    var record_omit_mousemove = Test.AnotherWay._record_control_get_element("record_omit_mousemove");
+    m_button.style.backgroundColor = Test.AnotherWay._g_record_keydown == "m" ? keydown_color : Test.AnotherWay._g_record_include_mousemove ? active_color : button_color;
+    record_include_mousemove.style.display = display[!Test.AnotherWay._g_record_include_mousemove];
+    record_omit_mousemove.style.display = display[Test.AnotherWay._g_record_include_mousemove];
+    
+    var c_button = Test.AnotherWay._record_control_get_element("record_c");
+    c_button.style.backgroundColor = Test.AnotherWay._record_control_key_disabled("c") ? disabled_color : Test.AnotherWay._g_record_keydown == "c" ? keydown_color : button_color;
+    
+    var record_indicator = Test.AnotherWay._record_control_get_element("record_indicator");
+    record_indicator.style.display = display[Test.AnotherWay._g_record_started];
+    
+    var pause_indicator = Test.AnotherWay._record_control_get_element("record_pause_indicator");
+    pause_indicator.style.display = display[Test.AnotherWay._g_record_paused];
+    
+    var record_control = Test.AnotherWay._record_control_get_element("record_control");
+    record_control.style.display = Test.AnotherWay._g_record_control_visible ? "block" : "none";
+    
+    var shift_button = Test.AnotherWay._record_control_get_element("record_shift_key");
+    shift_button.style.backgroundColor = Test.AnotherWay._g_record_shift_keydown ? keydown_color : button_color;
+    
+    var ctrl_button = Test.AnotherWay._record_control_get_element("record_ctrl_key");
+    ctrl_button.style.backgroundColor = Test.AnotherWay._g_record_ctrl_keydown ? keydown_color : button_color;
+};
+Test.AnotherWay._record_format_time = function(t){
+    t = new Date(t);
+    var m = t.getMinutes();
+    var s = t.getSeconds();
+    var str = m == 0 ? "" : m + "m ";
+    str += s + "s.";
+    return str;
+};
+Test.AnotherWay._record_control_update_time = function(){
+    var time_display = Test.AnotherWay._record_control_get_element("record_time");
+    if (time_display != null) {
+        time_display.innerHTML = Test.AnotherWay._record_format_time((new Date()).getTime() - Test.AnotherWay._g_record_start_time);
+    }
+};
+Test.AnotherWay._record_control_update_highlight = function(elem, style, event){
+    if (elem == null) {
+        Test.AnotherWay._record_highlight_border(null);
+    }
+    else {
+        var pos = Test.AnotherWay._get_page_coords(elem);
+        if (style == "ball" || elem != Test.AnotherWay._g_record_highlighted_element.element || pos.x != Test.AnotherWay._g_record_highlighted_element.x || pos.y != Test.AnotherWay._g_record_highlighted_element.y) {
+            Test.AnotherWay._g_record_highlighted_element = {
+                element: elem,
+                x: pos.x,
+                y: pos.y
+            };
+            Test.AnotherWay._record_highlight_border(elem, style, event);
+        }
+    }
+};
+Test.AnotherWay._record_decode_key = function(event){
+    var k = null;
+    if (event == null) {
+        k = Test.AnotherWay._g_record_wnd.event.keyCode;
+    }
+    else {
+        k = event.which;
+    }
+    if (k == 83) {
+        return "s";
+    }
+    else 
+        if (k == 72) {
+            return "h";
+        }
+        else 
+            if (k == 73) {
+                return "i";
+            }
+            else 
+                if (k == 80) {
+                    return "p";
+                }
+                else 
+                    if (k == 67) {
+                        return "c";
+                    }
+                    else 
+                        if (k == 77) {
+                            return "m";
+                        }
+                        else 
+                            if (k == 16) {
+                                return "shift";
+                            }
+                            else 
+                                if (k == 17) {
+                                    return "ctrl";
+                                }
+                                else 
+                                    if (k == 18) {
+                                        return "alt";
+                                    }
+                                    else 
+                                        if (k == 19) {
+                                            return "pause";
+                                        }
+                                        else 
+                                            if (k == 123) {
+                                                return "f12";
+                                            }
+    return "";
+};
+Test.AnotherWay._record_control_keydown = function(event){
+    var handled = false;
+    var k = Test.AnotherWay._record_decode_key(event);
+    if (k == "shift") {
+        Test.AnotherWay._g_record_shift_keydown = true;
+    }
+    else 
+        if (k == "ctrl") {
+            Test.AnotherWay._g_record_ctrl_keydown = true;
+        }
+        else 
+            if (k != "" && (Test.AnotherWay._g_record_keydown == null || Test.AnotherWay._g_record_keydown == k)) {
+                if (Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown && !Test.AnotherWay._record_control_key_disabled(k)) {
+                    Test.AnotherWay._g_record_keydown = k;
+                    handled = true;
+                }
+            }
+            else {
+                Test.AnotherWay._g_record_keydown = "";
+            }
+    Test.AnotherWay._record_control_update_ui();
+    if (!handled) {
+        //      Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+    }
+    return;
+};
+Test.AnotherWay._record_control_keyup = function(event){
+    var handled = false;
+    var k = Test.AnotherWay._record_decode_key(event);
+    if (k == "shift") {
+        Test.AnotherWay._g_record_shift_keydown = false;
+    }
+    else 
+        if (k == "ctrl") {
+            Test.AnotherWay._g_record_ctrl_keydown = false;
+        }
+        else 
+            if (k != "" && k == Test.AnotherWay._g_record_keydown && Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown) {
+                if (k == "s") {
+                    Test.AnotherWay._g_record_started = !Test.AnotherWay._g_record_started;
+                    if (Test.AnotherWay._g_record_started) {
+                        Test.AnotherWay._g_record_events = [];
+                        Test.AnotherWay._g_record_start_time = (new Date()).getTime();
+                        Test.AnotherWay._record_control_update_time();
+                        Test.AnotherWay._g_record_update_time_interval = window.setInterval(Test.AnotherWay._record_control_update_time, 200);
+                    }
+                    else {
+                        Test.AnotherWay._record_control_update_highlight(null);
+                        if (!Test.AnotherWay._g_record_paused) {
+                            window.clearInterval(Test.AnotherWay._g_record_update_time_interval);
+                        }
+                        Test.AnotherWay._g_record_waiting_for_results = true;
+                        // open a new window for self, pass a parameter to dump recorded events as javascript code there
+                        // (the easiest way to obtain a document from the same origin, so it's writable, is to open this same page again)
+                        Test.AnotherWay._g_record_paused = false;
+                        var loc = window.location;
+                        loc = loc.protocol + "//" + loc.host + loc.pathname + "?recording_results=" + Test.AnotherWay._g_record_random_id;
+                        if (window.open(loc, "_blank") == null) {
+                            alert("unable to open new window for results");
+                        }
+                    }
+                    handled = true;
+                }
+                else 
+                    if (k == "h") {
+                        Test.AnotherWay._g_record_control_visible = !Test.AnotherWay._g_record_control_visible;
+                        handled = true;
+                    }
+                    else 
+                        if (k == "p") {
+                            Test.AnotherWay._g_record_paused = !Test.AnotherWay._g_record_paused;
+                            if (Test.AnotherWay._g_record_paused) {
+                                Test.AnotherWay._g_record_pause_start_time = (new Date()).getTime();
+                                if (Test.AnotherWay._g_record_started) {
+                                    window.clearInterval(Test.AnotherWay._g_record_update_time_interval);
+                                }
+                                Test.AnotherWay._record_control_update_highlight(null);
+                            }
+                            else {
+                                var pause_duration = (new Date()).getTime() - Test.AnotherWay._g_record_pause_start_time;
+                                Test.AnotherWay._g_record_start_time += pause_duration;
+                                Test.AnotherWay._g_record_update_time_interval = window.setInterval(Test.AnotherWay._record_control_update_time, 200);
+                            }
+                            handled = true;
+                        }
+                        else 
+                            if (k == "m") {
+                                Test.AnotherWay._g_record_include_mousemove = !Test.AnotherWay._g_record_include_mousemove;
+                                handled = true;
+                            }
+                            else 
+                                if (k == "c") {
+                                    var o = Test.AnotherWay._record_checkpoint();
+                                    Test.AnotherWay._record_display_checkpoint(o);
+                                    Test.AnotherWay._record_flash_border("#24d");
+                                    handled = true;
+                                }
+            }
+    Test.AnotherWay._g_record_keydown = null;
+    Test.AnotherWay._record_control_update_ui();
+    if (!handled) {
+        //      Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
+    }
+    return;
+};
+Test.AnotherWay._record_html_node_path = function(node){
+    if (node == null) {
+        return null;
+    }
+    var path = [];
+    while (true) {
+        if (node.id != null && node.id != "") {
+            path.unshift("#" + node.id + " " + node.nodeName);
+            break;
+        }
+        else {
+            var parent_node = node.parentNode;
+            if (parent_node == null) {
+                return []; // no BODY up the path - this node is screwed (browsers differ in what's above the body), discard
+            }
+            else {
+                var i = 0;
+                var found = false;
+                for (var child = parent_node.firstChild; child != null; child = child.nextSibling) {
+                    if (child == node) {
+                        found = true;
+                        break;
+                    }
+                    if (child.nodeType == 1) { // count only HTML element nodes
+                        ++i;
+                    }
+                }
+                if (!found) {
+                    i = -1;
+                }
+                path.unshift(i + " " + node.nodeName);
+                if (parent_node.nodeName == "BODY" || parent_node.nodeName == "body") {
+                    break;
+                }
+                node = parent_node;
+            }
+        }
+    }
+    return path;
+};
+Test.AnotherWay._record_node_path_to_string = function(path){
+    var s = "";
+    if (path != null) {
+        for (var i = 0; i < path.length; ++i) {
+            s += i == 0 ? "" : ", ";
+            var elem = path[i].split(" ");
+            if (elem[0].charAt(0) == "#") {
+                s += elem[1] + " " + elem[0];
+            }
+            else {
+                s += elem[1] + " [" + elem[0] + "]";
+            }
+        }
+    }
+    return s;
+};
+Test.AnotherWay._record_node_path_to_node = function(path_str, doc){
+    if (path_str == null) {
+        return null;
+    }
+    var path = path_str.split(",");
+    var node = doc.body;
+    for (var i = 0; i < path.length; ++i) {
+        var node_i = path[i].split(" ")[0];
+        if (node_i.charAt(0) == "#") {
+            node = doc.getElementById(node_i.substring(1));
+        }
+        else {
+            if (node_i < 0 || node_i >= node.childNodes.length) {
+                node = null;
+            }
+            else {
+                node = node.firstChild;
+                while (node != null) {
+                    if (node.nodeType == 1) { // count only HTML element nodes
+                        if (node_i == 0) {
+                            break;
+                        }
+                        --node_i;
+                    }
+                    node = node.nextSibling;
+                }
+            }
+        }
+        if (node == null) {
+            return null;
+        }
+    }
+    return node;
+};
+Test.AnotherWay._record_control_contains_id = function(s){
+    return s.match(/^#record_[\w_]+/) && s.match(Test.AnotherWay._g_record_random_id);
+};
+Test.AnotherWay._record_checkpoint = function(){
+    var o = {
+        type: "_checkpoint",
+        time: (new Date()).getTime() - Test.AnotherWay._g_record_start_time,
+        which: Test.AnotherWay._g_record_checkpoint_count++,
+        target: Test.AnotherWay._record_html_node_path(Test.AnotherWay._g_record_under_cursor)
+    };
+    Test.AnotherWay._g_record_events.push(o);
+    return o;
+};
+Test.AnotherWay._record_event = function(event){
+    var unneeded = ["rangeOffset", "eventPhase", "timeStamp", "isTrusted", "popupWindowFeatures", "rangeOffset"];
+    if (Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused) {
+        var o = {};
+        for (var n in event) {
+            var needed = !n.match(/^[A-Z0-9_]+$/);
+            if (needed) {
+                for (var ui = 0; ui < unneeded.length; ++ui) {
+                    if (unneeded[ui] == n) {
+                        needed = false;
+                        break;
+                    }
+                }
+                if (needed) {
+                    var value = event[n];
+                    if (typeof(value) != "object" && typeof(value) != "function") {
+                        o[n] = value;
+                    }
+                    else 
+                        if (n == "target" || n == "relatedTarget") {
+                            o[n] = Test.AnotherWay._record_html_node_path(value);
+                        }
+                }
+            }
+        }
+        o["time"] = (new Date()).getTime() - Test.AnotherWay._g_record_start_time;
+        var over_record_control = o["target"] != null && o["target"][0] != null && Test.AnotherWay._record_control_contains_id(o["target"][0]);
+        if (!over_record_control) {
+            Test.AnotherWay._g_record_events.push(o);
+        }
+    }
+    return true;
+};
+Test.AnotherWay._record_on_mousemove = function(event){
+    var path = Test.AnotherWay._record_html_node_path(event.target);
+    var new_mouse_over_record_control = path != null && path[0] != null && Test.AnotherWay._record_control_contains_id(path[0]);
+    if (new_mouse_over_record_control != Test.AnotherWay._g_record_mouse_over_record_control) {
+        Test.AnotherWay._g_record_mouse_over_record_control = new_mouse_over_record_control;
+        Test.AnotherWay._record_control_update_ui();
+    }
+    if (event.target != null && event.target != Test.AnotherWay._g_record_under_cursor) {
+        Test.AnotherWay._g_record_under_cursor = event.target;
+        var s = "";
+        if (path == null || path[0] == null || !Test.AnotherWay._record_control_contains_id(path[0])) {
+            s = Test.AnotherWay._record_node_path_to_string(path);
+        }
+        if (s == "") {
+            s = "&nbsp;";
+        }
+        var cursor_over_indicator = Test.AnotherWay._record_control_get_element("record_cursor_over");
+        cursor_over_indicator.innerHTML = s;
+    }
+    
+    var highlight_element = null;
+    if (!Test.AnotherWay._g_record_mouse_over_record_control && Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused) {
+        highlight_element = event.target;
+    }
+    // highlight border disabled on recording - it causes page to scroll, issuing spurious mouseover/mouseout event
+    //Test.AnotherWay._record_control_update_highlight( highlight_element, "border" );
+    
+    if (Test.AnotherWay._g_record_include_mousemove) {
+        Test.AnotherWay._record_event(event);
+    }
+    return true;
+};
+Test.AnotherWay._record_display_checkpoint = function(o){
+    var checkpoints_div = Test.AnotherWay._record_control_get_element("record_checkpoints");
+    var p = checkpoints_div.appendChild(checkpoints_div.ownerDocument.createElement("div"));
+    p.style.marginTop = "3px";
+    p.style.font = "normal normal 8pt sans-serif";
+    p.style.color = "#000";
+    p.style.textAligh = "left";
+    p.style.position = "relative";
+    p.style.width = "100%";
+    var checkpoint_text = "";
+    checkpoint_text += "#" + (o.which + 1);
+    checkpoint_text += "  " + Test.AnotherWay._record_format_time(o.time);
+    if (o.target != null) {
+        checkpoint_text += Test.AnotherWay._record_node_path_to_string(o.target);
+    }
+    p.appendChild(p.ownerDocument.createTextNode(checkpoint_text));
+};
+Test.AnotherWay._record_save_results = function(doc){
+    // strange, but DOM-style append does not work here in opera 8.
+    var append = function(s){
+        doc.write("<div>" + s + "</div>");
+    };
+    append("/* paste this data into your javascript and pass it as an argument to replay_events method */");
+    append("{ checkpoints: [");
+    var first_checkpoint = true;
+    for (var i = 0; i < Test.AnotherWay._g_record_events.length; ++i) {
+        var o = Test.AnotherWay._g_record_events[i];
+        if (o.type == "_checkpoint") {
+            var str = first_checkpoint ? "" : "}, ";
+            str += "function( tst, wnd ) { // #" + o.which + " time " + Test.AnotherWay._record_format_time(o.time) + " cursor was over " + Test.AnotherWay._record_node_path_to_string(o.target);
+            append(str);
+            first_checkpoint = false;
+        }
+    }
+    if (!first_checkpoint) {
+        append("}");
+    }
+    append("], events: [ ");
+    var prev_time = 0;
+    for (var i = 0; i < Test.AnotherWay._g_record_events.length; ++i) {
+        var o = Test.AnotherWay._g_record_events[i];
+        var s = "";
+        s += "{";
+        var n_first = true;
+        for (var n in o) {
+            if (n == "time") { // convert to relative time
+                var cur_time = o[n] - 0;
+                o[n] = cur_time - prev_time;
+                prev_time = cur_time;
+            }
+            s += n_first ? n : ", " + n;
+            s += ":";
+            if (o[n] == null) {
+                s += "null";
+            }
+            else {
+                s += "\"" + o[n] + "\"";
+            }
+            n_first = false;
+        }
+        s += i == Test.AnotherWay._g_record_events.length - 1 ? "}" : "},";
+        append(s);
+    }
+    append("] }");
+    append(";");
+};
+
+Test.AnotherWay._g_record_border; // border highlighting element under cursor
+Test.AnotherWay._g_record_border_flashes = []; // array of { color: color, timeout: milliseconds }
+Test.AnotherWay._g_record_border_flashing = false;
+Test.AnotherWay._g_record_border_normal_color = "#d4b";
+Test.AnotherWay._record_flash_border_timeout = function(){
+    var color = Test.AnotherWay._g_record_border_normal_color;
+    var timeout = null;
+    if (Test.AnotherWay._g_record_border_flashes.length != 0) {
+        color = Test.AnotherWay._g_record_border_flashes[0].color;
+        timeout = Test.AnotherWay._g_record_border_flashes[0].timeout;
+        Test.AnotherWay._g_record_border_flashes.splice(0, 1);
+    }
+    if (Test.AnotherWay._g_record_border != null) {
+        for (var i = 0; i < Test.AnotherWay._g_record_border.length; ++i) {
+            Test.AnotherWay._g_record_border[i].style.backgroundColor = color;
+        }
+    }
+    if (timeout != null) {
+        setTimeout(Test.AnotherWay._record_flash_border_timeout, timeout);
+    }
+    else {
+        Test.AnotherWay._g_record_border_flashing = false;
+    }
+};
+Test.AnotherWay._get_page_coords = function(elm){
+    var point = {
+        x: 0,
+        y: 0
+    };
+    while (elm) {
+        point.x += elm.offsetLeft;
+        point.y += elm.offsetTop;
+        elm = elm.offsetParent;
+    }
+    return point;
+};
+Test.AnotherWay._set_page_coords = function(elm, x, y){
+    var parent_coords = {
+        x: 0,
+        y: 0
+    };
+    if (elm.offsetParent) {
+        parent_coords = Test.AnotherWay._get_page_coords(elm.offsetParent);
+    }
+    var new_x = x - parent_coords.x;
+    if (new_x < 0) {
+        new_x = 0;
+    }
+    elm.style.left = new_x + 'px';
+    var new_y = y - parent_coords.y;
+    if (new_y < 0) {
+        new_y = 0;
+    }
+    elm.style.top = new_y + 'px';
+};
+Test.AnotherWay._record_setup_highlight_positions = function(element, style, coords, positions){
+    if (style == "border") {
+        var width = element.clientWidth;
+        var height = element.clientHeight;
+        var step = 0;
+        var thickness = 2;
+        var fudge_expand = 4;
+        positions.push({
+            x: coords.x - step - thickness,
+            y: coords.y - step - thickness,
+            width: width + 2 * step + 2 * thickness + fudge_expand,
+            height: thickness
+        });
+        positions.push({
+            x: coords.x + width + step + fudge_expand,
+            y: coords.y - step - thickness,
+            width: thickness,
+            height: height + 2 * step + 2 * thickness + fudge_expand
+        });
+        positions.push({
+            x: positions[0].x,
+            y: positions[0].y,
+            width: positions[0].width,
+            height: positions[0].height
+        });
+        positions.push({
+            x: positions[1].x,
+            y: positions[1].y,
+            width: positions[1].width,
+            height: positions[1].height
+        });
+        positions[2].y += height + thickness + 2 * step + fudge_expand;
+        positions[3].x -= width + thickness + 2 * step + fudge_expand;
+    }
+    else 
+        if (style == "ball") {
+            positions.push({
+                x: coords.x + 2,
+                y: coords.y,
+                width: 2,
+                height: 6
+            });
+            positions.push({
+                x: coords.x,
+                y: coords.y + 2,
+                width: 6,
+                height: 2
+            });
+            positions.push({
+                x: coords.x + 1,
+                y: coords.y + 1,
+                width: 4,
+                height: 4
+            });
+        }
+};
+Test.AnotherWay._record_highlight_border = function(element, style, event) // null - hide border
+{
+    if (element != null) {
+        if (Test.AnotherWay._g_record_border == null || Test.AnotherWay._g_record_border[0].ownerDocument != element.ownerDocument) {
+            Test.AnotherWay._g_record_border = [];
+            var n = style == "border" ? 4 : style == "ball" ? 3 : 0;
+            for (var i = 0; i < 4; ++i) {
+                var b = element.ownerDocument.createElement("div");
+                b.style.position = "absolute";
+                b.style.zIndex = "1";
+                b.style.backgroundColor = Test.AnotherWay._g_record_border_normal_color;
+                element.ownerDocument.body.appendChild(b);
+                Test.AnotherWay._g_record_border.push(b);
+            }
+        }
+        var coords = null;
+        if (style == "border") {
+            coords = Test.AnotherWay._get_page_coords(element);
+        }
+        else 
+            if (style == "ball") {
+                if (event != null) {
+                    if (event.pageX != null && event.pageY != null) {
+                        coords = {
+                            x: event.pageX - 0,
+                            y: event.pageY - 0
+                        };
+                    }
+                    else 
+                        if (event.clientX != null && event.clientY != null) {
+                            var doc = element.ownerDocument;
+                            if (doc != null) {
+                                coords = {
+                                    x: (event.clientX - 0) + doc.body.scrollLeft,
+                                    y: (event.clientY - 0) + doc.body.scrollTop
+                                };
+                            }
+                        }
+                }
+            }
+        if (coords != null && element.clientWidth != null && element.clientHeight != null) {
+            var positions = [];
+            Test.AnotherWay._record_setup_highlight_positions(element, style, coords, positions);
+            for (var i = 0; i < positions.length; ++i) {
+                var b = Test.AnotherWay._g_record_border[i];
+                var p = positions[i];
+                Test.AnotherWay._set_page_coords(b, p.x, p.y);
+                b.style.width = p.width + "px";
+                b.style.height = p.height + "px";
+                b.style.display = "block";
+            }
+        }
+    }
+    else {
+        if (Test.AnotherWay._g_record_border != null) {
+            for (var i = 0; i < Test.AnotherWay._g_record_border.length; ++i) {
+                Test.AnotherWay._g_record_border[i].style.display = "none";
+            }
+        }
+    }
+};
+Test.AnotherWay._record_flash_border = function(color){
+    if (Test.AnotherWay._g_record_border_flashing) { //already
+        Test.AnotherWay._g_record_border_flashes.push({
+            color: Test.AnotherWay._g_record_border_normal_color,
+            timeout: 300
+        });
+        Test.AnotherWay._g_record_border_flashes.push({
+            color: color,
+            timeout: 600
+        });
+    }
+    else {
+        Test.AnotherWay._g_record_border_flashing = true;
+        Test.AnotherWay._g_record_border_flashes.push({
+            color: color,
+            timeout: 600
+        });
+        Test.AnotherWay._record_flash_border_timeout();
+    }
+};
+Test.AnotherWay._record_prepare_doc_for_results = function(){
+    document.open();
+    document.write("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
+    document.write("<html><head><title> Input recording results</title>");
+    document.write("<style type=\"text/css\">");
+    document.write("body { font: normal normal smaller sans-serif; }");
+    document.write("div { margin-top: 3px; }");
+    document.write("</style></head><body>");
+    // opera and mozilla disagree over who the opener is.
+    if (typeof(window.opener.Test) != "undefined" && typeof(window.opener.Test.AnotherWay) != "undefined") {
+        window.opener.Test.AnotherWay._record_save_results(document);
+        window.opener.Test.AnotherWay._g_record_waiting_for_results = false;
+        window.opener.Test.AnotherWay._record_control_update_ui();
+    }
+    else 
+        if (typeof(window.opener.opener.Test) != "undefined" && typeof(window.opener.opener.Test.AnotherWay) != "undefined") {
+            window.opener.opener.Test.AnotherWay._record_save_results(document);
+            window.opener.opener.Test.AnotherWay._g_record_waiting_for_results = false;
+            window.opener.opener.Test.AnotherWay._record_control_update_ui();
+        }
+    document.write("</body>");
+    document.close();
+};
+
+// global initialization
+onload = function(){
+    if (window.opera) {
+        var good_opera = typeof(window.opera.version) == "function";
+        good_opera = good_opera && window.opera.version().match(/^\s*(\d+)/);
+        good_opera = good_opera && RegExp.$1 >= 8;
+    }
+    var span = document.createElement("SPAN");
+    span.innerHTML = "<!--[if IE]><br /><![endif]-" + "->";
+    var is_ie = span.getElementsByTagName("BR").length > 0;
+    
+    Test.AnotherWay._g_test_iframe = window.frames.test_iframe;
+    
+    var query_str = window.location.search;
+    if (query_str.charAt(0) == "?") {
+        query_str = query_str.substring(1);
+    }
+    var testlist_page = "list-tests.html";
+    var auto_run = false;
+    if (query_str != "") {
+        var params = [query_str];
+        if (query_str.indexOf(";") != -1) {
+            params = query_str.split(";");
+        }
+        else 
+            if (query_str.indexOf("&") != -1) {
+                params = query_str.split("&");
+            }
+        for (var param_i = 0; param_i < params.length; ++param_i) {
+            var param = params[param_i].split("=");
+            if (param[0] == "recording_results") {
+                if (window.opener != null) {
+                    // we were told to show recording results - replace everything in the document with the results
+                    Test.AnotherWay._record_prepare_doc_for_results();
+                    return;
+                }
+            }
+            else 
+                if (param[0] == "testpage") {
+                    Test.AnotherWay._add_test_page_url(decodeURIComponent(param[1]), "anotherway");
+                }
+                else 
+                    if (param[0] == "jsantestpage") {
+                        Test.AnotherWay._add_test_page_url(decodeURIComponent(param[1]), "jsan");
+                    }
+                    else 
+                        if (param[0] == "testlist") {
+                            testlist_page = decodeURIComponent(param[1]);
+                        }
+                        else 
+                            if (param[0] == "testframe") {
+                                if (window.opera && !good_opera) {
+                                    Test.AnotherWay._show_error("testframe parameter does not work in versions of Opera prior to 8.0. Sorry (pathches are welcome).");
+                                // Opera 7 barfs on attempt to access frame.frameElement.
+                                // if someone knows a way to assign onload handler to that iframe in Opera 7
+                                // without disrupting code that works in other browsers, patches are welcome.
+                                }
+                                else {
+                                    var frame_path = param[1].split(".");
+                                    var frame = top;
+                                    for (var frame_path_i = 0; frame_path_i < frame_path.length; ++frame_path_i) {
+                                        frame = frame[frame_path[frame_path_i]];
+                                    }
+                                    if (frame == null) {
+                                        Test.AnotherWay._show_error("unable to find frame specified for loading test pages: " + param[1]);
+                                    }
+                                    else {
+                                        if (frame.frameElement != null) { // for the following assignement to onload to work, frameElement is required
+                                            frame = frame.frameElement;
+                                        }
+                                        Test.AnotherWay._g_test_iframe = frame;
+                                    }
+                                }
+                            }
+                            else 
+                                if (param[0] == "testframe_no_clear") {
+                                    Test.AnotherWay._g_test_frame_no_clear = true;
+                                }
+                                else 
+                                    if (param[0] == "windows") {
+                                        if (param[1] == "none") {
+                                            Test.AnotherWay._test_object_t.prototype.open_window = null;
+                                        }
+                                    }
+                                    else 
+                                        if (param[0] == "run") {
+                                            auto_run = true;
+                                            if (param[1] == "all") {
+                                                Test.AnotherWay._g_pages_to_run = "all";
+                                            }
+                                            else {
+                                                if (Test.AnotherWay._g_pages_to_run == null || Test.AnotherWay._g_pages_to_run == "all") {
+                                                    Test.AnotherWay._g_pages_to_run = [];
+                                                }
+                                                var pages = param[1].split(",");
+                                                for (var i = 0; i < pages.length; ++i) {
+                                                    Test.AnotherWay._g_pages_to_run.push(pages[i]);
+                                                }
+                                            }
+                                        }
+        }
+    }
+    if (Test.AnotherWay._g_test_page_urls.length == 0) { // if no individual pages were given on the command line, load the list
+        var result = Test.AnotherWay._set_iframe_location(window.frames["list_iframe"], testlist_page);
+        if (result.msg != null) {
+            Test.AnotherWay._show_error(result.msg);
+        }
+        Test.AnotherWay._g_run_on_list_load = auto_run;
+    }
+    else {
+        Test.AnotherWay._g_run_on_main_load = auto_run;
+    }
+    
+    var f = Test.AnotherWay._g_test_iframe;
+    try {
+        if (f.attachEvent != null) {
+            f.attachEvent("onload", Test.AnotherWay._test_page_onload);
+        }
+        else {
+            f.onload = Test.AnotherWay._test_page_onload;
+        }
+        if (Test.AnotherWay._g_test_iframe.nodeType != null && Test.AnotherWay._g_test_iframe.contentWindow != null) { // it's iframe element, not the iframe. we need iframe.
+            Test.AnotherWay._g_test_iframe = Test.AnotherWay._g_test_iframe.contentWindow;
+        }
+    } 
+    catch (e) {
+        // ignore stupid opera error if the frame has onload handler assigned in the inline html
+    }
+    var handlers = {
+        "run_all": {
+            "onclick": Test.AnotherWay._run_all_onclick
+        },
+        "run_selected": {
+            "onclick": Test.AnotherWay._run_selected_onclick
+        },
+        "unselect_all": {
+            "onclick": Test.AnotherWay._unselect_all_onclick
+        },
+        "record_select": {
+            "onfocus": Test.AnotherWay._record_check_onfocus
+        },
+        "record_input": {
+            "onfocus": Test.AnotherWay._record_check_onfocus
+        },
+        "record_start": {
+            "onclick": Test.AnotherWay._record_start_onclick
+        },
+        "clear_btn": {
+            "onclick": Test.AnotherWay._results_clear_onclick
+        },
+        "results_tab": {
+            "onclick": Test.AnotherWay._tab_onclick,
+            "onmouseover": Test.AnotherWay._tab_mouseover,
+            "onmouseout": Test.AnotherWay._tab_mouseout
+        },
+        "debug_tab": {
+            "onclick": Test.AnotherWay._tab_onclick,
+            "onmouseover": Test.AnotherWay._tab_mouseover,
+            "onmouseout": Test.AnotherWay._tab_mouseout
+        }
+    };
+    for (var hs in handlers) {
+        var o = document.getElementById(hs);
+        if (o != null) {
+            for (var h in handlers[hs]) {
+                o[h] = handlers[hs][h];
+            }
+        }
+        else {
+            Test.AnotherWay._show_error("unable to set " + h + " handler: id " + hs + " not found");
+        }
+    }
+    
+    if (window.opera && !good_opera) {
+        Test.AnotherWay._g_no_record_msg = "Input events recording and replaying is not available in opera versions prior to 8.0.";
+    }
+    if (is_ie) {
+        Test.AnotherWay._g_no_record_msg = "Input events recording and replaying is not available in internet explorer.";
+    }
+    if (Test.AnotherWay._g_no_record_msg != null) {
+        var no_record_p = document.getElementById("record_not_supported");
+        no_record_p.style.display = "block";
+        no_record_p.appendChild(document.createTextNode(Test.AnotherWay._g_no_record_msg));
+    }
+    
+    Test.AnotherWay._g_main_loaded = true;
+    if (Test.AnotherWay._g_run_on_main_load) {
+        Test.AnotherWay._g_run_on_main_load = false;
+        Test.AnotherWay._run_pages_to_run();
+    }
+};

Added: sandbox/marcjansen/testsuite/tests/Test.AnotherWay.xml_eq.js
===================================================================
--- sandbox/marcjansen/testsuite/tests/Test.AnotherWay.xml_eq.js	                        (rev 0)
+++ sandbox/marcjansen/testsuite/tests/Test.AnotherWay.xml_eq.js	2011-09-17 00:52:04 UTC (rev 12374)
@@ -0,0 +1,311 @@
+/**
+ * File: Test.AnotherWay.xml_eq.js 
+ * Adds a xml_eq method to AnotherWay test objects.
+ *
+ */
+
+(function() {
+
+    /**
+     * Function: createNode
+     * Given a string, try to create an XML DOM node.  Throws string messages
+     *     on failure.
+     * 
+     * Parameters:
+     * text - {String} An XML string.
+     *
+     * Returns:
+     * {DOMElement} An element node.
+     */
+    function createNode(text) {
+        
+        var index = text.indexOf('<');
+        if(index > 0) {
+            text = text.substring(index);
+        }
+        
+        var doc;
+        if(window.ActiveXObject && !this.xmldom) {
+            doc = new ActiveXObject("Microsoft.XMLDOM");
+            try {
+                doc.loadXML(text);
+            } catch(err) {
+                throw "ActiveXObject loadXML failed: " + err;
+            }
+        } else if(window.DOMParser) {
+            try {
+                doc = new DOMParser().parseFromString(text, 'text/xml');
+            } catch(err) {
+                throw "DOMParser.parseFromString failed";
+            }
+            if(doc.documentElement && doc.documentElement.nodeName == "parsererror") {
+                throw "DOMParser.parseFromString returned parsererror";
+            }
+        } else {
+            var req = new XMLHttpRequest();
+            req.open("GET", "data:text/xml;charset=utf-8," +
+                     encodeURIComponent(text), false);
+            if(req.overrideMimeType) {
+                req.overrideMimeType("text/xml");
+            }
+            req.send(null);
+            doc = req.responseXML;
+        }
+        
+        var root = doc.documentElement;
+        if(!root) {
+            throw "no documentElement";
+        }
+        return root;
+    }
+    
+    /**
+     * Function assertEqual
+     * Test two objects for equivalence (based on ==).  Throw an exception
+     *     if not equivalent.
+     * 
+     * Parameters:
+     * got - {Object}
+     * expected - {Object}
+     * msg - {String} The message to be thrown.  This message will be appended
+     *     with ": got {got} but expected {expected}" where got and expected are
+     *     replaced with string representations of the above arguments.
+     */
+    function assertEqual(got, expected, msg) {
+        if(got === undefined) {
+            got = "undefined";
+        } else if (got === null) {
+            got = "null";
+        }
+        if(expected === undefined) {
+            expected = "undefined";
+        } else if (expected === null) {
+            expected = "null";
+        }
+        if(got != expected) {
+            throw msg + ": got '" + got + "' but expected '" + expected + "'";
+        }
+    }
+    
+    /**
+     * Function assertElementNodesEqual
+     * Test two element nodes for equivalence.  Nodes are considered equivalent
+     *     if they are of the same type, have the same name, have the same
+     *     namespace prefix and uri, and if all child nodes are equivalent.
+     *     Throws a message as exception if not equivalent.
+     * 
+     * Parameters:
+     * got - {DOMElement}
+     * expected - {DOMElement}
+     * options - {Object} Optional object for configuring test options.
+     *
+     * Valid options:
+     * prefix - {Boolean} Compare element and attribute
+     *     prefixes (namespace uri always tested).  Default is false.
+     * includeWhiteSpace - {Boolean} Include whitespace only nodes when
+     *     comparing child nodes.  Default is false.
+     */
+    function assertElementNodesEqual(got, expected, options) {
+        var testPrefix = (options && options.prefix === true);
+        
+        // compare types
+        assertEqual(got.nodeType, expected.nodeType, "Node type mismatch");
+        
+        // compare names
+        var gotName = testPrefix ?
+            got.nodeName : got.nodeName.split(":").pop();
+        var expName = testPrefix ?
+            expected.nodeName : expected.nodeName.split(":").pop();
+        assertEqual(gotName, expName, "Node name mismatch");
+        
+        // for text nodes compare value
+        if(got.nodeType == 3) {
+            assertEqual(
+                got.nodeValue, expected.nodeValue, "Node value mismatch"
+            );
+        }
+        // for element type nodes compare namespace, attributes, and children
+        else if(got.nodeType == 1) {
+            
+            // test namespace alias and uri
+            if(got.prefix || expected.prefix) {
+                if(testPrefix) {
+                    assertEqual(
+                        got.prefix, expected.prefix,
+                        "Bad prefix for " + got.nodeName
+                    );
+                }
+            }
+            if(got.namespaceURI || expected.namespaceURI) {
+                assertEqual(
+                    got.namespaceURI, expected.namespaceURI,
+                    "Bad namespaceURI for " + got.nodeName
+                );
+            }
+            
+            // compare attributes - disregard xmlns given namespace handling above
+            var gotAttrLen = 0;
+            var gotAttr = {};
+            var expAttrLen = 0;
+            var expAttr = {};
+            var ga, ea, gn, en;
+            for(var i=0; i<got.attributes.length; ++i) {
+                ga = got.attributes[i];
+                if(ga.specified === undefined || ga.specified === true) {
+                    if(ga.name.split(":").shift() != "xmlns") {
+                        gn = testPrefix ? ga.name : ga.name.split(":").pop();
+                        gotAttr[gn] = ga;
+                        ++gotAttrLen;
+                    }
+                }
+            }
+            for(var i=0; i<expected.attributes.length; ++i) {
+                ea = expected.attributes[i];
+                if(ea.specified === undefined || ea.specified === true) {
+                    if(ea.name.split(":").shift() != "xmlns") {
+                        en = testPrefix ? ea.name : ea.name.split(":").pop();
+                        expAttr[en] = ea;
+                        ++expAttrLen;
+                    }
+                }
+            }
+            assertEqual(
+                gotAttrLen, expAttrLen,
+                "Attributes length mismatch for " + got.nodeName
+            );
+            var gv, ev;
+            for(var name in gotAttr) {
+                if(expAttr[name] == undefined) {
+                    throw "Attribute name " + gotAttr[name].name + " expected for element " + got.nodeName;
+                }
+                // test attribute namespace
+                assertEqual(
+                    gotAttr[name].namespaceURI, expAttr[name].namespaceURI,
+                    "Attribute namespace mismatch for element " +
+                    got.nodeName + " attribute name " + gotAttr[name].name
+                );
+                // test attribute value
+                assertEqual(
+                    gotAttr[name].value, expAttr[name].value,
+                    "Attribute value mismatch for element " + got.nodeName +
+                    " attribute name " + gotAttr[name].name
+                );
+            }
+            
+            // compare children
+            var gotChildNodes = getChildNodes(got, options);
+            var expChildNodes = getChildNodes(expected, options);
+
+            assertEqual(
+                gotChildNodes.length, expChildNodes.length,
+                "Children length mismatch for " + got.nodeName
+            );
+            for(var j=0; j<gotChildNodes.length; ++j) {
+                try {
+                    assertElementNodesEqual(
+                        gotChildNodes[j], expChildNodes[j], options
+                    );
+                } catch(err) {
+                    throw "Bad child " + j + " for element " + got.nodeName + ": " + err;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Function getChildNodes
+     * Returns the child nodes of the specified nodes. By default this method
+     *     will ignore child text nodes which are made up of whitespace content.
+     *     The 'includeWhiteSpace' option is used to control this behaviour.
+     * 
+     * Parameters:
+     * node - {DOMElement}
+     * options - {Object} Optional object for test configuration.
+     * 
+     * Valid options:
+     * includeWhiteSpace - {Boolean} Include whitespace only nodes when
+     *     comparing child nodes.  Default is false.
+     * 
+     * Returns:
+     * {Array} of {DOMElement}
+     */
+    function getChildNodes(node, options) {
+        //check whitespace
+        if (options && options.includeWhiteSpace) {
+            return node.childNodes;
+        }
+        else {
+           nodes = [];
+           for (var i = 0; i < node.childNodes.length; i++ ) {
+              var child = node.childNodes[i];
+              if (child.nodeType == 1) {
+                 //element node, add it 
+                 nodes.push(child);
+              }
+              else if (child.nodeType == 3) {
+                 //text node, add if non empty
+                 if (child.nodeValue && 
+                       child.nodeValue.replace(/^\s*(.*?)\s*$/, "$1") != "" ) { 
+
+                    nodes.push(child);
+                 }
+              }
+           }
+  
+           return nodes;
+        }
+    } 
+    
+    /**
+     * Function: Test.AnotherWay._test_object_t.xml_eq
+     * Test if two XML nodes are equivalent.  Tests for same node types, same
+     *     node names, same namespace URI, same attributes, and recursively
+     *     tests child nodes for same criteria.
+     *
+     * (code)
+     * t.xml_eq(got, expected, message);
+     * (end)
+     * 
+     * Parameters:
+     * got - {DOMElement | String} A DOM node or XML string to test.
+     * expected - {DOMElement | String} The expected DOM node or XML string.
+     * msg - {String} A message to print with test output.
+     * options - {Object} Optional object for configuring test.
+     *
+     * Valid options:
+     * prefix - {Boolean} Compare element and attribute
+     *     prefixes (namespace uri always tested).  Default is false.
+     * includeWhiteSpace - {Boolean} Include whitespace only nodes when
+     *     comparing child nodes.  Default is false.
+     */
+    var proto = Test.AnotherWay._test_object_t.prototype;
+    proto.xml_eq = function(got, expected, msg, options) {
+        // convert arguments to nodes if string
+        if(typeof got == "string") {
+            try {
+                got = createNode(got);
+            } catch(err) {
+                this.fail(msg + ": got argument could not be converted to an XML node: " + err);
+                return;
+            }
+        }
+        if(typeof expected == "string") {
+            try {
+                expected = createNode(expected);
+            } catch(err) {
+                this.fail(msg + ": expected argument could not be converted to an XML node: " + err);
+                return;
+            }
+        }
+        
+        // test nodes for equivalence
+        try {
+            assertElementNodesEqual(got, expected, options);
+            this.ok(true, msg);
+        } catch(err) {
+            this.fail(msg + ": " + err);
+        }
+    }
+    
+})();

Deleted: sandbox/marcjansen/testsuite/tests/geom_eq.js
===================================================================
--- sandbox/marcjansen/testsuite/tests/geom_eq.js	2011-09-17 00:09:27 UTC (rev 12373)
+++ sandbox/marcjansen/testsuite/tests/geom_eq.js	2011-09-17 00:52:04 UTC (rev 12374)
@@ -1,110 +0,0 @@
-/**
- * File: xml_eq.js
- * Adds a xml_eq method to AnotherWay test objects.
- *
- */
-
-(function() {
-    
-    /**
-     * Function assertEqual
-     * Test two objects for equivalence (based on ==).  Throw an exception
-     *     if not equivalent.
-     * 
-     * Parameters:
-     * got - {Object}
-     * expected - {Object}
-     * msg - {String} The message to be thrown.  This message will be appended
-     *     with ": got {got} but expected {expected}" where got and expected are
-     *     replaced with string representations of the above arguments.
-     */
-    function assertEqual(got, expected, msg) {
-        if(got === undefined) {
-            got = "undefined";
-        } else if (got === null) {
-            got = "null";
-        }
-        if(expected === undefined) {
-            expected = "undefined";
-        } else if (expected === null) {
-            expected = "null";
-        }
-        if(got != expected) {
-            throw msg + ": got '" + got + "' but expected '" + expected + "'";
-        }
-    }
-    
-    /**
-     * Function assertGeometryEqual
-     * Test two geometries for equivalence.  Geometries are considered
-     *     equivalent if they are of the same class, and given component
-     *     geometries, if all components are equivalent. Throws a message as
-     *     exception if not equivalent.
-     * 
-     * Parameters:
-     * got - {OpenLayers.Geometry}
-     * expected - {OpenLayers.Geometry}
-     * options - {Object} Optional object for configuring test options.
-     */
-    function assertGeometryEqual(got, expected, options) {
-        
-        var OpenLayers = Test.AnotherWay._g_test_iframe.OpenLayers;
-
-        // compare types
-        assertEqual(typeof got, typeof expected, "Object types mismatch");
-        
-        // compare classes
-        assertEqual(got.CLASS_NAME, expected.CLASS_NAME, "Object class mismatch");
-        
-        if(got instanceof OpenLayers.Geometry.Point) {
-            // compare points
-            assertEqual(got.x, expected.x, "x mismatch");
-            assertEqual(got.y, expected.y, "y mismatch");
-            assertEqual(got.z, expected.z, "z mismatch");
-        } else {
-            // compare components
-            assertEqual(
-                got.components.length, expected.components.length,
-                "Component length mismatch for " + got.CLASS_NAME
-            );
-            for(var i=0; i<got.components.length; ++i) {
-                try {
-                    assertGeometryEqual(
-                        got.components[i], expected.components[i], options
-                    );
-                } catch(err) {
-                    throw "Bad component " + i + " for " + got.CLASS_NAME + ": " + err;
-                }
-            }
-        }
-        return true;
-    }
-    
-    /**
-     * Function: Test.AnotherWay._test_object_t.geom_eq
-     * Test if two geometry objects are equivalent.  Tests for same geometry
-     *     class, same number of components (if any), equivalent component
-     *     geometries, and same coordinates.
-     *
-     * (code)
-     * t.geom_eq(got, expected, message);
-     * (end)
-     * 
-     * Parameters:
-     * got - {OpenLayers.Geometry} Any geometry instance.
-     * expected - {OpenLayers.Geometry} The expected geometry.
-     * msg - {String} A message to print with test output.
-     * options - {Object} Optional object for configuring test options.
-     */
-    var proto = Test.AnotherWay._test_object_t.prototype;
-    proto.geom_eq = function(got, expected, msg, options) {        
-        // test geometries for equivalence
-        try {
-            assertGeometryEqual(got, expected, options);
-            this.ok(true, msg);
-        } catch(err) {
-            this.fail(msg + ": " + err);
-        }
-    }
-    
-})();
\ No newline at end of file

Modified: sandbox/marcjansen/testsuite/tests/run-tests.html
===================================================================
--- sandbox/marcjansen/testsuite/tests/run-tests.html	2011-09-17 00:09:27 UTC (rev 12373)
+++ sandbox/marcjansen/testsuite/tests/run-tests.html	2011-09-17 00:52:04 UTC (rev 12374)
@@ -1,2419 +1,153 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
-<html><head><title> Run the testsuite</title>
-<noscript>Javascript is disabled in your browser. This page cannot be displayed correctly without Javascript. Sorry. <br/> If you want to view this page, please change your browser settings so that Javascript is enabled.</noscript>
-<!--
-Test.AnotherWay version 0.5
-
-Copyright (c) 2005 Artem Khodush, http://straytree.org
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--->
-<style type="text/css">
-* { padding: 0; margin: 0; }
-html { height: 99%; }
-body { height: 98%; font: normal normal 10pt sans-serif }
-#col1 {  float: left; width: 27em; margin: 0 0 0 1em; overflow: visible; }
-#col2 {  position: relative; height: 98%; margin: 0 0.5em 0 28em; }
-#col1_header { margin-top: 0.5em; }
-#scroller { height: 400px; overflow: auto;}
-#testtable { margin: 0 0 2em 0; width: 97%; }
-#run_buttons { margin-bottom: 4em; }
-
-#right_header { padding-top: 0.8em; }
-#results_count { float: left; }
-.active_tab 			{ float: right; padding: 0 1em 0.2em 1em; background: #0af; border: 1px solid #048; border-bottom: none; cursor: pointer; cursor: hand;
-					 position: relative; top: -0.2em; }
-.inactive_tab 			{ float: right; padding: 0 1em 0 1em; background: #9bb; color: #444; border: 1px solid #9bb; border-bottom: none; cursor: pointer; cursor: hand; }
-.inactive_mouseover_tab 	{ float: right; padding: 0 1em 0 1em; background: #9bb; color: #062; border: 1px solid #062; border-bottom: none; cursor: pointer; cursor: hand; }
-
-#right_frame { overflow: auto; position: relative; top: -0.2em; clear: right; height: 95%; border: 1px solid #048; }
-
-#debug { display: none; }
-#debug p { margin: 2px 0 0 5em; text-indent: -4.8em; }
-
-#error { display: none; color: #c22; }
-
-#results p { margin: 0 0 2px 0; }
-/* cursor indicating that detailed results may be expanded/contracted */
-#results p.badtest { cursor: text; }
-#results p.ok, #results p.fail { cursor: pointer; cursor: hand; }
-
-/* colored squares in the results window at the left of test page names */
-#results p.ok .bullet { background: #6d6; }
-#results p.fail .bullet { background:  #d46; }
-#results p.badtest .bullet { background: #ea3; }
-#results p.loading .bullet { background: #48f; }
-#results p.running .bullet { background: #26e; }
-#results p.waiting .bullet { background: #04d; }
-/* highlight in the results line */
-#results p .warning { background: #ffc; }
-
-/* layout of the detailed results */
-.result_detail { padding-left: 3em; }
-.result_exception_detail { padding-left: 4em; }
-.result_exception_stack_detail { padding-left: 5em;  }
-.result_micro_detail { padding-left: 6em; }
-/* colouring in the detailed results */
-.result_detail .fail, .result_exception_detail .fail,  .result_micro_detail .fail { background: #ffd8d8; }
-
-/* "start recording" controls*/
-#record_div { margin-top: 3em; }
-#record_div p { margin-bottom: 0.5em; }
-#record_select { width: 88%; }
-#record_input { width: 53%; }
-</style>
-<script type="text/javascript">
-<!--
-if( typeof( Test )=="undefined" ) {
-	Test={};
-}
-Test.AnotherWay={};
-
-Test.AnotherWay._g_test_iframe=null; // frame where to load test pages
-Test.AnotherWay._g_test_frame_no_clear=false; // true - leave last page displayed after tests end
-Test.AnotherWay._g_test_page_urls=[]; // array of: { url: url, convention: "anotherway" or "jsan" }
-Test.AnotherWay._g_test_object_for_jsan=null; // test object for filling by tests that adhere to jsan Test.Simple calling convention
-Test.AnotherWay._g_pages_to_run=null; // list of pages to run automatically after loading
-Test.AnotherWay._g_run_on_main_load=false; // special handling for run_pages_to_run when it might be called before onload or before list of test pages is known.
-Test.AnotherWay._g_run_on_list_load=false;
-Test.AnotherWay._g_main_loaded=false;
-
-Test.AnotherWay._run_pages_to_run=function( called_from_outside )
-{
-	if( !Test.AnotherWay._g_main_loaded ) {
-		Test.AnotherWay._g_run_on_main_load=true;
-	}else {
-		var a_pages=Test.AnotherWay._g_pages_to_run;
-		if( a_pages=="all" ) {
-			for( var i=0; i<Test.AnotherWay._g_test_page_urls.length; ++i ) {
-				Test.AnotherWay._run_test_page( "test"+i );
-			}
-		}else if( a_pages!=null ) {
-			for( var run_i=0; run_i<a_pages.length; ++run_i ) {
-				var run_page=a_pages[run_i];
-				var found=false;
-				for( var all_i=0; all_i<Test.AnotherWay._g_test_page_urls.length; ++all_i ) {
-					if( run_page==Test.AnotherWay._g_test_page_urls[all_i].url ) {
-						Test.AnotherWay._run_test_page( "test"+all_i, called_from_outside );
-						found=true;
-						break;
-					}
-				}
-				if( !found ) {
-					Test.AnotherWay._show_error( "page specified to run is not found in the page list: "+run_page );
-					break;
-				}
-			}
-		}
-	}
-}
-
-Test.AnotherWay._add_test_page_url=function( test_url, convention )
-{
-	var table=document.getElementById( "testtable" );
-	var record_select=document.getElementById( "record_select" );
-	var index=Test.AnotherWay._g_test_page_urls.length;
-
-	// trim spaces.
-	if( test_url.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
-		test_url=RegExp.$2;
-	}
-
-	Test.AnotherWay._g_test_page_urls[index]={ url: test_url, convention: convention };
-	var row=table.insertRow( -1 );
-
-	var cell;
-	var cell_child;
-	cell=row.insertCell( -1 );
-	cell_child=document.createElement( "input" );
-	cell_child.type="checkbox";
-	cell_child.id="checkbox"+index;
-    cell_child.checked='checked';
-    cell_child.defaultChecked='checked';
-	cell.appendChild( cell_child );
-
-	cell=row.insertCell( -1 );
-	cell.setAttribute( "width", "75%" );
-	cell.appendChild( document.createTextNode( test_url ) );
-
-	cell=row.insertCell( -1 );
-	cell_child=document.createElement( "input" );
-	cell_child.type="button";
-	cell_child.id="test"+index;
-	cell_child.value=" run ";
-	cell_child.onclick=Test.AnotherWay._run_one_onclick;
-	cell.appendChild( cell_child );
-
-	cell=row.insertCell( -1 );
-	cell.setAttribute( "width", "8em" );
-	cell_child=document.createElement( "span" );
-	cell.appendChild( cell_child );
-
-	var option=document.createElement( "option" );
-	option.appendChild( document.createTextNode( test_url ) );
-	record_select.appendChild( option );
-}
-Test.AnotherWay._show_error=function( msg )
-{
-	var error_div=document.getElementById( "error" );
-	error_div.innerHTML="";
-	error_div.appendChild( document.createTextNode( msg ) );
-	error_div.style.display="block";
-}
-
-// read urls from the list in the html file inside the list_iframe
-// fill on-screen list with urls and "run" buttons, and fill the g_test_page_urls object.
-Test.AnotherWay._list_iframe_onload=function()
-{
-	if( window.frames.list_iframe!=null && window.frames.list_iframe.location!="" && window.frames.list_iframe.location!="about:blank" ) {
-		var list_doc=window.frames.list_iframe.document;
-		var list=list_doc.getElementById( "testlist" );
-		if( list!=null ) {
-			for( var i=0; i<list.childNodes.length; ++i ) {
-				var item=list.childNodes[i];
-				if( item.nodeName=="LI" || item.nodeName=="li" ) {
-					var convention="anotherway";
-					if( Test.AnotherWay._get_css_class( item )=="jsan" ) {
-						convention="jsan";
-					}
-					Test.AnotherWay._add_test_page_url( item.innerHTML, convention );
-				}
-			}
-			if( Test.AnotherWay._g_run_on_list_load ) {
-				Test.AnotherWay._g_run_on_list_load=false;
-				Test.AnotherWay._run_pages_to_run();
-			}
-		}else {
-			Test.AnotherWay._show_error( "no list with id 'testlist' in a list file "+window.frames.list_iframe.location );
-		}
-	}
-}
-
-Test.AnotherWay._map_checkboxes=function( f )
-{
-	var table=document.getElementById( "testtable" );
-	var checks=table.getElementsByTagName( "INPUT" );
-	for( var i=0; i<checks.length; ++i ) {
-		if( checks[i].type=="checkbox" && checks[i].id.match( /^checkbox(\d+)$/ ) ) {
-			f( checks[i], RegExp.$1 );
-		}
-	}
-}
-Test.AnotherWay._run_all_onclick=function()
-{
-	Test.AnotherWay._map_checkboxes( function( c, id ) { Test.AnotherWay._run_test_page( "test"+id ); } );
-}
-Test.AnotherWay._run_selected_onclick=function()
-{
-	Test.AnotherWay._map_checkboxes( function( c, id ) { if( c.checked ) Test.AnotherWay._run_test_page( "test"+id ); } );
-}
-Test.AnotherWay._unselect_all_onclick=function()
-{
-	Test.AnotherWay._map_checkboxes( function( c, id ) { c.checked=false; } );
-}
-Test.AnotherWay._run_one_onclick=function()
-{
-	Test.AnotherWay._run_test_page( this.id );
-}
-
-// construct an object that will gather results of running one test function
-Test.AnotherWay._test_object_t=function( fun_name )
-{
-	this.name=fun_name; // name of the test function
-	this.n_plan=null; // planned number of assertions
-	this.n_ok=0; // # of ok assertions
-	this.n_fail=0; // # of failed assertions
-	this.exception=""; // if the function throwed an exception, it's its message
-	this.exception_stack=[]; // strings: function call stack from the exception
-	this.assertions=[]; // assertion results: array of { ok: 1 or 0, name: string }
-	this.wait_result_milliseconds=0; // how long to wait before collecting results from the test
-	this.second_wait_msg=null; // <p> status message (in addition to the page wait_msg)
-	this.delay_actions=[]; // array of actions to be perfomed after the test function returns
-				//	action : { acton_kind: "call" | "window" | "replay"
-				//				when "call": 		{ call_fn call_delay_milliseconds } call_fn takes nothing
-				//				when "window" : 	{ wnd_url wnd_wnd wnd_fn wnd_timeout_milliseconds wnd_dont_close } wnd_fn takes wnd
-				//				wnen "replay" : 	{ replay_wnd replay_events replay_event_i replay_checkpoints } checkpoint_fn takes this, wnd
-				//	}
-	this.delay_action_i=null; // index of delay action currently being performed
-	this.delay_prev_timer_time=0;	// for counting time while performing delay_actions
-	this.delay_current_milliseconds_left=0; // time left before the next action, runs down
-	this.delay_total_milliseconds_left=0; 	// for indication: total estimated time for all actions, runs up and down
-}
-
-Test.AnotherWay._test_object_t.prototype.ok=function( cond, name )
-{
-	if( cond ) {
-		++this.n_ok;
-		cond=1;
-	}else {
-		++this.n_fail;
-		cond=0;
-	}
-	this.assertions.push( { ok: cond, name: name } );
-}
-Test.AnotherWay._test_object_t.prototype.fail=function( name )
-{
-	this.ok( false, name );
-}
-Test.AnotherWay._test_object_t.prototype.plan=function( n )
-{
-	this.n_plan=n;
-}
-Test.AnotherWay._test_object_t.prototype.wait_result=function( seconds )
-{
-	this.wait_result_milliseconds=1000*seconds;
-}
-Test.AnotherWay._eq_fail_msg=function( path, what, expected, got )
-{
-	return "eq: "+path+" "+what+" differ: got "+got+", but expected "+expected;
-}
-Test.AnotherWay._array_eq=function( expected, got, path, msg )
-{
-	if( expected.length!=got.length ) {
-		msg.msg=Test.AnotherWay._eq_fail_msg( path, "array length", expected.length, got.length );
-		return false;
-	}
-	for( var i=0; i<expected.length; ++i ) {
-		if( !Test.AnotherWay._thing_eq( expected[i], got[i], path+"["+i+"]", msg ) ) {
-			return false;
-		}
-	}
-	return true;
-}
-Test.AnotherWay._object_eq=function( expected, got, path, msg )
-{
-	var v;
-	for( v in expected ) {
-		if( ! (v in got) ) {
-			msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", expected[v], "undefined" );
-			return false;
-		}
-		if( !Test.AnotherWay._thing_eq( expected[v], got[v], path+"."+v, msg ) ) {
-			return false;
-		}
-	}
-	for( v in got ) {
-		if( ! (v in expected) ) {
-			msg.msg=Test.AnotherWay._eq_fail_msg( path+"."+v, "properties", "undefined", got[v] );
-			return false;
-		}
-	}
-	return true;
-}
-Test.AnotherWay._constructor_name=function( x )
-{
-	if( x==null ) {
-		return "";
-	}
-	var s="unknown";
-	try {
-		s=typeof( x.constructor );
-		if( s!="unknown" ) {
-			s=x.constructor.toString();
-		}
-	}catch( e ) {
-		s="unknown";
-	}
-	if( s=="unknown" ) {
-		// hackish attempt to guess a type
-		var is_array=true;
-		var index=0;
-		for( i in x ) {
-			if( i!=index ) {
-				is_array=false;
-			}
-			++index;
-		}
-		return is_array ? "Array" : "Object"; // for empty arrays/objects, this will be wrong half the time
-	}else if( s.match( /^\s*function\s+(\w+)\s*\(/ ) ) {
-		return RegExp.$1;
-	}else {
-	  var c = '';
-	  switch(typeof x) {
-	    case 'string':
-	      c = 'String';
-	      break;
-	    case 'object':
-	      c = 'Object';
-	      break;
-	    default:
-	      c = '';
-	  }
-		return c;
-	}
-}
-Test.AnotherWay._is_array=function( x )
-{
-	return Test.AnotherWay._constructor_name( x )=="Array";
-}
-Test.AnotherWay._is_value_type=function( x )
-{
-	cn=Test.AnotherWay._constructor_name( x );
-	return cn=="Number" || cn=="String" || cn=="Boolean" || cn=="Date";
-}
-Test.AnotherWay._thing_eq=function( expected, got, path, msg )
-{
-	if( expected==null && got==null ) {
-		return true;
-	}else if( (expected==null && got!=null) || (expected!=null && got==null) ) {
-		msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
-		return false;
-	}else {
-		var expected_cn=Test.AnotherWay._constructor_name( expected );
-		var got_cn=Test.AnotherWay._constructor_name( got );
-		if( expected_cn!=got_cn ) {
-			msg.msg=Test.AnotherWay._eq_fail_msg( path, "types", expected_cn, got_cn );
-			return false;
-		}else {
-			if( Test.AnotherWay._is_array( expected ) ) {
-				return Test.AnotherWay._array_eq( expected, got, path, msg );
-			}else if( Test.AnotherWay._is_value_type( expected ) ) {
-				if( expected!=got ) {
-					msg.msg=Test.AnotherWay._eq_fail_msg( path, "values", expected, got );
-					return false;
-				}else {
-					return true;
-				}
-			}else { // just a plain object
-				return Test.AnotherWay._object_eq( expected, got, path, msg );
-			}
-		}
-	}
-}
-Test.AnotherWay._test_object_t.prototype.eq=function( got, expected, name )
-{
-	var msg={};
-	if( Test.AnotherWay._thing_eq( expected, got, "", msg ) ) {
-		this.ok( 1, name );
-	}else {
-		this.fail( name+". "+msg.msg );
-	}
-}
-Test.AnotherWay._test_object_t.prototype.like=function( got, expected, name )
-{
-	if( got.match( expected )!=null ) {
-		this.ok( 1, name );
-	}else {
-		this.fail( name+": got "+got+", but expected it to match: "+expected );
-	}
-}
-Test.AnotherWay._g_html_eq_span=null;
-Test.AnotherWay._html_eq_string_to_node=function( string_or_node, what, msg )
-{
-	if( string_or_node.nodeType!=null ) {
-		string_or_node=Test.AnotherWay._html_eq_node_to_string( string_or_node ); // double trip - to make properties assigned in scripts available as html node attributes
-	}
-	if( Test.AnotherWay._g_html_eq_span==null ) {
-		Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
-	}
-	Test.AnotherWay._g_html_eq_span.innerHTML=string_or_node;
-	if( Test.AnotherWay._g_html_eq_span.childNodes.length!=1 ) {
-		msg.msg="bad "+what+" html string given (should contain exactly one outermost element): "+string_or_node;
-	}
-	return Test.AnotherWay._g_html_eq_span.childNodes[0].cloneNode( true );
-}
-Test.AnotherWay._html_eq_node_to_string=function( node ) {
-	if( Test.AnotherWay._g_html_eq_span==null ) {
-		Test.AnotherWay._g_html_eq_span=document.createElement( "span" );
-	}
-	Test.AnotherWay._g_html_eq_span.innerHTML="";
-	if( node.outerHTML!=null ) {
-		Test.AnotherWay._g_html_eq_span.innerHTML=node.outerHTML;
-	}else {
-            var clone = node.cloneNode(true);
-            var node = Test.AnotherWay._g_html_eq_span;
-            if(node.ownerDocument && node.ownerDocument.importNode) {
-                if(node.ownerDocument != clone.ownerDocument) {
-                    clone = node.ownerDocument.importNode(clone, true);
-                }
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Run the testsuite</title>
+        <noscript>
+            Javascript is disabled in your browser. This page cannot be 
+            displayed correctly without Javascript. Sorry. 
+            <br/>
+            If you want to view this page, please change your browser settings
+            so that Javascript is enabled.
+        </noscript>
+        <!--
+        
+        Test.AnotherWay version 0.5
+        
+        Copyright (c) 2005 Artem Khodush, http://straytree.org
+        
+        Permission is hereby granted, free of charge, to any person obtaining
+        a copy of this software and associated documentation files (the
+        "Software"), to deal in the Software without restriction, including
+        without limitation the rights to use, copy, modify, merge, publish,
+        distribute, sublicense, and/or sell copies of the Software, and to
+        permit persons to whom the Software is furnished to do so, subject to
+        the following conditions:
+        
+        The above copyright notice and this permission notice shall be
+        included in all copies or substantial portions of the Software.
+        
+        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+        EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+        MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+        NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+        LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+        OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+        WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+        
+        -->
+        <link rel="stylesheet" href="Test.AnotherWay.css" />
+        <script type="text/javascript" src="Test.AnotherWay.js"></script>
+        <script type="text/javascript" src="Test.AnotherWay.baseadditions.js"></script>
+        <script type="text/javascript" src="Test.AnotherWay.xml_eq.js"></script>
+        <script type="text/javascript" src="Test.AnotherWay.geom_eq.js"></script>
+    </head>
+    <body>
+        <div id="col1">
+            <div id="col1_header">
+                Test pages:
+                <input id="quickfilter" placeholder="quick filter">
+            </div>
+            <div id="scroller">
+                <table id="testtable">
+                </table>
+            </div>
+            <div id="run_buttons">
+                <input type="button" value=" clear " id="clear_btn" /><input type="button" value=" run all " id="run_all" /><input type="button" value=" run selected " id="run_selected" /><input type="button" value=" unselect all " id="unselect_all" />
+            </div>
+            <div id="running-time">
+            </div>
+            <input type="checkbox" id="dont_close_test_windows" /> do not close windows opened by tests
+            <div id="error">
+            </div>
+            <div id="record_div">
+                <p id="record_not_supported" style="display:none">
+                </p>
+                <p>
+                    Record mouse input for the page:
+                </p>
+                <p>
+                    <input type="radio" name="record_choose" value="select" checked="checked" />
+                    <select id="record_select">
+                        <option selected="selected">-- select a page: --</option>
+                    </select>
+                </p>
+                <p>
+                    <input type="radio" name="record_choose" value="input" /> or enter page url: <input type="text" id="record_input" />
+                </p>
+                <p>
+                    <input type="button" value=" record " id="record_start" />
+                </p>
+            </div>
+        </div>
+        <div id="col2">
+            <div id="right_header">
+                <span id="results_count">Results: <span id="total"></span></span>
+                <span id="results_tab" class="active_tab" style="visibility:hidden">Results</span>
+                <span id="debug_tab" class="inactive_tab" style="visibility:hidden">Debug</span>
+            </div>
+            <div id="right_frame">
+                <div id="results">
+                </div>
+                <div id="debug">
+                </div>
+            </div>
+        </div>
+        <span style="display:none">
+            <iframe name="list_iframe" onload="Test.AnotherWay._list_iframe_onload();">
+            </iframe>
+            <iframe name="test_iframe" onload="Test.AnotherWay._test_page_onload();">
+            </iframe>
+            <!-- record_control div is to be imported into other documents, so all its styles are inline -->-
+            <div id="record_control" style="position:absolute;bottom:0;left:0;margin:0;padding:0.5em;width:22em;height:22em;border:1px solid;background:#ffd;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+                <p style="margin:0 0 0 0; padding:0">
+                    &nbsp;<span style="display:none;font-weight:bold;color:#408" id="record_indicator">recording. <span style="font-weight:normal">time: <span id="record_time"></span></span><span id="record_pause_indicator">paused</span></span>
+                </p>
+                <div id="record_cursor_over" style="margin:0;padding:2px;width:14em;height:1.1em;overflow:hidden;float:right;border:1px solid #777;background:#fff;font: normal normal 8pt sans-serif;position:relative;top:3px;color:#000;text-align:left;">
+                    &nbsp;
+                </div>
+                <p style="margin:2px 0 0 0; padding:0">
+                    cursor is over
+                </p>
+                <p style="margin:8px 0 0 0; padding:0;">
+                    keyboard control: press<span id="record_ctrl_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">ctrl</span>
+                    -<span id="record_shift_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">shift</span>
+                    -
+                </p>
+                <p style="margin:4px 0 0 0; padding:0">
+                    <span id="record_s" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">s</span>
+                    <span id="record_on">to <b>start</b> recording</span>
+                    <span id="record_off" style="display:none">to <b>stop</b> recording</span>
+                </p>
+                <p style="margin:4px 0 0 0; padding:0">
+                    <span id="record_h" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">h</span>
+                    <span>to <b>hide/show</b> this window</span>
+                </p>
+                <p style="margin:4px 0 0 0; padding:0">
+                    <span id="record_m" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">m</span>
+                    <span id="record_include_mousemove">to <b>record</b> mousemove</span>
+                    <span id="record_omit_mousemove" style="display:none">to <b>omit</b> mousemove</span>
+                </p>
+                <p style="margin:4px 0 0 0; padding:0">
+                    <span id="record_p" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">p</span>
+                    <span id="record_pause_on">to <b>pause</b> recording</span>
+                    <span id="record_pause_off" style="display:none">to <b>continue</b> recording</span>
+                </p>
+                <p style="margin:4px 0 0 0; padding:0">
+                    <span id="record_c" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">c</span>
+                    <span>to add checkpoint</span>
+                </p>
+                <p style="margin:6px 0 0 0; padding:0">
+                    checkpoints:
+                </p>
+                <div id="record_checkpoints" style="position:relative;width:100%;height:6em;overflow:auto;font: normal normal 8pt sans-serif; color:#000; text-align: left">
+                </div>
+            </div>
+        </span>
+        <script>
+            if (/noscroll/.test(location.href)) {
+                document.getElementById('scroller').style.height = 'auto';
+                document.getElementById('right_frame').style.height = 'auto';
             }
-            node.appendChild(clone);
-	}
-	return Test.AnotherWay._g_html_eq_span.innerHTML;
-}
-Test.AnotherWay._html_eq_path_msg=function( path )
-{
-	var msg="";
-	for( var i=0; i<path.length; ++i ) {
-		msg+=" [node "+path[i].node;
-		if( path[i].id!=null && path[i].id!="" ) {
-			msg+=" id "+path[i].id;
-		}else if( path[i].index!=null ) {
-			msg+=" at index "+path[i].index;
-		}
-		msg+="] "
-	}
-	return msg;
-}
-Test.AnotherWay._html_eq_fail_msg=function( path, what, expected, got )
-{
-	return Test.AnotherWay._html_eq_path_msg( path )+": "+what+" differ: got "+got+", but expected "+expected;
-}
-Test.AnotherWay._html_eq_remove_blank=function( text )
-{
-	if( text==null ) {
-		return "";
-	}else if( text.match( "^(\\s*)(.*\\S)(\\s*)$" ) ) {
-		return RegExp.$2;
-	}else if( text.match( "\s*" ) ) {
-		return "";
-	}
-	return text;
-}
-Test.AnotherWay._html_eq_remove_blank_nodes=function( node )
-{
-	var to_remove=[];
-	for( var child=node.firstChild; child!=null; child=child.nextSibling ) {
-		if( child.nodeType==3 ) {
-			var value=Test.AnotherWay._html_eq_remove_blank( child.nodeValue );
-			if( value=="" ) {
-				to_remove.push( child );
-			}else {
-				child.nodeValue=value;
-			}
-		}
-	}
-	for( var i=0; i<to_remove.length; ++i ) {
-		node.removeChild( to_remove[i] );
-	}
-}
-Test.AnotherWay._html_node_type_text=function( node_type )
-{
-	if( node_type==1 ) {
-		return "1 (html element)";
-	}else if( node_type==3 ) {
-		return "3 (text)";
-	}else {
-		return node_type;
-	}
-}
-Test.AnotherWay._html_eq_node=function( expected, got, path, msg, expected_loc_base, got_loc_base )
-{
-	if( expected.nodeType!=got.nodeType ) {
-		msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node types", Test.AnotherWay._html_node_type_text( expected.nodeType ), Test.AnotherWay._html_node_type_text( got.nodeType ) );
-		return false;
-	}else if( expected.nodeType==3 ) {
-		if( expected.nodeValue!=got.nodeValue ) {
-			msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "text", expected.nodeValue, got.nodeValue );
-			return false;
-		}
-	}else if( expected.nodeType==1 ) {
-		if( expected.nodeName!=got.nodeName ) {
-			msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "node names", expected.nodeName, got.nodeName );
-			return false;
-		}
-		// compare attributes
-		var expected_attrs={};
-		var got_attrs={};
-		var i;
-		var a;
-		for( i=0; i<expected.attributes.length; ++i ) {
-			a=expected.attributes[i];
-			if( a.specified ) {
-				expected_attrs[a.name]=1;
-			}
-		}
-		for( i=0; i<got.attributes.length; ++i ) {
-			a=got.attributes[i];
-			if( a.specified ) {
-				got_attrs[a.name]=1;
-			}
-		}
-		for( a in expected_attrs ) {
-			if( ! (a in got_attrs) ) {
-				msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: expected attribute "+a+" is missing";
-				return false;
-			}
-		}
-		for( a in got_attrs ) {
-			if( ! (a in expected_attrs) ) {
-				msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute sets differ: got extra attribute "+a;
-				return false;
-			}
-		}
-		for( a in expected_attrs ) {
-			var expected_value=expected.getAttribute( a );
-			var got_value=got.getAttribute( a );
-			if( typeof( expected_value )=="string" && typeof( got_value )=="string" ) {
-				expected_value=Test.AnotherWay._html_eq_remove_blank( expected_value );
-				got_value=Test.AnotherWay._html_eq_remove_blank( got_value );
-				var ok=expected_value==got_value;
-				if( !ok && (a=="href" || a=="HREF" )  ) { // try relative hrefs
-					var expected_relative_value=expected_value;
-					if( expected_loc_base!=null && expected_value.substring( 0, expected_loc_base.length )==expected_loc_base ) {
-						expected_relative_value=expected_value.substring( expected_loc_base.length );
-					}
-					var got_relative_value=got_value;
-					if( got_loc_base!=null && got_value.substring( 0, got_loc_base.length )==got_loc_base ) {
-						got_relative_value=got_value.substring( got_loc_base.length );
-					}
-					ok=expected_relative_value==got_relative_value;
-				}
-				if( !ok ) {
-					msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
-					return false;
-				}
-			}else if( typeof( expected_value )=="function" && typeof( got_value )=="function" ) {
-				expected_value=expected_value.toString();
-				got_value=got_value.toString();
-				if( expected_value!=got_value ) {
-					msg.msg=Test.AnotherWay._html_eq_fail_msg( path, "attribute "+a+" values", expected_value, got_value );
-					return false;
-				}
-			}else {
-				var value_msg={};
-				if( !Test.AnotherWay._thing_eq( expected_value, got_value, "", value_msg ) ) {
-					msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": attribute "+a+" values differ: "+value_msg.msg;
-					return false;
-				}
-			}
-		}
-		// compare child nodes
-		Test.AnotherWay._html_eq_remove_blank_nodes( expected );
-		Test.AnotherWay._html_eq_remove_blank_nodes( got );
-		var expected_length=expected.childNodes.length;
-		var got_length=got.childNodes.length;
-		if( expected_length<got_length ) {
-			msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": got "+(got_length-expected_length)+" extra child nodes";
-			return false;
-		}else if( expected_length>got_length ) {
-			msg.msg=Test.AnotherWay._html_eq_path_msg( path )+": expected "+(expected_length-got_length)+" more child nodes";
-			return false;
-		}else {
-			for( i=0; i<expected_length; ++i ) {
-				var expected_node=expected.childNodes[i];
-				path.push( { node: expected_node.nodeName, id: expected_node.id, index: i } );
-				var eq=Test.AnotherWay._html_eq_node( expected_node, got.childNodes[i], path, msg, expected_loc_base, got_loc_base );
-				path.pop();
-				if( !eq ) {
-					return false;
-				}
-			}
-		}
-	}
-	return true;
-}
-Test.AnotherWay._html_eq_get_loc_base=function( node )
-{
-	var loc_base=document.location;
-	if( node.ownerDocument!=null ) {
-		loc_base=node.ownerDocument.location;
-	}
-	if( loc_base!=null ) {
-		loc_base=loc_base.href;
-		var slash_pos=loc_base.lastIndexOf( "/" );
-		if( slash_pos!=-1 ) {
-			loc_base=loc_base.substring( 0, slash_pos+1 );
-		}
-	}
-	return loc_base;
-}
-Test.AnotherWay._test_object_t.prototype.html_eq=function( got, expected, name )
-{
-	var msg={};
-	var expected_node=Test.AnotherWay._html_eq_string_to_node( expected, "expected", msg );
-	if( msg.msg!=null ) {
-		this.fail( name+" html_eq: "+msg.msg );
-	}else {
-		var got_node=Test.AnotherWay._html_eq_string_to_node( got, "got", msg );
-		if( msg.msg!=null ) {
-			this.fail( name+" html_eq: "+msg.msg );
-		}else {
-			var expected_loc_base=Test.AnotherWay._html_eq_get_loc_base( expected );
-			var got_loc_base=Test.AnotherWay._html_eq_get_loc_base( got );
-			if( Test.AnotherWay._html_eq_node( expected_node, got_node, [], msg, expected_loc_base, got_loc_base ) ) {
-				this.ok( 1, name );
-			}else {
-				var msg=name+" html_eq "+msg.msg;
-				var expected_str=Test.AnotherWay._html_eq_node_to_string( expected_node );
-				var got_str=Test.AnotherWay._html_eq_node_to_string( got_node );
-				msg+=".\n got html: "+got_str;
-				msg+=".\n expected html: "+expected_str;
-				this.fail( msg );
-			}
-		}
-	}
-}
-Test.AnotherWay._debug_pane_print=function( msg )
-{
-	var d=new Date();
-	var p=document.createElement( "p" );
-	p.appendChild( document.createTextNode( d.toLocaleTimeString()+" "+msg ) );
-	var debug_pane=document.getElementById( "debug" );
-	debug_pane.appendChild( p );
-	var debug_tab=document.getElementById( "debug_tab" );
-	var results_tab=document.getElementById( "results_tab" );
-	debug_tab.style.visibility="visible";
-	results_tab.style.visibility="visible";
-}
-Test.AnotherWay._test_object_t.prototype.debug_print=function( msg )
-{
-	Test.AnotherWay._debug_pane_print( this.name+": "+msg );
-}
-Test.AnotherWay._test_object_t.prototype.delay_call=function()
-{
-	var timeout_ms=200;
-	for( var i=0; i<arguments.length; ++i ) {
-		if( typeof( arguments[i] )!="function" ) {
-			timeout_ms=1000*arguments[i];
-		}else {
-			var action={ action_kind: "call", call_delay_milliseconds: timeout_ms, call_fn: arguments[i] };
-			this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
-			this.delay_actions.push( action );
-		}
-	}
-}
-Test.AnotherWay._test_object_t.prototype.open_window=function( url, fn, timeout_seconds )
-{
-	if( timeout_seconds==null ) {
-		timeout_seconds=4;
-	}
-	var no_close=document.getElementById( "dont_close_test_windows" );
-	var action={ action_kind: "window", wnd_url: url.toString() + (window.location.search || ""), wnd_wnd: null, wnd_fn: fn, wnd_timeout_milliseconds: timeout_seconds*1000, wnd_no_close: no_close.checked };
-	this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
-	this.delay_actions.push( action );
-}
-Test.AnotherWay._test_object_t.prototype.replay_events=function( wnd, events )
-{
-	if( Test.AnotherWay._g_no_record_msg!=null ) {
-		this.fail( "replay_events: "+Test.AnotherWay._g_no_record_msg );
-	}else {
-		var action={ action_kind: "replay", replay_wnd: wnd, replay_events: events.events, replay_event_i: null, replay_checkpoints: events.checkpoints };
-		this.delay_total_milliseconds_left+=Test.AnotherWay._action_estimate_milliseconds( action );
-		this.delay_actions.push( action );
-	}
-}
-Test.AnotherWay._action_estimate_milliseconds=function( action )
-{
-	var ms=0;
-	if( action.action_kind=="call" ) {
-		ms=action.call_delay_milliseconds;
-	}else if( action.action_kind=="window" ) {
-		ms=0;
-	}else if( action.action_kind=="replay" ) {
-		ms=0;
-		for( var i=0; i<action.replay_events.length; ++i ) {
-			ms+=action.replay_events[i]["time"]-0;
-		}
-	}
-	return ms;
-}
-
-Test.AnotherWay._g_timeout_granularity=200;
-Test.AnotherWay._g_tests_queue=[]; // vector of { url: string, test_objects : array of test_object_t, test_object_i: int, wait_msg: <p> object, loading_timeout_milliseconds: int, timeout_id: id }
-
-// load one html page, schedule further processing
-Test.AnotherWay._run_test_page=function( id, called_from_outside )
-{
-	if( id.match( /^test(\d+)/ ) ) {
-		id=RegExp.$1;
-		Test.AnotherWay._g_tests_queue.push( {
-			url: Test.AnotherWay._g_test_page_urls[id].url,
-			convention: Test.AnotherWay._g_test_page_urls[id].convention,
-			test_objects: []
-			} );
-		if( Test.AnotherWay._g_tests_queue.length==1 ) {
-			if( !called_from_outside ) {
-				// Crap. Be careful stepping around.
-				// For Mozilla and Opera, when this file is included into the frameset page that is in another directory (and _g_outside_path_correction!=null)
-				// but the test pages are started from within it (by "run" buttons), then:
-				// depending on whether the page is the first one loaded into the test frame or not,
-				// the base url for relative test pages differs.
-				// Crap, like I said.
-				Test.AnotherWay._g_tests_queue[0].suppress_outside_path_correction=true;
-			}
-			Test.AnotherWay._start_loading_page();
-		}
-	}
-}
-Test.AnotherWay._load_next_page=function()
-{
-	Test.AnotherWay._g_tests_queue.splice( 0, 1 );
-	if( Test.AnotherWay._g_tests_queue.length>0 ) {
-		Test.AnotherWay._start_loading_page();
-	}else {
-		if( !Test.AnotherWay._g_test_frame_no_clear ) {
-			Test.AnotherWay._g_test_iframe.location.replace( "about:blank" );
-		}
-	}
-}
-Test.AnotherWay._g_opera_path_correction=null; // ugly wart to support opera
-Test.AnotherWay._g_outside_path_correction=null; // ugly wart to accomodate Opera and Mozilla, where relative url relates to the directory where the page that calls this function is located
-Test.AnotherWay._set_iframe_location=function( iframe, loc, outside_path_correction )
-{
-	// allow to load only locations with the same origin
-	var proto_end=loc.indexOf( "://" );
-	if( proto_end!=-1 ) { // otherwise, it's safe to assume (for Opera, Mozilla and IE ) that loc will be treated as relative
-		var main_loc=window.location.href;
-		var host_end=loc.substring( proto_end+3 ).indexOf( "/" );
-		var ok=false;
-		if( host_end!=-1 ) {
-			var loc_origin=loc.substring( 0, proto_end+3+host_end+1 );
-			if( main_loc.length>=loc_origin.length && main_loc.substring( 0, loc_origin.length )==loc_origin ) {
-				ok=true;
-			}
-		}
-		if( !ok ) {
-			return { msg: "test pages may have only urls with the same origin as "+main_loc };
-		}
-	}
-	// opera cannot handle urls relative to file:// without assistance
-	if( window.opera!=null && window.location.protocol=="file:" && loc.indexOf( ":" )==-1 ) {
-		var base=window.location.href;
-		var q_pos=base.indexOf( "?" );
-		if( q_pos!=-1 ) {
-			base=base.substring( 0, q_pos );
-		}
-		var slash_pos=base.lastIndexOf( "/" );
-		if( slash_pos!=-1 ) {
-			base=base.substring( 0, slash_pos+1 );
-			Test.AnotherWay._g_opera_path_correction=base;
-			loc=base+loc;
-		}
-	}
-	// if this function is called from another page, and if that page is in another directory, correction is needed
-	if( outside_path_correction!=null ) {
-		var pos=loc.indexOf( outside_path_correction );
-		if( pos==0 ) {
-			loc=loc.substring( outside_path_correction.length+1 );
-		}
-	}
-	if( iframe.location!=null ) {
-		iframe.location.replace( loc );
-	}else {
-		iframe.src=loc;
-	}
-	return {};
-}
-Test.AnotherWay._start_loading_page=function()
-{
-	var test_page=Test.AnotherWay._g_tests_queue[0];
-	test_page.loading_timeout_milliseconds=12000;
-	test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
-	test_page.wait_msg=Test.AnotherWay._print_counter_result( test_page.url, "loading...", test_page.loading_timeout_milliseconds, "loading" );
-	if( test_page.convention=="jsan" ) {
-		// the tests in that page will run when it's loading, so the test object must be ready
-		Test.AnotherWay._g_test_object_for_jsan=new Test.AnotherWay._test_object_t( test_page.url );
-	}
-	var outside_path_correction=null;
-	if( Test.AnotherWay._g_outside_path_correction!=null && !test_page.suppress_outside_path_correction ) {
-		outside_path_correction=Test.AnotherWay._g_outside_path_correction;
-	}
-	var result=Test.AnotherWay._set_iframe_location( Test.AnotherWay._g_test_iframe, test_page.url, outside_path_correction );
-	if( result.msg!=null ) {
-		Test.AnotherWay._unprint_result( test_page.wait_msg );
-		Test.AnotherWay._print_result( test_page.url, result.msg, "badtest", null );
-		Test.AnotherWay._load_next_page();
-	}
-}
-
-Test.AnotherWay._loading_timeout=function()
-{
-	var test_page=Test.AnotherWay._g_tests_queue[0];
-	test_page.loading_timeout_milliseconds-=Test.AnotherWay._g_timeout_granularity;
-	if( test_page.loading_timeout_milliseconds>0 ) {
-		Test.AnotherWay._update_msg_counter( test_page.wait_msg, (test_page.loading_timeout_milliseconds/1000).toFixed() );
-		test_page.timeout_id=setTimeout( Test.AnotherWay._loading_timeout, Test.AnotherWay._g_timeout_granularity );
-	}else {
-		Test.AnotherWay._unprint_result( test_page.wait_msg );
-		Test.AnotherWay._print_result( test_page.url, "Unable to load test page. Timeout expired", "badtest", null );
-		Test.AnotherWay._load_next_page();
-	}
-}
-
-Test.AnotherWay._strip_query_and_hash=function( s )
-{
-	var i=s.lastIndexOf( "#" );
-	if( i!=-1 ) {
-		s=s.substring( 0, i );
-	}
-	i=s.lastIndexOf( "?" );
-	if( i!=-1 ) {
-		s=s.substring( 0, i );
-	}
-	return s;
-}
-Test.AnotherWay._is_url_loaded=function( url, wnd )
-{
-	var loaded=false;
-	if( wnd!=null && wnd.location!=null ) {
-		// after some popup blocker interference, location may behave strange..
-		var location_s="";
-		location_s+=wnd.location;
-		if( location_s!="" ) {
-			var pathname=wnd.location.pathname;
-			var expected_url=url;
-			var i=expected_url.lastIndexOf( "#" );
-			if( i!=-1 ) {
-				expected_url=expected_url.substring( 0, i );
-			}
-			i=expected_url.lastIndexOf( "?" );
-			if( i!=-1 ) {
-				expected_url=expected_url.substring( 0, i );
-			}
-			i=expected_url.lastIndexOf( "/" );
-			if( i!=-1 && i!=expected_url.length-1 ) {
-				expected_url=expected_url.substring( i+1 );
-			}
-			i=pathname.indexOf( expected_url )
-			if( wnd.location.href==url || (i!=-1 && i==pathname.length-expected_url.length) ) {
-				if( /*window.opera==null*/wnd.document.readyState==null || wnd.document.readyState=="complete" ) { // for opera (and IE?), getElementById does not work until..
-					loaded=true;
-				}
-			}
-		}
-	}
-	return loaded;
-}
-// find and run all test functions in the g_cur_page html page.
-Test.AnotherWay._test_page_onload=function()
-{
-	if( Test.AnotherWay._g_tests_queue.length==0 ) {
-		return;
-	}
-	var test_page=Test.AnotherWay._g_tests_queue[0];
-	if( !Test.AnotherWay._is_url_loaded( test_page.url, Test.AnotherWay._g_test_iframe ) ) {
-		return;
-	}
-	clearTimeout( test_page.timeout_id );
-	Test.AnotherWay._unprint_result( test_page.wait_msg );
-
-	if( test_page.convention=="anotherway" ) {
-		// get test function names (those beginning with "test")
-		if( typeof( Test.AnotherWay._g_test_iframe.document.scripts )!='undefined' ) { // IE
-			for( var i=0; i<Test.AnotherWay._g_test_iframe.document.scripts.length; ++i ) {
-				var script_text=Test.AnotherWay._g_test_iframe.document.scripts[i].text;
-				var fun_sig="function test";
-				var fun_start=script_text.indexOf( fun_sig );
-
-				while( fun_start!=-1 ) {
-					script_text=script_text.substring( fun_start, script_text.length );
-					var fun_end=script_text.indexOf( '(' );
-					var fun_name=script_text.substring( "function ".length,  fun_end );
-                                        var whitespace = fun_name.indexOf( ' ' );
-                                        if (whitespace >= 0)
-                                            fun_name = fun_name.substring( 0, whitespace );
-					test_page.test_objects.push( new Test.AnotherWay._test_object_t( fun_name ) );
-					script_text=script_text.substring( fun_end, script_text.length );
-					fun_start=script_text.indexOf( fun_sig  );
-				}
-			}
-		}else { // otherwise (not IE) it ought to work like this
-			for( var i in Test.AnotherWay._g_test_iframe) {
-				// Hack to prevent failure in FF3.0b1 (innerWidth/innerHeight) and FF>=3.5 (sessionStorage)
-                if (i == "innerWidth" || i == "innerHeight" || i == "sessionStorage") { continue; }
-                if( typeof( Test.AnotherWay._g_test_iframe[i] )=='function' ) {
-					if( i.substring( 0, 4 )=="test" ) {
-						test_page.test_objects.push( new Test.AnotherWay._test_object_t( i ) );
-					}
-				}
-			}
-		}
-	}else if( test_page.convention=="jsan" ) {
-		// the test object is already filled with results
-		test_page.test_objects.push( Test.AnotherWay._g_test_object_for_jsan );
-	}
-
-	if( test_page.test_objects.length==0 ) {
-		Test.AnotherWay._print_result( test_page.url,  "No test functions defined in the page", "badtest", null );
-		Test.AnotherWay._load_next_page();
-		return;
-	}
-
-	test_page.wait_msg=Test.AnotherWay._print_result( test_page.url, "running tests..<span class=\"counter\">"+test_page.test_objects.length+"</span>", "running", null );
-
-	test_page.test_object_i=0;
-	Test.AnotherWay._run_more_tests();
-}
-
-Test.AnotherWay._handle_exception=function( o, e, title )
-{
-	var s=title+": "+typeof( e )+": ";
-	if( e.message!=null ) {
-		s+=e.message;
-	}else if( e.description!=null ) {
-		s+=e.description;
-	}else {
-		s+=e.toString();
-	}
-//	if( e.location!=null ) {  // XXX figure out how to display exception location if it's present (like in mozilla)
-//		s+=" location: "+e.location.toString();
-//	}
-	o.exception=s;
-	s=[];
-	if( e.stack ) {
-		var lines=e.stack.split( "\n" );
-		for( var i=0; i<lines.length; ++i ) {
-			// format of the line: func_name(args)@file_name:line_no
-			if( lines[i].match( /(\w*)\(([^\)]*)\)@(.*):([^:]*)$/ ) ) {
-				var func_name=RegExp.$1;
-				if( func_name.length==0 ) {
-					func_name="<anonymous>";
-				}
-				s.push( "in "+func_name+"( "+RegExp.$2+") at "+RegExp.$3+" line "+RegExp.$4+"\n" );
-			}
-		}
-	}
-	o.exception_stack=s;
-}
-
-Test.AnotherWay._run_more_tests=function()
-{
-	var test_page=Test.AnotherWay._g_tests_queue[0];
-	while( test_page.test_object_i<test_page.test_objects.length ) {
-		Test.AnotherWay._update_msg_counter( test_page.wait_msg, (1+test_page.test_object_i)+"/"+test_page.test_objects.length );
-		var o=test_page.test_objects[test_page.test_object_i];
-		if( test_page.convention=="anotherway" ) {
-			try {
-				Test.AnotherWay._g_test_iframe[o.name]( o );
-			}catch( e ) {
-				Test.AnotherWay._handle_exception( o, e, "" );
-			}
-		} // for "jsan" convention, test has run already
-		if( o.delay_actions.length>0 || o.wait_result_milliseconds>0 ) {
-			o.delay_total_milliseconds_left+=o.wait_result_milliseconds;
-			Test.AnotherWay._delay_actions_timeout();
-			return;
-		}
-		++test_page.test_object_i;
-	}
-	Test.AnotherWay._unprint_result( test_page.wait_msg );
-	Test.AnotherWay._print_result( test_page.url, null, null, test_page.test_objects );
-	Test.AnotherWay._load_next_page();
-}
-
-Test.AnotherWay._delay_actions_timeout=function()
-{
-	var test_page=Test.AnotherWay._g_tests_queue[0];
-	var test_object=test_page.test_objects[test_page.test_object_i];
-	var finished=true;
-	if( test_object.delay_action_i==null ) {
-		// set up to start first action
-		test_object.delay_action_i=-1;
-	}else {
-		// perform current action
-		var milliseconds_passed=(new Date()).getTime()-test_object.delay_prev_timer_time;
-		test_object.delay_current_milliseconds_left-=milliseconds_passed;
-		test_object.delay_total_milliseconds_left-=milliseconds_passed;
-		finished=Test.AnotherWay._delay_continue_action( test_object, milliseconds_passed );
-	}
-	while( finished && test_object.delay_action_i<test_object.delay_actions.length ) {
-		++test_object.delay_action_i; // start next action
-		finished=Test.AnotherWay._delay_start_action( test_object );
-	}
-	if( test_object.delay_action_i<=test_object.delay_actions.length ) { // any more actions left ?
-		test_object.delay_prev_timer_time=(new Date()).getTime();
-		var next_timeout=Test.AnotherWay._g_timeout_granularity;
-		if( test_object.delay_current_milliseconds_left<next_timeout ) {
-			next_timeout=test_object.delay_current_milliseconds_left;
-		}
-		if( test_object.second_wait_msg!=null ) {
-			Test.AnotherWay._update_msg_counter( test_object.second_wait_msg, (test_object.delay_total_milliseconds_left/1000).toFixed() );
-		}
-		setTimeout( Test.AnotherWay._delay_actions_timeout, next_timeout );
-	}else { // no more actions left. run the next test.
-		if( test_object.second_wait_msg!=null ) {
-			Test.AnotherWay._unprint_result( test_object.second_wait_msg );
-			test_object.second_wait_msg=null;
-		}
-		++test_page.test_object_i;
-		Test.AnotherWay._run_more_tests();
-	}
-}
-Test.AnotherWay._delay_start_action=function( test_object )
-{
-	var finished=false;
-	var wait_msg="";
-	if( test_object.delay_action_i==test_object.delay_actions.length ) {
-		if( test_object.wait_result_milliseconds>0 ) {
-			test_object.delay_current_milliseconds_left=test_object.wait_result_milliseconds; // wait for result
-			wait_msg="waiting for results..";
-		}else {
-			++test_object.delay_action_i; // dont wait for result
-		}
-	}else {
-		var action=test_object.delay_actions[test_object.delay_action_i];
-		if( action.action_kind=="call" ) {
-			test_object.delay_current_milliseconds_left=action.call_delay_milliseconds;
-			wait_msg="performing delayed calls..";
-		}else if( action.action_kind=="window" ) {
-			if( Test.AnotherWay._g_opera_path_correction!=null && action.wnd_url.indexOf( ":" )==-1 ) {
-				action.wnd_url=Test.AnotherWay._g_opera_path_correction+action.wnd_url;
-			}
-			action.wnd_wnd=window.open( action.wnd_url, "_blank" );
-			if( action.wnd_wnd==null ) {
-				finished=true;
-				test_object.fail( "unable to open window for "+action.wnd_url );
-			}else {
-				test_object.delay_current_milliseconds_left=action.wnd_timeout_milliseconds;
-				wait_msg="opening window..";
-			}
-		}else if( action.action_kind=="replay" ) {
-			if( action.replay_events.length==0 ) {
-				finished=true;
-			}else {
-				action.replay_event_i=0;
-				test_object.delay_current_milliseconds_left=action.replay_events[0]["time"];
-				wait_msg="replaying events..";
-			}
-		}
-	}
-	if( test_object.second_wait_msg!=null ) {
-		Test.AnotherWay._unprint_result( test_object.second_wait_msg );
-	}
-	if( wait_msg!="" ) {
-		var test_page=Test.AnotherWay._g_tests_queue[0];
-		test_object.second_wait_msg=Test.AnotherWay._print_counter_result( test_page.url, wait_msg, test_object.delay_total_milliseconds_left, "waiting" );
-	}else {
-		test_object.second_wait_msg=null;
-	}
-	return finished;
-}
-Test.AnotherWay._delay_continue_action=function( test_object, milliseconds_passed )
-{
-	var finished=test_object.delay_current_milliseconds_left<=0;
-	if( test_object.delay_action_i==test_object.delay_actions.length ) { // action is "waiting for results"
-		if( test_object.n_plan!=null && test_object.n_plan==test_object.n_ok+test_object.n_fail ) {
-			finished=true; // if all assertions results are recorded, don't wait any more
-		}
-		if( finished ) {
-			++test_object.delay_action_i; // move on to the next test
-		}
-	}else {
-		var action=test_object.delay_actions[test_object.delay_action_i];
-		if( action.action_kind=="call" ) {
-			if( finished ) {
-				try {
-					action.call_fn();
-			    }catch( e ) {
-					Test.AnotherWay._handle_exception( test_object, e, "in delay_call" );
-				}
-			}
-		}else if( action.action_kind=="window" ) {
-			test_object.delay_total_milliseconds_left+=milliseconds_passed; // for "window", the countdown is suspended since it's unknown how long it will take
-			if( Test.AnotherWay._is_url_loaded( action.wnd_url, action.wnd_wnd ) ) {
-				try {
-					action.wnd_fn( action.wnd_wnd );
-				}catch( e ) {
-					Test.AnotherWay._handle_exception( test_object, e, "in open_window function call" );
-				}
-				finished=true;
-			}else if( finished ) {
-				test_object.fail(  "unable to open window for url '"+action.wnd_url+"'. timeout expired" );
-			}
-		}else if( action.action_kind=="replay" ) {
-			if( finished ) {
-//				try {
-					Test.AnotherWay._delay_replay_event( test_object, action.replay_wnd, action.replay_events[action.replay_event_i], action.replay_checkpoints );
-//				}catch( e ) { // disabled, until I know how to gel location info from an exception
-//					Test.AnotherWay._handle_exception( test_object, e, "while replaying event" );
-//				}
-				++action.replay_event_i;
-				finished=action.replay_event_i==action.replay_events.length;
-				if( !finished ) {
-					test_object.delay_current_milliseconds_left=action.replay_events[action.replay_event_i]["time"];
-				}
-			}
-		}
-	}
-	return finished;
-}
-Test.AnotherWay._delay_replay_event=function( test_object, wnd, event, checkpoints )
-{
-	if( event.type=="_checkpoint" ) {
-		var checkpoint_n=event.which;
-		var prev_n_fail=test_object.n_fail;
-		checkpoints[checkpoint_n]( test_object, wnd );
-		var flash_color= prev_n_fail==test_object.n_fail ? "#2f2" : "#f22" ;
-		Test.AnotherWay._record_flash_border( flash_color );
-	}else if( event.type=="click" || event.type=="mouseover" || event.type=="mouseout" || event.type=="mousemove" || event.type=="mousedown" || event.type=="mouseup" ) {
-		var target=Test.AnotherWay._record_node_path_to_node( event["target"], wnd.document );
-		if( target!=null ) {
-			Test.AnotherWay._record_control_update_highlight( target, "ball", event );
-			var e=wnd.document.createEvent( "MouseEvents" );
-			var related_target=Test.AnotherWay._record_node_path_to_node( event["relatedTarget"], wnd.document );
-			e.initMouseEvent(
-				event["type"],
-				event["cancelable"],
-				event["bubbles"],
-				wnd.document.defaultView,
-				event["detail"],
-				event["screenX"],
-				event["screenY"],
-				event["clientX"],
-				event["clientY"],
-				event["ctrlKey"],
-				event["altKey"],
-				event["shiftKey"],
-				event["metaKey"],
-				event["button"],
-				Test.AnotherWay._record_node_path_to_node( event["relatedTarget"], wnd.document )
-			);
-			// Firefox 1.0.6 somehow loses relatedTarget somewhere on the way. Pass through our own, for those who choose to care.
-			e.passThroughRelatedTarget=related_target;
-			target.dispatchEvent( e );
-		}
-	}else if( event.type=="keyup" || event.type=="keydown" || event.type=="keypress" ) {
-		var e=wnd.document.createEvent( "KeyboardEvents" ); // forget it. Apparently it's not supported neither by mozilla nor by opera.
-		e.initKeyboardEvent(
-				event["type"],
-				event["cancelable"],
-				event["bubbles"],
-				wnd.document.defaultView,
-				event["which"],
-				event["which"],
-				event["ctrlKey"],
-				event["altKey"],
-				event["shiftKey"],
-				event["metaKey"],
-				false
-		);
-		wnd.document.dispatchEvent( e );
-	}
-}
-
-Test.AnotherWay._print_counter_result=function( url, msg, milliseconds, style )
-{
-	return Test.AnotherWay._print_result( url, msg+"<span class=\"counter\">"+(milliseconds/1000).toFixed()+"</span>", style, null );
-}
-
-Test.AnotherWay._g_result_count=0; // for assigning unique ids to result paragraphs
-
-// number of pages tested
-Test.AnotherWay._g_ok_pages=0;
-Test.AnotherWay._g_fail_pages=0;
-
-Test.AnotherWay._print_result=function( url, msg, style, test_objects )
-{
-	var results=document.getElementById( "results" );
-	var r=results.appendChild( document.createElement( "p" ) );
-	r.id="result"+Test.AnotherWay._g_result_count;
-	++Test.AnotherWay._g_result_count;
-	r.onclick=Test.AnotherWay._toggle_detail;
-	var text="<span class=\"bullet\">&nbsp;&nbsp;&nbsp;</span>&nbsp;";
-	if( url!="" ) {
-		text+=url+":  ";
-	}
-	if( msg!=null ) {
-		text+=msg;
-	}
-	if( test_objects!=null ) {
-		// compose summary and detail texts
-		var total_ok=0;
-		var total_detail_ok=0;
-		var total_fail=0;
-		var total_detail_fail=0;
-		var no_plan=0;
-
-		var detail=results.appendChild( document.createElement( "div" ) );
-
-		if( r.id.match( /^result(\d+)$/ ) ) {
-			detail.id="result_detail"+RegExp.$1;
-		}
-
-		for( var i=0; i<test_objects.length; ++i ) {
-			var o=test_objects[i];
-			var p;
-			var p_text;
-			p=document.createElement( "P" );
-			Test.AnotherWay._set_css_class( p, "result_detail" );
-			p_text=o.name;
-			if( o.n_fail>0 || o.exception || (o.n_plan!=null && o.n_plan!=o.n_ok+o.n_fail) || (o.n_plan==null && o.n_ok==0 && o.n_fail==0)) {
-				++total_fail;
-				p_text+=" <span class=\"fail\">";
-				if( o.n_plan!=null && o.n_plan!=o.n_ok+o.n_fail) {
-					p_text+="planned "+o.n_plan+" assertions but got "+(o.n_ok+o.n_fail)+"; ";
-				}
-				if(o.n_plan==null && o.n_ok==0 && o.n_fail==0) {
-					p_text+="test did not output anything";
-				}else {
-					p_text+=" fail "+o.n_fail;
-				}
-				p_text+="</span>";
-			}else {
-				++total_ok;
-			}
-			p_text+=" ok "+o.n_ok;
-			if( o.n_plan==null ) {
-				no_plan=1;
-				p_text+=" <span class=\"warning\">no plan</span>";
-			}
-			p.innerHTML=p_text;
-			detail.appendChild( p );
-			if( o.exception ) {
-				p=document.createElement( "P" );
-				Test.AnotherWay._set_css_class( p, "result_exception_detail" );
-				p.innerHTML="<span class=\"fail\">exception:</span> "+o.exception;
-				detail.appendChild( p );
-				p=document.createElement( "P" );
-				Test.AnotherWay._set_css_class( p, "result_exception_stack_detail" );
-				p.innerHTML=o.exception_stack.join( "<br/>" );
-				detail.appendChild( p );
-			}
-			for( var ii=0; ii<o.assertions.length; ++ii ) {
-				var oo=o.assertions[ii];
-				var status=oo.ok ? "ok" : "<span class=\"fail\">fail</span>";
-				p=document.createElement( "P" );
-				Test.AnotherWay._set_css_class( p, "result_micro_detail" );
-				p.innerHTML=status;
-				p.appendChild( document.createTextNode( " "+oo.name ) );
-				detail.appendChild( p );
-			}
-			total_detail_ok+=o.n_ok;
-			total_detail_fail+=o.n_fail;
-		}
-		if( total_fail || total_detail_fail ) {
-			text+=" fail "+total_fail;
-		}
-		text+=" ok "+total_ok+" (detailed:";
-		if( total_fail || total_detail_fail ) {
-			text+=" fail "+total_detail_fail;
-		}
-		text+=" ok "+total_detail_ok+")";
-		if( no_plan ) {
-			text+=" <span class=\"warning\">no plan</span>";
-		}
-		style= total_fail==0 ? "ok" : "fail";
-		detail.style.display= style=="fail" ? "block" : "none";
-		detail.style.cursor="text";
-	}
-	if( style!=null ) {
-		Test.AnotherWay._set_css_class( r, style );
-		if( style=="ok" ) {
-			++Test.AnotherWay._g_ok_pages;
-		}else if( style=="fail" || style=="badtest" ) {
-			++Test.AnotherWay._g_fail_pages;
-		}
-		var pages_total="";
-		if( Test.AnotherWay._g_fail_pages>0 ) {
-			pages_total+=" fail "+Test.AnotherWay._g_fail_pages;
-		}
-		pages_total+=" ok "+Test.AnotherWay._g_ok_pages;
-		Test.AnotherWay._update_results_total( pages_total );
-	}
-	r.innerHTML=text;
-	if( results.scrollHeight!=null && results.scrollTop!=null && results.offsetHeight!=null ) {
-		results.scrollTop=results.scrollHeight-results.offsetHeight;
-	}
-	// when test_objects is not null, the results are final - good time to clean up
-	if( test_objects!=null ) {
-		for( var i=0; i<test_objects.length; ++i ) {
-			var actions=test_objects[i].delay_actions;
-			for( var action_i=0; action_i<actions.length; ++action_i ) {
-				var action=actions[action_i];
-				if( action.action_kind=="window" && action.wnd_wnd!=null && !action.wnd_no_close ) {
-					action.wnd_wnd.close();
-					action.wnd_wnd=null;
-				}
-			}
-		}
-	}
-	return r;
-}
-Test.AnotherWay._unprint_result=function( child )
-{
-	var results=document.getElementById( "results" );
-	results.removeChild( child );
-}
-Test.AnotherWay._toggle_detail=function()
-{
-	if( this.id.match( /^result(\d+)$/ ) ) {
-		var detail=document.getElementById( "result_detail"+RegExp.$1 );
-		if( detail!=null ) {
-			if( detail.style.display=="none" ) {
-				detail.style.display="block";
-			}else if( detail.style.display=="block" ) {
-				detail.style.display="none";
-			}
-		}
-	}
-}
-Test.AnotherWay._update_msg_counter=function( msg, text )
-{
-	for( var i=0; i<msg.childNodes.length; ++i ) {
-		var item=msg.childNodes[i];
-		if( item.nodeName=="SPAN" && Test.AnotherWay._get_css_class( item )=="counter" ) {
-			item.innerHTML=text;
-		}
-	}
-}
-Test.AnotherWay._update_results_total=function( msg )
-{
-	var total=document.getElementById( "total" );
-	if( total ) {
-		total.innerHTML=msg;
-	}
-}
-Test.AnotherWay._results_clear_onclick=function()
-{
-	var results=document.getElementById( "results" );
-	results.innerHTML="";
-	Test.AnotherWay._update_results_total( "" );
-	Test.AnotherWay._g_ok_pages=0;
-	Test.AnotherWay._g_fail_pages=0;
-	var debug=document.getElementById( "debug" );
-	debug.innerHTML="";
-}
-
-Test.AnotherWay._get_css_class=function( o )
-{
-	var c=o.getAttribute( "className" );
-	if( c==null || c=="" ) {
-		c=o.getAttribute( "class" );
-	}
-	return c;
-}
-Test.AnotherWay._set_css_class=function( o, css_class )
-{
-	o.setAttribute( "className", css_class );
-	o.setAttribute( "class", css_class );
-}
-
-Test.AnotherWay._tab_onclick=function()
-{
-	var tab=this;
-	var tabs=[ document.getElementById( "debug_tab" ), document.getElementById( "results_tab" ) ];
-	var panes=[ document.getElementById( "debug" ), document.getElementById( "results" ) ];
-	for( var i=0; i<tabs.length; ++i ) {
-		if( tab==tabs[i] ) {
-			Test.AnotherWay._set_css_class( tabs[i], "active_tab" );
-			panes[i].style.display="block";
-		}else {
-			Test.AnotherWay._set_css_class( tabs[i], "inactive_tab" );
-			panes[i].style.display="none";
-		}
-	}
-}
-Test.AnotherWay._tab_mouseover=function()
-{
-	if( Test.AnotherWay._get_css_class( this )=="inactive_tab" ) {
-		Test.AnotherWay._set_css_class( this, "inactive_mouseover_tab" );
-	}
-}
-Test.AnotherWay._tab_mouseout=function()
-{
-	if( Test.AnotherWay._get_css_class( this )=="inactive_mouseover_tab" ) {
-		Test.AnotherWay._set_css_class( this, "inactive_tab" );
-	}
-}
-
-// recording mouse input
-Test.AnotherWay._record_check_onfocus=function()
-{
-	var o=this;
-	var check_select=o.type!="text";
-	var div=document.getElementById( "record_div" );
-	var inputs=div.getElementsByTagName( "input" );
-	for( var i=0; i<inputs.length; ++i ) {
-		var input=inputs[i];
-		if( input.type=="radio" ) {
-			if( input.value=="select" ) {
-				input.checked=check_select;
-			}else if( input.value=="input" ) {
-				input.checked=!check_select;
-			}
-		}
-	}
-}
-
-Test.AnotherWay._g_no_record_msg=null; // not null - recording is unavailable
-Test.AnotherWay._g_record_timeout_cnt=0; // opening window for a page for recording
-Test.AnotherWay._g_record_url=null;
-Test.AnotherWay._g_record_wnd=null;
-Test.AnotherWay._g_record_random_id=null; // added to element ids of record_control div so that they do not clash with ids already in the page for which input is recorded
-Test.AnotherWay._g_record_keydown=null; // recording control - which key is down
-Test.AnotherWay._g_record_ctrl_keydown=false;
-Test.AnotherWay._g_record_shift_keydown=false;
-Test.AnotherWay._g_record_control_visible=true; // recording control ui state
-Test.AnotherWay._g_record_started;
-Test.AnotherWay._g_record_paused;
-Test.AnotherWay._g_record_include_mousemove=false;
-Test.AnotherWay._g_record_start_time; // for time references
-Test.AnotherWay._g_record_pause_start_time;
-Test.AnotherWay._g_record_update_time_interval; // showing time in the control ui
-Test.AnotherWay._g_record_waiting_for_results=false; // waiting for results window to open
-Test.AnotherWay._g_record_events; // recorded events
-Test.AnotherWay._g_record_under_cursor; // track element under cursor
-Test.AnotherWay._g_record_checkpoint_count; // for checkpoint numbering
-Test.AnotherWay._g_record_mouse_over_record_control; // for avoiding record control highlight on mouseover
-Test.AnotherWay._g_record_highlighted_element={ element: null, x: null, y: null };
-
-Test.AnotherWay._record_control_get_element=function( id )
-{
-	if( Test.AnotherWay._g_record_wnd!=null && Test.AnotherWay._g_record_wnd.document!=null ) {
-		return Test.AnotherWay._g_record_wnd.document.getElementById( id+Test.AnotherWay._g_record_random_id );
-	}else {
-		return null;
-	}
-}
-Test.AnotherWay._record_start_onclick=function() // "record" button on the run_tests.html: open a window for a page for which input is recorded
-{
-	if( Test.AnotherWay._g_no_record_msg!=null ) {
-		alert( Test.AnotherWay._g_no_record_msg );
-		return;
-	}
-	if( Test.AnotherWay._g_record_timeout_cnt>0
-	    || (Test.AnotherWay._g_record_wnd!=null && (Test.AnotherWay._g_record_wnd.closed!=null && !Test.AnotherWay._g_record_wnd.closed)) ) { // in opera, closed is null.
-		alert( "there is already window opened for recording input for a page "+Test.AnotherWay._g_record_url );
-		return;
-	}
-	var div=document.getElementById( "record_div" );
-	var inputs=div.getElementsByTagName( "input" );
-	var url=null;
-	for( var i=0; i<inputs.length; ++i ) {
-		var input=inputs[i];
-		if( input.type=="radio" ) {
-			if( input.value=="select" && input.checked ) {
-				var index=document.getElementById( "record_select" ).selectedIndex;
-				if( index>0 ) {
-					url=Test.AnotherWay._g_test_page_urls[index-1].url;
-				}
-			}else if( input.value=="input" && input.checked ) {
-				url=document.getElementById( "record_input" ).value;
-			}
-		}
-	}
-	if( url!=null ) {
-		Test.AnotherWay._g_record_url=url;
-		Test.AnotherWay._g_record_wnd=window.open( url, "_blank" );
-		if( Test.AnotherWay._g_record_wnd==null ) {
-			alert( "unable to open new window for a page: "+url );
-		}else {
-			Test.AnotherWay._g_record_timeout_cnt=50;
-			setTimeout( Test.AnotherWay._record_window_timeout, 100 );
-		}
-	}
-}
-Test.AnotherWay._record_window_timeout=function()
-{
-	if( Test.AnotherWay._is_url_loaded( Test.AnotherWay._g_record_url, Test.AnotherWay._g_record_wnd ) ) {
-		Test.AnotherWay._record_window_setup( Test.AnotherWay._g_record_wnd );
-	}else {
-		if( --Test.AnotherWay._g_record_timeout_cnt>0 ) {
-			setTimeout( Test.AnotherWay._record_window_timeout, 100 );
-		}else {
-			alert( "timeout expired while opening new window for a page: "+Test.AnotherWay._g_record_url );
-			Test.AnotherWay._g_record_wnd=null;
-			Test.AnotherWay._g_record_url=null;
-			Test.AnotherWay._g_record_timeout_cnt=0;
-		}
-	}
-}
-Test.AnotherWay._record_control_randomize_id=function( e, r )
-{
-	if( e.id!="" ) {
-		e.id=e.id+r;
-	}
-	for( var c=e.firstChild; c!=null; c=c.nextSibling ) {
-		Test.AnotherWay._record_control_randomize_id( c, r );
-	}
-}
-Test.AnotherWay._record_window_setup=function( wnd ) // insert recording control into the page for which input is recorded
-{
-	Test.AnotherWay._g_record_timeout_cnt=0;
-	var this_div=document.getElementById( "record_control" );
-	var record_control=wnd.document.importNode( this_div, true );
-	Test.AnotherWay._g_record_random_id=(1000*Math.random()).toFixed();
-	Test.AnotherWay._record_control_randomize_id( record_control, Test.AnotherWay._g_record_random_id );
-	Test.AnotherWay._g_record_control_visible=true;
-	Test.AnotherWay._g_record_started=false;
-	Test.AnotherWay._g_record_paused=false;
-	Test.AnotherWay._g_record_checkpoint_count=0;
-	Test.AnotherWay._g_record_mouse_over_record_control=false;
-	var doc=wnd.document;
-	doc.body.appendChild( record_control );
-	// opera sans-serif font is different
-	if( window.opera ) {
-		cursor_over_indicator=Test.AnotherWay._record_control_get_element( "record_cursor_over" );
-		cursor_over_indicator.style.width="18em";
-		cursor_over_indicator.style.height="2em";
-		cursor_over_indicator.style.fontSize="7pt";
-	}
-	doc.addEventListener( "keydown", Test.AnotherWay._record_control_keydown, true );
-	doc.addEventListener( "keyup", Test.AnotherWay._record_control_keyup, true );
-//	doc.addEventListener( "keypress", Test.AnotherWay._record_event, true ); // replaying is not supported by any known browser
-
-	doc.body.addEventListener( "mousemove", Test.AnotherWay._record_on_mousemove, true );
-	doc.body.addEventListener( "click", Test.AnotherWay._record_event, true );
-	doc.body.addEventListener( "mouseover", Test.AnotherWay._record_event, true );
-	doc.body.addEventListener( "mouseout", Test.AnotherWay._record_event, true );
-	doc.body.addEventListener( "mousedown", Test.AnotherWay._record_event, true );
-	doc.body.addEventListener( "mouseup", Test.AnotherWay._record_event, true );
-}
-Test.AnotherWay._record_control_key_disabled=function( k )
-{
-	if( k=="c" ) {
-		return !Test.AnotherWay._g_record_started;
-	}else if( k=="p" ) {
-		return !Test.AnotherWay._g_record_started;
-	}else if( k=="s" ) {
-		return Test.AnotherWay._g_record_waiting_for_results;
-	}else {
-		return false;
-	}
-}
-
-Test.AnotherWay._record_control_update_ui=function()
-{
-	var keydown_color="#fff";
-	var disabled_color="#aaa";
-	var button_color="#adf";
-	var active_color="#fdf";
-
-	var display={};
-	display[false]="none";
-	display[true]="inline";
-
-	var s_button=Test.AnotherWay._record_control_get_element( "record_s" );
-	var record_on=Test.AnotherWay._record_control_get_element( "record_on" );
-	var record_off=Test.AnotherWay._record_control_get_element( "record_off" );
-
-	s_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "s" ) ? disabled_color
-		: Test.AnotherWay._g_record_keydown=="s" ? keydown_color : Test.AnotherWay._g_record_started ? active_color : button_color;
-	record_on.style.display=display[!Test.AnotherWay._g_record_started];
-	record_off.style.display=display[Test.AnotherWay._g_record_started];
-
-	var h_button=Test.AnotherWay._record_control_get_element( "record_h" );
-	h_button.style.backgroundColor= Test.AnotherWay._g_record_keydown=="h" ? keydown_color : button_color;
-
-	var p_button=Test.AnotherWay._record_control_get_element( "record_p" );
-	var record_pause_on=Test.AnotherWay._record_control_get_element( "record_pause_on" );
-	var record_pause_off=Test.AnotherWay._record_control_get_element( "record_pause_off" );
-	p_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "p" ) ? disabled_color
-		: Test.AnotherWay._g_record_keydown=="p" ? keydown_color : Test.AnotherWay._g_record_paused ? active_color : button_color;
-	record_pause_on.style.display=display[!Test.AnotherWay._g_record_paused];
-	record_pause_off.style.display=display[Test.AnotherWay._g_record_paused];
-
-	var m_button=Test.AnotherWay._record_control_get_element( "record_m" );
-	var record_include_mousemove=Test.AnotherWay._record_control_get_element( "record_include_mousemove" );
-	var record_omit_mousemove=Test.AnotherWay._record_control_get_element( "record_omit_mousemove" );
-	m_button.style.backgroundColor= Test.AnotherWay._g_record_keydown=="m" ? keydown_color : Test.AnotherWay._g_record_include_mousemove ? active_color : button_color;
-	record_include_mousemove.style.display=display[!Test.AnotherWay._g_record_include_mousemove];
-	record_omit_mousemove.style.display=display[Test.AnotherWay._g_record_include_mousemove];
-
-	var c_button=Test.AnotherWay._record_control_get_element( "record_c" );
-	c_button.style.backgroundColor= Test.AnotherWay._record_control_key_disabled( "c" ) ? disabled_color
-		: Test.AnotherWay._g_record_keydown=="c" ? keydown_color : button_color;
-
-	var record_indicator=Test.AnotherWay._record_control_get_element( "record_indicator" );
-	record_indicator.style.display=display[Test.AnotherWay._g_record_started];
-
-	var pause_indicator=Test.AnotherWay._record_control_get_element( "record_pause_indicator" );
-	pause_indicator.style.display=display[Test.AnotherWay._g_record_paused];
-
-	var record_control=Test.AnotherWay._record_control_get_element( "record_control" );
-	record_control.style.display= Test.AnotherWay._g_record_control_visible ? "block" : "none";
-
-	var shift_button=Test.AnotherWay._record_control_get_element( "record_shift_key" );
-	shift_button.style.backgroundColor= Test.AnotherWay._g_record_shift_keydown ? keydown_color : button_color;
-
-	var ctrl_button=Test.AnotherWay._record_control_get_element( "record_ctrl_key" );
-	ctrl_button.style.backgroundColor= Test.AnotherWay._g_record_ctrl_keydown ? keydown_color : button_color;
-}
-Test.AnotherWay._record_format_time=function( t )
-{
-	t=new Date( t );
-	var m=t.getMinutes();
-	var s=t.getSeconds();
-	var str= m==0 ? "" : m+"m ";
-	str+=s+"s.";
-	return str;
-}
-Test.AnotherWay._record_control_update_time=function()
-{
-	var time_display=Test.AnotherWay._record_control_get_element( "record_time" );
-	if( time_display!=null ) {
-		time_display.innerHTML=Test.AnotherWay._record_format_time( (new Date()).getTime()-Test.AnotherWay._g_record_start_time );
-	}
-}
-Test.AnotherWay._record_control_update_highlight=function( elem, style, event )
-{
-	if( elem==null ) {
-		Test.AnotherWay._record_highlight_border( null );
-	}else {
-		var pos=Test.AnotherWay._get_page_coords( elem );
-		if( style=="ball" || elem!=Test.AnotherWay._g_record_highlighted_element.element || pos.x!=Test.AnotherWay._g_record_highlighted_element.x || pos.y!=Test.AnotherWay._g_record_highlighted_element.y ) {
-			Test.AnotherWay._g_record_highlighted_element={ element: elem, x: pos.x, y: pos.y };
-			Test.AnotherWay._record_highlight_border( elem, style, event );
-		}
-	}
-}
-Test.AnotherWay._record_decode_key=function( event )
-{
-	var k=null;
-	if( event==null ) {
-		k=Test.AnotherWay._g_record_wnd.event.keyCode;
-	}else {
-		k=event.which;
-	}
-	if( k==83 ) {
-		return "s";
-	}else if( k==72 ) {
-		return "h";
-	}else if( k==73 ) {
-		return "i";
-	}else if( k==80 ) {
-		return "p";
-	}else if( k==67 ) {
-		return "c";
-	}else if( k==77 ) {
-		return "m";
-	}else if( k==16 ) {
-		return "shift";
-	}else if( k==17 ) {
-		return "ctrl";
-	}else if( k==18 ) {
-		return "alt";
-	}else if( k==19 ) {
-		return "pause";
-	}else if( k==123 ) {
-		return "f12";
-	}
-	return "";
-}
-Test.AnotherWay._record_control_keydown=function( event )
-{
-	var handled=false;
-	var k=Test.AnotherWay._record_decode_key( event );
-	if( k=="shift" ) {
-		Test.AnotherWay._g_record_shift_keydown=true;
-	}else if( k=="ctrl" ) {
-		Test.AnotherWay._g_record_ctrl_keydown=true;
-	}else if( k!="" && (Test.AnotherWay._g_record_keydown==null || Test.AnotherWay._g_record_keydown==k) ) {
-		if( Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown && !Test.AnotherWay._record_control_key_disabled( k ) ) {
-			Test.AnotherWay._g_record_keydown=k;
-			handled=true;
-		}
-	}else {
-		Test.AnotherWay._g_record_keydown="";
-	}
-	Test.AnotherWay._record_control_update_ui();
-	if( !handled ) {
-//		Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
-	}
-	return;
-}
-Test.AnotherWay._record_control_keyup=function( event )
-{
-	var handled=false;
-	var k=Test.AnotherWay._record_decode_key( event );
-	if( k=="shift" ) {
-		Test.AnotherWay._g_record_shift_keydown=false;
-	}else if( k=="ctrl" ) {
-		Test.AnotherWay._g_record_ctrl_keydown=false;
-	}else if( k!="" && k==Test.AnotherWay._g_record_keydown && Test.AnotherWay._g_record_ctrl_keydown && Test.AnotherWay._g_record_shift_keydown  ) {
-		if( k=="s" ) {
-			Test.AnotherWay._g_record_started=!Test.AnotherWay._g_record_started;
-			if( Test.AnotherWay._g_record_started ) {
-				Test.AnotherWay._g_record_events=[];
-				Test.AnotherWay._g_record_start_time=(new Date()).getTime();
-				Test.AnotherWay._record_control_update_time();
-				Test.AnotherWay._g_record_update_time_interval=window.setInterval( Test.AnotherWay._record_control_update_time, 200 );
-			}else {
-				Test.AnotherWay._record_control_update_highlight( null );
-				if( !Test.AnotherWay._g_record_paused ) {
-					window.clearInterval( Test.AnotherWay._g_record_update_time_interval );
-				}
-				Test.AnotherWay._g_record_waiting_for_results=true;
-				// open a new window for self, pass a parameter to dump recorded events as javascript code there
-				// (the easiest way to obtain a document from the same origin, so it's writable, is to open this same page again)
-				Test.AnotherWay._g_record_paused=false;
-				var loc=window.location;
-				loc=loc.protocol+"//"+loc.host+loc.pathname+"?recording_results="+Test.AnotherWay._g_record_random_id;
-				if( window.open( loc, "_blank" )==null ) {
-					alert( "unable to open new window for results" );
-				}
-			}
-			handled=true;
-		}else if( k=="h" ) {
-			Test.AnotherWay._g_record_control_visible=!Test.AnotherWay._g_record_control_visible;
-			handled=true;
-		}else if( k=="p" ) {
-			Test.AnotherWay._g_record_paused=!Test.AnotherWay._g_record_paused;
-			if( Test.AnotherWay._g_record_paused ) {
-				Test.AnotherWay._g_record_pause_start_time=(new Date()).getTime();
-				if( Test.AnotherWay._g_record_started ) {
-					window.clearInterval( Test.AnotherWay._g_record_update_time_interval );
-				}
-				Test.AnotherWay._record_control_update_highlight( null );
-			}else {
-				var pause_duration=(new Date()).getTime()-Test.AnotherWay._g_record_pause_start_time;
-				Test.AnotherWay._g_record_start_time+=pause_duration;
-				Test.AnotherWay._g_record_update_time_interval=window.setInterval( Test.AnotherWay._record_control_update_time, 200 );
-			}
-			handled=true;
-		}else if( k=="m" ) {
-			Test.AnotherWay._g_record_include_mousemove=!Test.AnotherWay._g_record_include_mousemove;
-			handled=true;
-		}else if( k=="c" ) {
-			var o=Test.AnotherWay._record_checkpoint();
-			Test.AnotherWay._record_display_checkpoint( o );
-			Test.AnotherWay._record_flash_border( "#24d" );
-			handled=true;
-		}
-	}
-	Test.AnotherWay._g_record_keydown=null;
-	Test.AnotherWay._record_control_update_ui();
-	if( !handled ) {
-//		Test.AnotherWay._record_event( event ); // replaying is not supported in any known browser
-	}
-	return;
-}
-Test.AnotherWay._record_html_node_path=function( node )
-{
-	if( node==null ) {
-		return null;
-	}
-	var path=[];
-	while( true ) {
-		if( node.id!=null && node.id!="" ) {
-			path.unshift( "#"+node.id+" "+node.nodeName );
-			break;
-		}else {
-			var parent_node=node.parentNode;
-			if( parent_node==null ) {
-				return []; // no BODY up the path - this node is screwed (browsers differ in what's above the body), discard
-			}else {
-				var i=0;
-				var found=false;
-				for( var child=parent_node.firstChild; child!=null; child=child.nextSibling ) {
-					if( child==node ) {
-						found=true;
-						break;
-					}
-					if( child.nodeType==1 ) { // count only HTML element nodes
-						++i;
-					}
-				}
-				if( !found ) {
-					i=-1;
-				}
-				path.unshift( i+" "+node.nodeName );
-				if( parent_node.nodeName=="BODY" || parent_node.nodeName=="body" ) {
-					break;
-				}
-				node=parent_node;
-			}
-		}
-	}
-	return path;
-}
-Test.AnotherWay._record_node_path_to_string=function( path )
-{
-	var s="";
-	if( path!=null ) {
-		for( var i=0; i<path.length; ++i ) {
-			s+= i==0 ? "" : ", ";
-			var elem=path[i].split( " " );
-			if( elem[0].charAt( 0 )=="#" ) {
-				s+=elem[1]+" "+elem[0];
-			}else {
-				s+=elem[1]+" ["+elem[0]+"]";
-			}
-		}
-	}
-	return s;
-}
-Test.AnotherWay._record_node_path_to_node=function( path_str, doc )
-{
-	if( path_str==null ) {
-		return null;
-	}
-	var path=path_str.split( "," );
-	var node=doc.body;
-	for( var i=0; i<path.length; ++i ) {
-		var node_i=path[i].split( " " )[0];
-		if( node_i.charAt( 0 )=="#" ) {
-			node=doc.getElementById( node_i.substring( 1 ) );
-		}else {
-			if( node_i<0 || node_i>=node.childNodes.length ) {
-				node=null;
-			}else {
-				node=node.firstChild;
-				while( node!=null ) {
-					if( node.nodeType==1 ) {  // count only HTML element nodes
-						if( node_i==0 ) {
-							break;
-						}
-						--node_i;
-					}
-					node=node.nextSibling;
-				}
-			}
-		}
-		if( node==null ) {
-			return null;
-		}
-	}
-	return node;
-}
-Test.AnotherWay._record_control_contains_id=function( s )
-{
-	return s.match( /^#record_[\w_]+/ ) && s.match( Test.AnotherWay._g_record_random_id );
-}
-Test.AnotherWay._record_checkpoint=function()
-{
-	var o={ type: "_checkpoint", time: (new Date()).getTime()-Test.AnotherWay._g_record_start_time, which: Test.AnotherWay._g_record_checkpoint_count++,
-			target: Test.AnotherWay._record_html_node_path( Test.AnotherWay._g_record_under_cursor ) };
-	Test.AnotherWay._g_record_events.push( o );
-	return o;
-}
-Test.AnotherWay._record_event=function( event )
-{
-	var unneeded=["rangeOffset","eventPhase","timeStamp","isTrusted","popupWindowFeatures","rangeOffset"];
-	if( Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused ) {
-		var o={};
-		for( var n in event ) {
-			var needed=!n.match( /^[A-Z0-9_]+$/ );
-			if( needed ) {
-				for( var ui=0; ui<unneeded.length; ++ui ) {
-					if( unneeded[ui]==n ) {
-						needed=false;
-						break;
-					}
-				}
-				if( needed ) {
-					var value=event[n];
-					if( typeof( value )!="object" && typeof( value )!="function" ) {
-						o[n]=value;
-					}else if( n=="target" || n=="relatedTarget" ) {
-						o[n]=Test.AnotherWay._record_html_node_path( value );
-					}
-				}
-			}
-		}
-		o["time"]=(new Date()).getTime()-Test.AnotherWay._g_record_start_time;
-		var over_record_control= o["target"]!=null && o["target"][0]!=null && Test.AnotherWay._record_control_contains_id( o["target"][0] );
-		if( !over_record_control ) {
-			Test.AnotherWay._g_record_events.push( o );
-		}
-	}
-	return true;
-}
-Test.AnotherWay._record_on_mousemove=function( event )
-{
-	var path=Test.AnotherWay._record_html_node_path( event.target );
-	var new_mouse_over_record_control= path!=null && path[0]!=null && Test.AnotherWay._record_control_contains_id( path[0] );
-	if( new_mouse_over_record_control!=Test.AnotherWay._g_record_mouse_over_record_control ) {
-		Test.AnotherWay._g_record_mouse_over_record_control=new_mouse_over_record_control;
-		Test.AnotherWay._record_control_update_ui();
-	}
-	if( event.target!=null && event.target!=Test.AnotherWay._g_record_under_cursor ) {
-		Test.AnotherWay._g_record_under_cursor=event.target;
-		var s="";
-		if( path==null || path[0]==null || !Test.AnotherWay._record_control_contains_id( path[0] ) ) {
-			s=Test.AnotherWay._record_node_path_to_string( path );
-		}
-		if( s=="" ) {
-			s="&nbsp;";
-		}
-		var cursor_over_indicator=Test.AnotherWay._record_control_get_element( "record_cursor_over" );
-		cursor_over_indicator.innerHTML=s;
-	}
-
-	var highlight_element=null;
-	if( !Test.AnotherWay._g_record_mouse_over_record_control && Test.AnotherWay._g_record_started && !Test.AnotherWay._g_record_paused ) {
-		highlight_element=event.target;
-	}
-	// highlight border disabled on recording - it causes page to scroll, issuing spurious mouseover/mouseout event
-	//Test.AnotherWay._record_control_update_highlight( highlight_element, "border" );
-
-	if( Test.AnotherWay._g_record_include_mousemove ) {
-		Test.AnotherWay._record_event( event );
-	}
-	return true;
-}
-Test.AnotherWay._record_display_checkpoint=function( o )
-{
-	var checkpoints_div=Test.AnotherWay._record_control_get_element( "record_checkpoints" );
-	var p=checkpoints_div.appendChild( checkpoints_div.ownerDocument.createElement( "div" ) );
-	p.style.marginTop="3px";
-	p.style.font="normal normal 8pt sans-serif";
-	p.style.color="#000";
-	p.style.textAligh="left";
-	p.style.position="relative";
-	p.style.width="100%";
-	var checkpoint_text="";
-	checkpoint_text+="#"+(o.which+1);
-	checkpoint_text+="  "+Test.AnotherWay._record_format_time( o.time );
-	if( o.target!=null ) {
-		checkpoint_text+=Test.AnotherWay._record_node_path_to_string( o.target );
-	}
-	p.appendChild( p.ownerDocument.createTextNode( checkpoint_text ) );
-}
-Test.AnotherWay._record_save_results=function( doc )
-{
-	// strange, but DOM-style append does not work here in opera 8.
-	var append=function( s ) { doc.write( "<div>"+s+"</div>" ); };
-	append( "/* paste this data into your javascript and pass it as an argument to replay_events method */" );
-	append( "{ checkpoints: [" );
-	var first_checkpoint=true;
-	for( var i=0; i<Test.AnotherWay._g_record_events.length; ++i ) {
-		var o=Test.AnotherWay._g_record_events[i];
-		if( o.type=="_checkpoint" ) {
-			var str= first_checkpoint ? "" : "}, ";
-			str+="function( tst, wnd ) { // #"+o.which+" time "+Test.AnotherWay._record_format_time( o.time )+" cursor was over "+Test.AnotherWay._record_node_path_to_string( o.target );
-			append( str );
-			first_checkpoint=false;
-		}
-	}
-	if( !first_checkpoint ) {
-		append( "}" );
-	}
-	append( "], events: [ " );
-	var prev_time=0;
-	for( var i=0; i<Test.AnotherWay._g_record_events.length; ++i ) {
-		var o=Test.AnotherWay._g_record_events[i];
-		var s="";
-		s+= "{";
-		var n_first=true;
-		for( var n in o ) {
-			if( n=="time" ) { // convert to relative time
-				var cur_time=o[n]-0;
-				o[n]=cur_time-prev_time;
-				prev_time=cur_time;
-			}
-			s+=n_first ? n : ", "+n;
-			s+=":";
-			if( o[n]==null ) {
-				s+="null";
-			}else {
-				s+="\""+o[n]+"\"";
-			}
-			n_first=false;
-		}
-		s+= i==Test.AnotherWay._g_record_events.length-1 ? "}" : "},";
-		append( s );
-	}
-	append( "] }" );
-	append( ";" );
-}
-
-Test.AnotherWay._g_record_border; // border highlighting element under cursor
-Test.AnotherWay._g_record_border_flashes=[]; // array of { color: color, timeout: milliseconds }
-Test.AnotherWay._g_record_border_flashing=false;
-Test.AnotherWay._g_record_border_normal_color="#d4b";
-Test.AnotherWay._record_flash_border_timeout=function()
-{
-	var color=Test.AnotherWay._g_record_border_normal_color;
-	var timeout=null;
-	if( Test.AnotherWay._g_record_border_flashes.length!=0 ) {
-		color=Test.AnotherWay._g_record_border_flashes[0].color;
-		timeout=Test.AnotherWay._g_record_border_flashes[0].timeout;
-		Test.AnotherWay._g_record_border_flashes.splice( 0, 1 );
-	}
-	if( Test.AnotherWay._g_record_border!=null ) {
-		for( var i=0; i<Test.AnotherWay._g_record_border.length; ++i ) {
-			Test.AnotherWay._g_record_border[i].style.backgroundColor=color;
-		}
-	}
-	if( timeout!=null ) {
-		setTimeout( Test.AnotherWay._record_flash_border_timeout, timeout );
-	}else {
-		Test.AnotherWay._g_record_border_flashing=false;
-	}
-}
-Test.AnotherWay._get_page_coords=function( elm )
-{
-	var point = { x: 0, y: 0 };
-	while( elm )  {
-		point.x+=elm.offsetLeft;
-		point.y+=elm.offsetTop;
-		elm=elm.offsetParent;
-	 }
-	return point;
-}
-Test.AnotherWay._set_page_coords=function( elm, x, y )
-{
-	var parent_coords={ x: 0, y: 0 };
-	if( elm.offsetParent )  {
-		parent_coords=Test.AnotherWay._get_page_coords( elm.offsetParent );
-	}
-	var new_x=x-parent_coords.x;
-	if( new_x<0 ) {
-		new_x=0;
-	}
-	elm.style.left=new_x+'px';
-	var new_y=y-parent_coords.y;
-	if( new_y<0 ) {
-		new_y=0;
-	}
-	elm.style.top=new_y+'px';
-}
-Test.AnotherWay._record_setup_highlight_positions=function( element, style, coords, positions )
-{
-	if( style=="border" ) {
-		var width=element.clientWidth;
-		var height=element.clientHeight;
-		var step=0;
-		var thickness=2;
-		var fudge_expand=4;
-		positions.push( { x: coords.x-step-thickness, y: coords.y-step-thickness, width: width+2*step+2*thickness+fudge_expand, height: thickness } );
-		positions.push( { x: coords.x+width+step+fudge_expand, y: coords.y-step-thickness, width: thickness, height: height+2*step+2*thickness+fudge_expand } );
-		positions.push( { x:positions[0].x, y:positions[0].y, width:positions[0].width, height:positions[0].height } );
-		positions.push( { x:positions[1].x, y:positions[1].y, width:positions[1].width, height:positions[1].height } );
-		positions[2].y+=height+thickness+2*step+fudge_expand;
-		positions[3].x-=width+thickness+2*step+fudge_expand;
-	}else if( style=="ball" ) {
-		positions.push( { x: coords.x+2, y: coords.y, width: 2, height: 6 } );
-		positions.push( { x: coords.x, y: coords.y+2, width: 6, height: 2 } );
-		positions.push( { x: coords.x+1, y: coords.y+1, width: 4, height: 4 } );
-	}
-}
-Test.AnotherWay._record_highlight_border=function( element, style, event ) // null - hide border
-{
-	if( element!=null ) {
-		if( Test.AnotherWay._g_record_border==null || Test.AnotherWay._g_record_border[0].ownerDocument!=element.ownerDocument ) {
-			Test.AnotherWay._g_record_border=[];
-			var n= style=="border" ? 4 : style=="ball" ? 3 : 0;
-			for( var i=0; i<4; ++i ) {
-				var b=element.ownerDocument.createElement( "div" );
-				b.style.position="absolute";
-				b.style.zIndex="1";
-				b.style.backgroundColor=Test.AnotherWay._g_record_border_normal_color;
-				element.ownerDocument.body.appendChild( b );
-				Test.AnotherWay._g_record_border.push( b );
-			}
-		}
-		var coords=null;
-		if( style=="border" ) {
-			coords=Test.AnotherWay._get_page_coords( element );
-		}else if( style=="ball" ) {
-			if( event!=null ) {
-				if( event.pageX!=null && event.pageY!=null ) {
-					coords={ x: event.pageX-0, y: event.pageY-0 };
-				}else if( event.clientX!=null && event.clientY!=null ) {
-					var doc=element.ownerDocument;
-					if( doc!=null ) {
-						coords={ x: (event.clientX-0)+doc.body.scrollLeft, y: (event.clientY-0)+doc.body.scrollTop };
-					}
-				}
-			}
-		}
-		if( coords!=null && element.clientWidth!=null && element.clientHeight!=null ) {
-			var positions=[];
-			Test.AnotherWay._record_setup_highlight_positions( element, style, coords, positions );
-			for( var i=0; i<positions.length; ++i ) {
-				var b=Test.AnotherWay._g_record_border[i];
-				var p=positions[i];
-				Test.AnotherWay._set_page_coords( b, p.x, p.y );
-				b.style.width=p.width+"px";
-				b.style.height=p.height+"px";
-				b.style.display="block";
-			}
-		}
-	}else {
-		if( Test.AnotherWay._g_record_border!=null ) {
-			for( var i=0; i<Test.AnotherWay._g_record_border.length; ++i ) {
-				Test.AnotherWay._g_record_border[i].style.display="none";
-			}
-		}
-	}
-}
-Test.AnotherWay._record_flash_border=function( color )
-{
-	if( Test.AnotherWay._g_record_border_flashing ) { //already
-		Test.AnotherWay._g_record_border_flashes.push( { color: Test.AnotherWay._g_record_border_normal_color, timeout:300 } );
-		Test.AnotherWay._g_record_border_flashes.push( { color: color, timeout:600 } );
-	}else {
-		Test.AnotherWay._g_record_border_flashing=true;
-		Test.AnotherWay._g_record_border_flashes.push( { color: color, timeout:600 } );
-		Test.AnotherWay._record_flash_border_timeout();
-	}
-}
-Test.AnotherWay._record_prepare_doc_for_results=function()
-{
-	document.open();
-	document.write( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">" );
-	document.write( "<html><head><title> Input recording results</title>" );
-	document.write( "<style type=\"text/css\">" );
-	document.write( "body { font: normal normal smaller sans-serif; }" );
-	document.write( "div { margin-top: 3px; }" );
-	document.write( "</style></head><body>" );
-	// opera and mozilla disagree over who the opener is.
-	if( typeof( window.opener.Test )!="undefined" && typeof( window.opener.Test.AnotherWay )!="undefined" ) {
-		window.opener.Test.AnotherWay._record_save_results( document );
-		window.opener.Test.AnotherWay._g_record_waiting_for_results=false;
-		window.opener.Test.AnotherWay._record_control_update_ui();
-	}else if( typeof( window.opener.opener.Test  )!="undefined" && typeof( window.opener.opener.Test.AnotherWay )!="undefined" ) {
-		window.opener.opener.Test.AnotherWay._record_save_results( document );
-		window.opener.opener.Test.AnotherWay._g_record_waiting_for_results=false;
-		window.opener.opener.Test.AnotherWay._record_control_update_ui();
-	}
-	document.write( "</body>" );
-	document.close();
-}
-
-// global initialization
-onload=function()
-{
-	if( window.opera ) {
-		var good_opera=typeof( window.opera.version )=="function";
-		good_opera=good_opera && window.opera.version().match( /^\s*(\d+)/ );
-		good_opera=good_opera && RegExp.$1>=8;
-	}
-	var span=document.createElement( "SPAN" );
-	span.innerHTML="<!--[if IE]><br /><![endif]-"+"->";
-	var is_ie=span.getElementsByTagName( "BR" ).length>0;
-
-	Test.AnotherWay._g_test_iframe=window.frames.test_iframe;
-
-	var query_str=window.location.search;
-	if( query_str.charAt( 0 )=="?" ) {
-		query_str=query_str.substring( 1 );
-	}
-	var testlist_page="list-tests.html";
-	var auto_run=false;
-	if( query_str!="" ) {
-		var params=[query_str];
-		if( query_str.indexOf( ";" )!=-1 ) {
-			params=query_str.split( ";" );
-		}else if( query_str.indexOf( "&" )!=-1 ) {
-			params=query_str.split( "&" );
-		}
-		for( var param_i=0; param_i<params.length; ++param_i ) {
-			var param=params[param_i].split( "=" );
-			if( param[0]=="recording_results" ) {
-				if( window.opener!=null ) {
-					// we were told to show recording results - replace everything in the document with the results
-					Test.AnotherWay._record_prepare_doc_for_results();
-					return;
-				}
-			}else if( param[0]=="testpage" ) {
-				Test.AnotherWay._add_test_page_url( decodeURIComponent( param[1] ), "anotherway" );
-			}else if( param[0]=="jsantestpage" ) {
-				Test.AnotherWay._add_test_page_url( decodeURIComponent( param[1] ), "jsan" );
-			}else if( param[0]=="testlist" ) {
-				testlist_page=decodeURIComponent( param[1] );
-			}else if( param[0]=="testframe" ) {
-				if( window.opera && !good_opera ) {
-					Test.AnotherWay._show_error( "testframe parameter does not work in versions of Opera prior to 8.0. Sorry (pathches are welcome)." );
-					// Opera 7 barfs on attempt to access frame.frameElement.
-					// if someone knows a way to assign onload handler to that iframe in Opera 7
-					// without disrupting code that works in other browsers, patches are welcome.
-				}else {
-					var frame_path=param[1].split( "." );
-					var frame=top;
-					for( var frame_path_i=0; frame_path_i<frame_path.length; ++frame_path_i ) {
-						frame=frame[frame_path[frame_path_i]];
-					}
-					if( frame==null ) {
-						Test.AnotherWay._show_error( "unable to find frame specified for loading test pages: "+param[1] );
-					}else {
-						if( frame.frameElement!=null ) { // for the following assignement to onload to work, frameElement is required
-							frame=frame.frameElement;
-						}
-						Test.AnotherWay._g_test_iframe=frame;
-					}
-				}
-			}else if( param[0]=="testframe_no_clear" ) {
-				Test.AnotherWay._g_test_frame_no_clear=true;
-			}else if( param[0]=="windows" ) {
-                if (param[1] == "none") {
-                    Test.AnotherWay._test_object_t.prototype.open_window=null;
-                }
-			}else if( param[0]=="run" ) {
-				auto_run=true;
-				if( param[1]=="all" ) {
-					Test.AnotherWay._g_pages_to_run="all";
-				}else {
-					if( Test.AnotherWay._g_pages_to_run==null || Test.AnotherWay._g_pages_to_run=="all" ) {
-						Test.AnotherWay._g_pages_to_run=[];
-					}
-					var pages=param[1].split( "," );
-					for( var i=0; i<pages.length; ++i ) {
-						Test.AnotherWay._g_pages_to_run.push( pages[i] );
-					}
-				}
-			}
-		}
-	}
-	if( Test.AnotherWay._g_test_page_urls.length==0 ) {  // if no individual pages were given on the command line, load the list
-		var result=Test.AnotherWay._set_iframe_location( window.frames["list_iframe"], testlist_page );
-		if( result.msg!=null ) {
-			Test.AnotherWay._show_error( result.msg );
-		}
-		Test.AnotherWay._g_run_on_list_load=auto_run;
-	}else {
-		Test.AnotherWay._g_run_on_main_load=auto_run;
-	}
-
-	var f=Test.AnotherWay._g_test_iframe;
-	try {
-		if( f.attachEvent!=null ) {
-			f.attachEvent( "onload", Test.AnotherWay._test_page_onload );
-		}else {
-			f.onload=Test.AnotherWay._test_page_onload;
-		}
-		if( Test.AnotherWay._g_test_iframe.nodeType!=null && Test.AnotherWay._g_test_iframe.contentWindow!=null ) { // it's iframe element, not the iframe. we need iframe.
-			Test.AnotherWay._g_test_iframe=Test.AnotherWay._g_test_iframe.contentWindow;
-		}
-	}catch(e) {
-		// ignore stupid opera error if the frame has onload handler assigned in the inline html
-	}
-	var handlers={
-		"run_all": { "onclick": Test.AnotherWay._run_all_onclick },
-		"run_selected": { "onclick": Test.AnotherWay._run_selected_onclick },
-		"unselect_all": { "onclick": Test.AnotherWay._unselect_all_onclick },
-		"record_select": { "onfocus": Test.AnotherWay._record_check_onfocus },
-		"record_input": { "onfocus": Test.AnotherWay._record_check_onfocus },
-		"record_start": { "onclick": Test.AnotherWay._record_start_onclick },
-		"clear_btn": { "onclick": Test.AnotherWay._results_clear_onclick },
-		"results_tab": { "onclick": Test.AnotherWay._tab_onclick, "onmouseover": Test.AnotherWay._tab_mouseover, "onmouseout": Test.AnotherWay._tab_mouseout },
-		"debug_tab": { "onclick": Test.AnotherWay._tab_onclick, "onmouseover": Test.AnotherWay._tab_mouseover, "onmouseout": Test.AnotherWay._tab_mouseout }
-	};
-	for( var hs in handlers ) {
-		var o=document.getElementById( hs );
-		if( o!=null ) {
-			for( var h in handlers[hs] ) {
-				o[h]=handlers[hs][h];
-			}
-		}else {
-			Test.AnotherWay._show_error( "unable to set "+h+" handler: id "+hs+" not found" );
-		}
-	}
-
-	if( window.opera && !good_opera ) {
-		Test.AnotherWay._g_no_record_msg="Input events recording and replaying is not available in opera versions prior to 8.0.";
-	}
-	if( is_ie ) {
-		Test.AnotherWay._g_no_record_msg="Input events recording and replaying is not available in internet explorer.";
-	}
- 	if( Test.AnotherWay._g_no_record_msg!=null ) {
-		var no_record_p=document.getElementById( "record_not_supported" );
-		no_record_p.style.display="block";
-		no_record_p.appendChild( document.createTextNode( Test.AnotherWay._g_no_record_msg ) );
-	}
-
-	Test.AnotherWay._g_main_loaded=true;
-	if( Test.AnotherWay._g_run_on_main_load ) {
-		Test.AnotherWay._g_run_on_main_load=false;
-		Test.AnotherWay._run_pages_to_run();
-	}
-}
-// -->
-</script>
-<script type="text/javascript" src="xml_eq.js"></script>
-<script type="text/javascript" src="geom_eq.js"></script>
-</head><body>
-
-<div id="col1">
-<div id="col1_header">Test pages:</div>
-<div id="scroller">
-<table id="testtable">
-</table>
-</div>
-<div id="run_buttons">
-<input type="button" value=" clear " id="clear_btn" />
-<input type="button" value=" run all " id="run_all" />
-<input type="button" value=" run selected " id="run_selected" />
-<input type="button" value=" unselect all " id="unselect_all" />
-</div>
-<input type="checkbox" id="dont_close_test_windows" /> do not close windows opened by tests
-<div id="error"></div>
-<div id="record_div">
-<p id="record_not_supported" style="display:none"></p>
-<p>Record mouse input for the page:</p>
-<p><input type="radio" name="record_choose" value="select" checked="checked" /> <select id="record_select"><option selected="selected">-- select a page: --</option></select></p>
-<p><input type="radio" name="record_choose" value="input" /> or enter page url: <input type="text" id="record_input" /></p>
-<p><input type="button" value=" record " id="record_start" /></p>
-</div>
-</div>
-
-<div id="col2">
-<div id="right_header">
-<span id="results_count">Results: <span id="total"></span></span>
-<span id="results_tab" class="active_tab" style="visibility:hidden">Results</span>
-<span id="debug_tab" class="inactive_tab" style="visibility:hidden">Debug</span>
-</div>
-<div id="right_frame">
-<div id="results"></div>
-<div id="debug"></div>
-</div>
-</div>
-
-<span style="display:none">
-<iframe name="list_iframe" onload="Test.AnotherWay._list_iframe_onload();"></iframe>
-<iframe name="test_iframe" onload="Test.AnotherWay._test_page_onload();"></iframe>
-
-<!-- record_control div is to be imported into other documents, so all its styles are inline -->
--<div id="record_control" style="position:absolute;bottom:0;left:0;margin:0;padding:0.5em;width:22em;height:22em;border:1px solid;background:#ffd;font: normal normal 8pt sans-serif; color:#000; text-align: left">
-
-<p style="margin:0 0 0 0; padding:0">
-&nbsp;
-<span style="display:none;font-weight:bold;color:#408" id="record_indicator">
-recording. <span style="font-weight:normal">time: <span id="record_time"></span></span><span id="record_pause_indicator"> paused</span>
-</span>
-</p>
-
-<div id="record_cursor_over" style="margin:0;padding:2px;width:14em;height:1.1em;overflow:hidden;float:right;border:1px solid #777;background:#fff;font: normal normal 8pt sans-serif;position:relative;top:3px;color:#000;text-align:left;">&nbsp;</div>
-<p style="margin:2px 0 0 0; padding:0">
-cursor is over
-</p>
-
-<p style="margin:8px 0 0 0; padding:0;">
- keyboard control: press
- <span id="record_ctrl_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">ctrl</span> -
- <span id="record_shift_key" style="border:1px solid #226;background:#adf;padding:0 0.5em">shift</span> -
-</p>
-
-<p style="margin:4px 0 0 0; padding:0">
-<span id="record_s" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">s</span>
-<span id="record_on">to <b>start</b> recording</span>
-<span id="record_off" style="display:none">to <b>stop</b> recording</span>
-</p>
-
-<p style="margin:4px 0 0 0; padding:0">
-<span id="record_h" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">h</span>
-<span>to <b>hide/show</b> this window</span>
-</p>
-
-<p style="margin:4px 0 0 0; padding:0">
-<span id="record_m" style="border:1px solid #226;background:#adf;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">m</span>
-<span id="record_include_mousemove">to <b> record</b> mousemove</span>
-<span id="record_omit_mousemove" style="display:none">to <b>omit</b> mousemove</span>
-</p>
-
-<p style="margin:4px 0 0 0; padding:0">
-<span id="record_p" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">p</span>
-<span id="record_pause_on">to <b> pause</b> recording</span>
-<span id="record_pause_off" style="display:none">to <b>continue</b> recording</span>
-</p>
-
-<p style="margin:4px 0 0 0; padding:0">
-<span id="record_c" style="border:1px solid #226;background:#aaa;width:1.2em;float:left;font-weight:bold;text-align:center;margin-right:0.5em">c</span>
-<span>to add checkpoint</span>
-</p>
-
-<p style="margin:6px 0 0 0; padding:0">
-checkpoints:
-</p>
-<div id="record_checkpoints" style="position:relative;width:100%;height:6em;overflow:auto;font: normal normal 8pt sans-serif; color:#000; text-align: left">
-</div>
-</div>
-
-</span>
-<script>
-    if (/noscroll/.test(location.href)) {
-        document.getElementById('scroller').style.height='auto';
-        document.getElementById('right_frame').style.height='auto';
-    }
-</script>
-</body></html>
+        </script>
+    </body>
+</html>

Deleted: sandbox/marcjansen/testsuite/tests/xml_eq.js
===================================================================
--- sandbox/marcjansen/testsuite/tests/xml_eq.js	2011-09-17 00:09:27 UTC (rev 12373)
+++ sandbox/marcjansen/testsuite/tests/xml_eq.js	2011-09-17 00:52:04 UTC (rev 12374)
@@ -1,311 +0,0 @@
-/**
- * File: xml_eq.js
- * Adds a xml_eq method to AnotherWay test objects.
- *
- */
-
-(function() {
-
-    /**
-     * Function: createNode
-     * Given a string, try to create an XML DOM node.  Throws string messages
-     *     on failure.
-     * 
-     * Parameters:
-     * text - {String} An XML string.
-     *
-     * Returns:
-     * {DOMElement} An element node.
-     */
-    function createNode(text) {
-        
-        var index = text.indexOf('<');
-        if(index > 0) {
-            text = text.substring(index);
-        }
-        
-        var doc;
-        if(window.ActiveXObject && !this.xmldom) {
-            doc = new ActiveXObject("Microsoft.XMLDOM");
-            try {
-                doc.loadXML(text);
-            } catch(err) {
-                throw "ActiveXObject loadXML failed: " + err;
-            }
-        } else if(window.DOMParser) {
-            try {
-                doc = new DOMParser().parseFromString(text, 'text/xml');
-            } catch(err) {
-                throw "DOMParser.parseFromString failed";
-            }
-            if(doc.documentElement && doc.documentElement.nodeName == "parsererror") {
-                throw "DOMParser.parseFromString returned parsererror";
-            }
-        } else {
-            var req = new XMLHttpRequest();
-            req.open("GET", "data:text/xml;charset=utf-8," +
-                     encodeURIComponent(text), false);
-            if(req.overrideMimeType) {
-                req.overrideMimeType("text/xml");
-            }
-            req.send(null);
-            doc = req.responseXML;
-        }
-        
-        var root = doc.documentElement;
-        if(!root) {
-            throw "no documentElement";
-        }
-        return root;
-    }
-    
-    /**
-     * Function assertEqual
-     * Test two objects for equivalence (based on ==).  Throw an exception
-     *     if not equivalent.
-     * 
-     * Parameters:
-     * got - {Object}
-     * expected - {Object}
-     * msg - {String} The message to be thrown.  This message will be appended
-     *     with ": got {got} but expected {expected}" where got and expected are
-     *     replaced with string representations of the above arguments.
-     */
-    function assertEqual(got, expected, msg) {
-        if(got === undefined) {
-            got = "undefined";
-        } else if (got === null) {
-            got = "null";
-        }
-        if(expected === undefined) {
-            expected = "undefined";
-        } else if (expected === null) {
-            expected = "null";
-        }
-        if(got != expected) {
-            throw msg + ": got '" + got + "' but expected '" + expected + "'";
-        }
-    }
-    
-    /**
-     * Function assertElementNodesEqual
-     * Test two element nodes for equivalence.  Nodes are considered equivalent
-     *     if they are of the same type, have the same name, have the same
-     *     namespace prefix and uri, and if all child nodes are equivalent.
-     *     Throws a message as exception if not equivalent.
-     * 
-     * Parameters:
-     * got - {DOMElement}
-     * expected - {DOMElement}
-     * options - {Object} Optional object for configuring test options.
-     *
-     * Valid options:
-     * prefix - {Boolean} Compare element and attribute
-     *     prefixes (namespace uri always tested).  Default is false.
-     * includeWhiteSpace - {Boolean} Include whitespace only nodes when
-     *     comparing child nodes.  Default is false.
-     */
-    function assertElementNodesEqual(got, expected, options) {
-        var testPrefix = (options && options.prefix === true);
-        
-        // compare types
-        assertEqual(got.nodeType, expected.nodeType, "Node type mismatch");
-        
-        // compare names
-        var gotName = testPrefix ?
-            got.nodeName : got.nodeName.split(":").pop();
-        var expName = testPrefix ?
-            expected.nodeName : expected.nodeName.split(":").pop();
-        assertEqual(gotName, expName, "Node name mismatch");
-        
-        // for text nodes compare value
-        if(got.nodeType == 3) {
-            assertEqual(
-                got.nodeValue, expected.nodeValue, "Node value mismatch"
-            );
-        }
-        // for element type nodes compare namespace, attributes, and children
-        else if(got.nodeType == 1) {
-            
-            // test namespace alias and uri
-            if(got.prefix || expected.prefix) {
-                if(testPrefix) {
-                    assertEqual(
-                        got.prefix, expected.prefix,
-                        "Bad prefix for " + got.nodeName
-                    );
-                }
-            }
-            if(got.namespaceURI || expected.namespaceURI) {
-                assertEqual(
-                    got.namespaceURI, expected.namespaceURI,
-                    "Bad namespaceURI for " + got.nodeName
-                );
-            }
-            
-            // compare attributes - disregard xmlns given namespace handling above
-            var gotAttrLen = 0;
-            var gotAttr = {};
-            var expAttrLen = 0;
-            var expAttr = {};
-            var ga, ea, gn, en;
-            for(var i=0; i<got.attributes.length; ++i) {
-                ga = got.attributes[i];
-                if(ga.specified === undefined || ga.specified === true) {
-                    if(ga.name.split(":").shift() != "xmlns") {
-                        gn = testPrefix ? ga.name : ga.name.split(":").pop();
-                        gotAttr[gn] = ga;
-                        ++gotAttrLen;
-                    }
-                }
-            }
-            for(var i=0; i<expected.attributes.length; ++i) {
-                ea = expected.attributes[i];
-                if(ea.specified === undefined || ea.specified === true) {
-                    if(ea.name.split(":").shift() != "xmlns") {
-                        en = testPrefix ? ea.name : ea.name.split(":").pop();
-                        expAttr[en] = ea;
-                        ++expAttrLen;
-                    }
-                }
-            }
-            assertEqual(
-                gotAttrLen, expAttrLen,
-                "Attributes length mismatch for " + got.nodeName
-            );
-            var gv, ev;
-            for(var name in gotAttr) {
-                if(expAttr[name] == undefined) {
-                    throw "Attribute name " + gotAttr[name].name + " expected for element " + got.nodeName;
-                }
-                // test attribute namespace
-                assertEqual(
-                    gotAttr[name].namespaceURI, expAttr[name].namespaceURI,
-                    "Attribute namespace mismatch for element " +
-                    got.nodeName + " attribute name " + gotAttr[name].name
-                );
-                // test attribute value
-                assertEqual(
-                    gotAttr[name].value, expAttr[name].value,
-                    "Attribute value mismatch for element " + got.nodeName +
-                    " attribute name " + gotAttr[name].name
-                );
-            }
-            
-            // compare children
-            var gotChildNodes = getChildNodes(got, options);
-            var expChildNodes = getChildNodes(expected, options);
-
-            assertEqual(
-                gotChildNodes.length, expChildNodes.length,
-                "Children length mismatch for " + got.nodeName
-            );
-            for(var j=0; j<gotChildNodes.length; ++j) {
-                try {
-                    assertElementNodesEqual(
-                        gotChildNodes[j], expChildNodes[j], options
-                    );
-                } catch(err) {
-                    throw "Bad child " + j + " for element " + got.nodeName + ": " + err;
-                }
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Function getChildNodes
-     * Returns the child nodes of the specified nodes. By default this method
-     *     will ignore child text nodes which are made up of whitespace content.
-     *     The 'includeWhiteSpace' option is used to control this behaviour.
-     * 
-     * Parameters:
-     * node - {DOMElement}
-     * options - {Object} Optional object for test configuration.
-     * 
-     * Valid options:
-     * includeWhiteSpace - {Boolean} Include whitespace only nodes when
-     *     comparing child nodes.  Default is false.
-     * 
-     * Returns:
-     * {Array} of {DOMElement}
-     */
-    function getChildNodes(node, options) {
-        //check whitespace
-        if (options && options.includeWhiteSpace) {
-            return node.childNodes;
-        }
-        else {
-           nodes = [];
-           for (var i = 0; i < node.childNodes.length; i++ ) {
-              var child = node.childNodes[i];
-              if (child.nodeType == 1) {
-                 //element node, add it 
-                 nodes.push(child);
-              }
-              else if (child.nodeType == 3) {
-                 //text node, add if non empty
-                 if (child.nodeValue && 
-                       child.nodeValue.replace(/^\s*(.*?)\s*$/, "$1") != "" ) { 
-
-                    nodes.push(child);
-                 }
-              }
-           }
-  
-           return nodes;
-        }
-    } 
-    
-    /**
-     * Function: Test.AnotherWay._test_object_t.xml_eq
-     * Test if two XML nodes are equivalent.  Tests for same node types, same
-     *     node names, same namespace URI, same attributes, and recursively
-     *     tests child nodes for same criteria.
-     *
-     * (code)
-     * t.xml_eq(got, expected, message);
-     * (end)
-     * 
-     * Parameters:
-     * got - {DOMElement | String} A DOM node or XML string to test.
-     * expected - {DOMElement | String} The expected DOM node or XML string.
-     * msg - {String} A message to print with test output.
-     * options - {Object} Optional object for configuring test.
-     *
-     * Valid options:
-     * prefix - {Boolean} Compare element and attribute
-     *     prefixes (namespace uri always tested).  Default is false.
-     * includeWhiteSpace - {Boolean} Include whitespace only nodes when
-     *     comparing child nodes.  Default is false.
-     */
-    var proto = Test.AnotherWay._test_object_t.prototype;
-    proto.xml_eq = function(got, expected, msg, options) {
-        // convert arguments to nodes if string
-        if(typeof got == "string") {
-            try {
-                got = createNode(got);
-            } catch(err) {
-                this.fail(msg + ": got argument could not be converted to an XML node: " + err);
-                return;
-            }
-        }
-        if(typeof expected == "string") {
-            try {
-                expected = createNode(expected);
-            } catch(err) {
-                this.fail(msg + ": expected argument could not be converted to an XML node: " + err);
-                return;
-            }
-        }
-        
-        // test nodes for equivalence
-        try {
-            assertElementNodesEqual(got, expected, options);
-            this.ok(true, msg);
-        } catch(err) {
-            this.fail(msg + ": " + err);
-        }
-    }
-    
-})();



More information about the Commits mailing list