<html>
<head>
<script src="../OLLoader.js"></script>
  <script type="text/javascript">

    function setUp() {
        // Stub out functions that are meant to be overridden by
        // subclasses.
        OpenLayers.Renderer.Elements.prototype._createRenderRoot =
            OpenLayers.Renderer.Elements.prototype.createRenderRoot;
        
        var rendererRoot = document.createElement("div");
        OpenLayers.Renderer.Elements.prototype.createRenderRoot = function() {
            return rendererRoot;
        };
        
        OpenLayers.Renderer.Elements.prototype._createRoot =
            OpenLayers.Renderer.Elements.prototype.createRoot;
        
        OpenLayers.Renderer.Elements.prototype.createRoot = function() {
            return document.createElement("div");
        };
        
        OpenLayers.Renderer.Elements.prototype._createNode =
            OpenLayers.Renderer.Elements.prototype.createNode;
        
        OpenLayers.Renderer.Elements.prototype.createNode = function() {
            return document.createElement("div");
        };
    }
    
    // Create a new Elements renderer based on an id and an ordering
    // type. For these tests, both of these parameters are optional.
    function create_renderer(id, options) {
        
        rendererRoot = null;
        
        if (id == null) {
            var el = document.createElement('div');
            document.body.appendChild(el);
            el.id = OpenLayers.Util.createUniqueID();
            id = el.id;
        }
        
        return new OpenLayers.Renderer.Elements(id, options);
    }
    
    // Cleanup stubs made in the function above.
    function tearDown() {
        OpenLayers.Renderer.Elements.prototype.createRenderRoot =
            OpenLayers.Renderer.Elements.prototype._createRenderRoot;
        OpenLayers.Renderer.Elements.prototype.createRoot =
            OpenLayers.Renderer.Elements.prototype._createRoot;
        OpenLayers.Renderer.Elements.prototype.createNode =
            OpenLayers.Renderer.Elements.prototype._createNode;
    }

    function test_Elements_constructor(t) {
        t.plan(6);
        
        setUp();
        
        var r = create_renderer();
        
        t.ok(r instanceof OpenLayers.Renderer.Elements, "new OpenLayers.Renderer.Elements returns Elements object" );
        t.ok(r.rendererRoot != null, "elements rendererRoot is not null");
        t.ok(r.root != null, "elements root is not null");
        t.ok(r.indexer == null, "indexer is null if unused.");
        
        t.ok(r.root.parentNode == r.rendererRoot, "elements root is correctly appended to rendererRoot");
        t.ok(r.rendererRoot.parentNode == r.container, "elements rendererRoot is correctly appended to container");
        
        tearDown();
    }
    
    function test_Elements_destroy(t) {
        t.plan(5);
        
        var elems = {
            'clear': function() {
                t.ok(true, "clear called");
            },
            'rendererRoot': {},
            'root': {},
            'xmlns': {}
        };

        OpenLayers.Renderer.prototype._destroy = 
            OpenLayers.Renderer.prototype.destroy;

        var args = [{}, {}, {}];
        OpenLayers.Renderer.prototype.destroy = function() {
            t.ok((arguments[0] == args[0]) &&
                 (arguments[1] == args[1]) &&
                 (arguments[2] == args[2]), "correct arguments passed to OpenLayers.Renderer.destroy()");
        };

        OpenLayers.Renderer.Elements.prototype.destroy.apply(elems, args);

        t.ok(elems.rendererRoot == null, "rendererRoot nullified");
        t.ok(elems.root == null, "root nullified");
        t.ok(elems.xmlns == null, "xmlns nullified");

        OpenLayers.Renderer.prototype.destroy = 
            OpenLayers.Renderer.prototype._destroy;

    }
    
    function test_Elements_clear(t) {
        t.plan(2);
        
        setUp();
        
        var r = create_renderer();
        var element = document.createElement("div");
        r.root = element;
        
        var node = document.createElement("div");
        element.appendChild(node);
        
        r.clear();
        
        t.ok(r.vectorRoot.childNodes.length == 0, "vector root is correctly cleared");
        t.ok(r.textRoot.childNodes.length == 0, "text root is correctly cleared");
        
        tearDown();
    }
    
    function test_Elements_drawGeometry(t) {
        t.plan(7);

        setUp();

        var r = create_renderer();
        
        var element = document.createElement("div");
        r.vectorRoot = element;

        r.nodeFactory = function(id, type) {
            var element = document.createElement("div");
            return element;
        };
        var g_Node = null;
        var b_Node = null;
        r.drawGeometryNode = function(node, geometry, style) {
            g_Node = node;
            return {node: node, complete: true};
        };
        r.redrawBackgroundNode = function(id, geometry, style, featureId) {
            b_Node = r.nodeFactory();
            b_Node.id = "foo_background";
            element.appendChild(b_Node);
        };

        r.getNodeType = function(geometry, style) {
            return "div";
        };
        var geometry = {
            id: 'foo',
            CLASS_NAME: 'bar',
            getBounds: function() {return {bottom: 0}}
        };
        var style = {'backgroundGraphic': 'foo'};
        var featureId = 'dude';
        r.drawGeometry(geometry, style, featureId);
        t.ok(g_Node.parentNode == element, "node is correctly appended to root");
        t.ok(b_Node.parentNode == element, "redrawBackgroundNode appended background node");
        t.eq(g_Node._featureId, 'dude', "_featureId is correct");
        t.eq(g_Node._style.backgroundGraphic, "foo", "_style is correct");
        t.eq(g_Node._geometryClass, 'bar', "_geometryClass is correct");

        var returnNode = function(id) {
        	return id == "foo_background" ? b_Node : g_Node;
        }
       	
        var _getElement = document.getElementById;
        document.getElementById = returnNode;
        OpenLayers.Util.getElement = returnNode;
        
        style = {'display':'none'};
        r.drawGeometry(geometry, style, featureId);
        t.ok(g_Node.parentNode != element, "node is correctly removed");
        t.ok(b_Node.parentNode != element, "background node correctly removed")
        
        document.getElementById = _getElement;
            
        tearDown();
    }

    function test_Elements_drawGeometry_2(t) {
        t.plan(8);
        
        setUp();
        
        var geometry = {
            getBounds: function() {return {bottom: 0}}
        }
        
        var r = create_renderer();
        
        var element = document.createElement("div");
        r.root = element;
        
        r.nodeFactory = function(id, type) {
            var element = document.createElement("div");
            return element;
        };
        r.setStyle = function(node, style, options, geometry) {
            return node;
        };
        
        // point
        var properDraw = false;
        r.drawPoint = function(node, geometry) {
            properDraw = true;
            return {};
        };
        var point = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.Point'}, geometry);
        style = true;
        r.drawGeometry(point, style);
        t.ok(properDraw, "drawGeometry called drawPoint when passed a point");

        // line string
        var properDraw = false;
        r.drawLineString = function(g) {
            properDraw = true;
            return {};
        };
        var linestring = OpenLayers.Util.applyDefaults({id: "foo", CLASS_NAME: 'OpenLayers.Geometry.LineString'}, geometry);
        style = true;
        r.drawGeometry(linestring, style);
        t.ok(properDraw, "drawGeometry called drawLineString when passed a line string");

        // linear ring
        var properDraw = false;
        r.drawLinearRing = function(g) {
            properDraw = true;
            return {};
        };
        var linearring = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.LinearRing'}, geometry);
        style = true;
        r.drawGeometry(linearring, style);
        t.ok(properDraw, "drawGeometry called drawLinearRing when passed a linear ring");

        // polygon
        var properDraw = false;
        r.drawPolygon = function(g) {
            properDraw = true;
            return {};
        };
        var polygon = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.Polygon'}, geometry);
        style = true;
        r.drawGeometry(polygon, style);
        t.ok(properDraw, "drawGeometry called drawPolygon when passed a polygon");

        // rectangle
        var properDraw = false;
        r.drawRectangle = function(g) {
            properDraw = true;
            return {};
        };
        var rectangle = OpenLayers.Util.applyDefaults({CLASS_NAME: 'OpenLayers.Geometry.Rectangle'}, geometry);
        style = true;
        r.drawGeometry(rectangle, style);
        t.ok(properDraw, "drawGeometry called drawRectangle when passed a rectangle");

        // multi-point
        var properDraw = false;
        r.drawPoint = function(g) {
            properDraw = true;
            return {};
        };
        var multipoint = OpenLayers.Util.applyDefaults({
            CLASS_NAME: 'OpenLayers.Geometry.MultiPoint',
            components: [point]
        }, geometry);
        style = true;
        r.drawGeometry(multipoint, style);
        t.ok(properDraw, "drawGeometry called drawPoint when passed a multi-point");

        // multi-linestring
        var properDraw = false;
        r.drawLineString = function(g) {
            properDraw = true;
            return {};
        };
        var multilinestring = OpenLayers.Util.applyDefaults({
            CLASS_NAME: 'OpenLayers.Geometry.MultiLineString',
            components: [linestring]
        }, geometry);
        style = true;
        r.drawGeometry(multilinestring, style);
        t.ok(properDraw, "drawGeometry called drawLineString when passed a multi-linestring");

        // multi-polygon
        var properDraw = false;
        r.drawPolygon = function(g) {
            properDraw = true;
            return {};
        };
        var multipolygon = OpenLayers.Util.applyDefaults({
            CLASS_NAME: 'OpenLayers.Geometry.MultiPolygon',
            components: [polygon]
        }, geometry);
        style = true;
        r.drawGeometry(multipolygon, style);
        t.ok(properDraw, "drawGeometry called drawPolygon when passed a multi-polygon");
      
        tearDown();
    }
    
    function test_Elements_getfeatureidfromevent(t) {
        t.plan(2);
        
        var node = {
            _featureId: 'foo'
        };
        var event = {
            target: node
        };
        
        var id = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent(event);
        t.eq(id, 'foo', "returned id is correct when event with target is passed");
        
        var event = {
            srcElement: node
        };
        
        var id = OpenLayers.Renderer.Elements.prototype.getFeatureIdFromEvent(event);
        t.eq(id, 'foo', "returned id is correct when event with srcElement is passed");
    }
    
    function test_Elements_erasegeometry(t) {
        t.plan(15);

        var elements = {
            'eraseGeometry': function(geometry) {
                gErased.push(geometry);
            }
        };
        
        var geometry = {
            'components': [{}, {}, {}]
        };
        
      //multipoint
        geometry.CLASS_NAME = "OpenLayers.Geometry.MultiPoint";
        gErased = [];
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gErased[0] == geometry.components[0]) &&
              (gErased[1] == geometry.components[1]) &&
              (gErased[2] == geometry.components[2]), "multipoint all components of geometry correctly erased.");

      //multilinestring
        geometry.CLASS_NAME = "OpenLayers.Geometry.MultiLineString";
        gErased = [];
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gErased[0] == geometry.components[0]) &&
              (gErased[1] == geometry.components[1]) &&
              (gErased[2] == geometry.components[2]), "multilinestring all components of geometry correctly erased.");
      
      //multipolygon
        geometry.CLASS_NAME = "OpenLayers.Geometry.MultiPolygon";
        gErased = [];
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gErased[0] == geometry.components[0]) &&
              (gErased[1] == geometry.components[1]) &&
              (gErased[2] == geometry.components[2]), "multipolygon all components of geometry correctly erased.");
      
      //collection
        geometry.CLASS_NAME = "OpenLayers.Geometry.Collection";
        gErased = [];
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gErased[0] == geometry.components[0]) &&
              (gErased[1] == geometry.components[1]) &&
              (gErased[2] == geometry.components[2]), "collection all components of geometry correctly erased.");
        

    // OTHERS
    //
        geometry.CLASS_NAME = {};

        gElement = null;
        gBackElement = null;

        OpenLayers.Util._getElement = OpenLayers.Util.getElement;
        OpenLayers.Util.getElement = function(id) {
            var retVal = null;
            if (id != null) {
                var hasBack = (id.indexOf(elements.BACKGROUND_ID_SUFFIX) != -1);
                retVal = hasBack ? gBackElement : gElement;
            }
            return retVal;
        };

      //element null
        geometry.id = null;
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        // (no tests here, just make sure it doesn't bomb)      
      
      //element.parentNode null
        elements.BACKGROUND_ID_SUFFIX = 'BLAHBLAHBLAH';
        geometry.id = "foo";        
        gElement = {};
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        // (no tests here, just make sure it doesn't bomb)      
      
      //valid element.parentNode, element.geometry
        elements.indexer = {
            'remove': function(elem) {
                gIndexerRemoved = elem;
            }
        };

        gElement = {
            'geometry': {
                'destroy': function() { 
                    t.ok(true, "geometry destroyed");
                }
            },
            'parentNode': {
                'removeChild': function(elem) {
                    gElemRemoved = elem;
                }
            },
            '_style' : {backgroundGraphic: "foo"}
        };
        gBackElement = {
            'parentNode': {
                'removeChild': function(elem) {
                    gBackRemoved = elem;
                }
            }
        };

        gElemRemoved = gBackRemoved =  gIndexerRemoved = null;
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gElement.geometry == null), "all normal: element's 'geometry' property nullified");
        t.ok( (gElemRemoved == gElement), "all normal: main element properly removed from parent node");
        t.ok( (gBackRemoved == gBackElement), "all normal: back element properly removed from parent node");
        t.ok( (gIndexerRemoved == gElement), "all normal: main element properly removed from the indexer");

      //valid element.parentNode, no element.geometry, no bElem
        gBackElement = null;
        gElement.geometry = null;
        gElemRemoved = gBackRemoved =  gIndexerRemoved = null;
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gElemRemoved == gElement), "no bElem: main element properly removed from parent node");
        t.ok( (gBackRemoved == null), "no bElem: back element not tried to remove from parent node when it doesn't exist");
        t.ok( (gIndexerRemoved == gElement), "no bElem: main element properly removed from the indexer");

      //valid element.parentNode, no element.geometry, valid bElem, no bElem.parentNode 
        gBackElement = {};
        gElemRemoved = gBackRemoved =  gIndexerRemoved = null;
        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]);
        t.ok( (gElemRemoved == gElement), "no bElem.parentNode: main element properly removed from parent node");
        t.ok( (gBackRemoved == null), "no bElem.parentNode: back element not tried to remove from parent node when it has no parent node");
        t.ok( (gIndexerRemoved == gElement), "no bElem.parentNode: main element properly removed from the indexer");


        OpenLayers.Util.getElement = OpenLayers.Util._getElement;
    }
    
    function test_Elements_drawAndErase(t) {
        t.plan(20);

        setUp();

        var r = create_renderer(null, {zIndexing: true});
        var element = document.createElement("div");
        r.vectorRoot = element;
        document.body.appendChild(element);

        r.createNode = function(type, id) {
            var element = document.createElement("div");
            element.id = id;
            return element;
        };
        r.nodeTypeCompare = function() {return true};
        r.setStyle = function(node, style, options, geometry) {
            return node;
        };
        
        var geometry = {
            id: 'foo',
            CLASS_NAME: 'bar',
            getBounds: function() {return {bottom: 0}}
        };
        var style = {
            graphicZIndex: 10
        };
        var featureId = 'foo';
        r.drawGeometry(geometry, style, featureId);

        function count(obj) {
            var result = 0;
            for (var i in obj) {
                result++;
            }
            return result;
        }

        t.eq(element.childNodes.length, 1, "root is correctly filled");
        t.eq(r.indexer.maxZIndex, 10, "indexer.maxZIndex is correctly filled");
        t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
        t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");

        r.eraseGeometry(geometry);

        t.eq(element.childNodes.length, 0, "root is correctly cleared");
        t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset");
        t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset");
        t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset");

        delete(style.graphicZIndex);
        r.drawGeometry(geometry, style, featureId);

        t.eq(element.childNodes.length, 1, "root is correctly filled");
        t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly filled");
        t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
        t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");

        r.clear();

        t.eq(element.childNodes.length, 0, "root is correctly cleared");
        t.eq(r.indexer.maxZIndex, 0, "indexer.maxZIndex is correctly reset");
        t.eq(r.indexer.order.length, 0, "indexer.order is correctly reset");
        t.eq(count(r.indexer.indices), 0, "indexer.indices is correctly reset");

        style.graphicZIndex = 12;
        r.drawGeometry(geometry, style, featureId);

        t.eq(element.childNodes.length, 1, "root is correctly filled");
        t.eq(r.indexer.maxZIndex, 12, "indexer.maxZIndex is correctly filled");
        t.eq(r.indexer.order.length, 1, "indexer.order is correctly filled");
        t.eq(count(r.indexer.indices), 1, "indexer.indices is correctly filled");

        tearDown();
    }

    function test_Elements_moveRoot(t) {
        t.plan(2);
        setUp();
        var r1 = create_renderer();
        var r2 = create_renderer();
        r1.moveRoot(r2);
        t.xml_eq(r1.root.parentNode, r2.root.parentNode, "root moved successfully");
        r1.moveRoot(r1);
        t.xml_eq(r1.root.parentNode, r1.rendererRoot, "root moved back successfully");
        tearDown();        
    }

    function test_Elements_drawGeometry_3(t) {
        t.plan(2);

        setUp();

        var r = create_renderer();
        
        var element = document.createElement("div");
        r.vectorRoot = element;

        r.nodeFactory = function(id, type) {
            return document.createElement("div");
        };
        var g_Node = null;
        var b_Node = null;
        r.drawGeometryNode = function(node, geometry, style) {
            g_Node = node;
            return {node: node, complete: true};
        };
        r.redrawBackgroundNode = function(id, geometry, style, featureId) {
            b_Node = r.nodeFactory();
            b_Node.id = "foo_background";
            element.appendChild(b_Node);
        };

        r.getNodeType = function(geometry, style) {
            return "div";
        };
        var geometry = {
            id: 'foo',
            CLASS_NAME: 'bar',
            getBounds: function() {return {bottom: 0}}
        };
        var style = {'backgroundGraphic': 'foo'};
        var featureId = 'dude';
        r.drawGeometry(geometry, style, featureId);
        t.ok(b_Node.parentNode == element, "redrawBackgroundNode appended background node");

        var returnNode = function(id) {
        	return id == "foo_background" ? b_Node : g_Node;
        }
       	
        var _getElement = document.getElementById;
        document.getElementById = returnNode;
        OpenLayers.Util.getElement = returnNode;

        style = {};
        r.drawGeometry(geometry, style, featureId);
        t.ok(b_Node.parentNode != element, "background node correctly removed")
            
        document.getElementById = _getElement;

        tearDown();
    }

    function test_setExtent(t) {
        t.plan(10);
        setUp();
        var resolution = 1;
        var r = create_renderer();
        r.map = {
            getMaxExtent: function() {
                return new OpenLayers.Bounds(-180,-90,180,90);
            },
            getExtent: function() {
                return r.extent;
            },
            getResolution: function() {
                return resolution;
            },
            baseLayer: {wrapDateLine: true}
        }
        
        r.setExtent(new OpenLayers.Bounds(179, -1, 182, 1), true);
        t.eq(r.rightOfDateLine, true, "on the right side of the dateline");
        t.eq(r.xOffset, r.map.getMaxExtent().getWidth(), "correct xOffset");
        r.setExtent(new OpenLayers.Bounds(179.5, -1, 182.5, 1), false);
        t.eq(r.rightOfDateLine, true, "still on the right side of the dateline");
        t.eq(r.xOffset, r.map.getMaxExtent().getWidth(), "still correct xOffset");
        resolution = 2;
        r.setExtent(new OpenLayers.Bounds(178, -2, 184, 2), true);
        t.eq(r.rightOfDateLine, true, "still on the right side of the dateline");
        t.eq(r.xOffset, r.map.getMaxExtent().getWidth() / resolution, "xOffset adjusted for new resolution");
        r.setExtent(new OpenLayers.Bounds(-184, -2, 178, 2), false);
        t.eq(r.rightOfDateLine, false, "on the left side of the dateline");
        t.eq(r.xOffset, 0, "no xOffset");
        r.setExtent(new OpenLayers.Bounds(178, -2, 184, 2), true);
        t.eq(r.rightOfDateLine, true, "back on the right side of the dateline");
        t.eq(r.xOffset, r.map.getMaxExtent().getWidth() / resolution, "correct xOffset");
        
        tearDown();
    }

  </script>
</head>
<body>
<div id="map" style="width:500px;height:550px"></div>
</body>
</html>