OpenLayers OpenLayers

Changeset 7652

Show
Ignore:
Timestamp:
07/31/08 20:17:59 (1 month ago)
Author:
euzuro
Message:

ordering <exclamation point/>. you can now gracefully z-order your vectors... and you can even 'yOrder' them and add background images, making for a nice 3dish look. be sure to check out the two new example html's: marker-shadow.html and ordering.html. Big thanks to tcoulter (funkyc) for a prolonged effort with this patch. It has come a long way and now what a beautiful finish. (Closes #1357)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/openlayers/lib/OpenLayers/Layer/Vector.js

    r7650 r7652  
    153153     */ 
    154154    renderer: null, 
     155     
     156    /** 
     157     * APIProperty: yOrdering 
     158     * {String} Whether or not externalGraphic y-ordering is enabled on this  
     159     *     layer. Default is false. 
     160     */ 
     161    yOrdering: false, 
    155162    
    156163    /**  
     
    253260            var rendererClass = OpenLayers.Renderer[this.renderers[i]]; 
    254261            if (rendererClass && rendererClass.prototype.supported()) { 
    255                this.renderer = new rendererClass(this.div); 
    256                break; 
     262                this.renderer = new rendererClass(this.div, this.yOrdering); 
     263                break; 
    257264            }   
    258265        }   
     
    333340             
    334341            this.renderer.root.style.visibility = "visible"; 
    335              
     342 
    336343            // Force a reflow on gecko based browsers to prevent jump/flicker. 
    337344            // This seems to happen on only certain configurations; it was originally 
  • trunk/openlayers/lib/OpenLayers/Renderer/Elements.js

    r7645 r7652  
    66 * @requires OpenLayers/Renderer.js 
    77 */ 
     8 
     9/** 
     10 * Class: OpenLayers.ElementsIndexer 
     11 * This class takes care of figuring out which order elements should be 
     12 *     placed in the DOM based on given indexing methods.  
     13 */ 
     14OpenLayers.ElementsIndexer = OpenLayers.Class({ 
     15    
     16    /** 
     17     * Property: maxZIndex 
     18     * {Integer} This is the largest-most z-index value for a node 
     19     *     contained within the indexer. 
     20     */ 
     21    maxZIndex: null, 
     22     
     23    /** 
     24     * Property: order 
     25     * {Array<String>} This is an array of node id's stored in the 
     26     *     order that they should show up on screen. Id's higher up in the 
     27     *     array (higher array index) represent nodes with higher z-indeces. 
     28     */ 
     29    order: null,  
     30     
     31    /** 
     32     * Property: indices 
     33     * {Object} This is a hash that maps node ids to their z-index value 
     34     *     stored in the indexer. This is done to make finding a nodes z-index  
     35     *     value O(1). 
     36     */ 
     37    indices: null, 
     38     
     39    /** 
     40     * Property: compare 
     41     * {Function} This is the function used to determine placement of 
     42     *     of a new node within the indexer. If null, this defaults to to 
     43     *     the Z_ORDER_DRAWING_ORDER comparison method. 
     44     */ 
     45    compare: null, 
     46    
     47    /** 
     48     * APIMethod: initialize 
     49     * Create a new indexer with  
     50     *  
     51     * Parameters: 
     52     * yOrdering - {Boolean} Whether to use y-ordering. 
     53     */ 
     54    initialize: function(yOrdering) { 
     55 
     56        this.compare = yOrdering ?  
     57            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER : 
     58            OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; 
     59             
     60        this.order = []; 
     61        this.indices = {}; 
     62        this.maxZIndex = 0; 
     63    }, 
     64     
     65    /** 
     66     * APIMethod: insert 
     67     * Insert a new node into the indexer. In order to find the correct  
     68     *     positioning for the node to be inserted, this method uses a binary  
     69     *     search. This makes inserting O(log(n)).  
     70     *  
     71     * Parameters: 
     72     * newNode - {DOMElement} The new node to be inserted. 
     73     * root - {DOMElement} The root node from which to insert the new node. 
     74     */ 
     75    insert: function(newNode, root) { 
     76        var nodeId = newNode.id; 
     77         
     78        this.determineZIndex(newNode);        
     79 
     80        var leftIndex = -1; 
     81        var rightIndex = this.order.length; 
     82        var middle; 
     83 
     84        while (rightIndex - leftIndex > 1) { 
     85            middle = parseInt((leftIndex + rightIndex) / 2); 
     86             
     87            var nextId = this.order[middle]; 
     88            var nextNode = OpenLayers.Util.getElement(nextId); 
     89             
     90            var placement = this.compare(this, newNode, nextNode); 
     91             
     92            if (placement > 0) { 
     93                leftIndex = middle; 
     94            } else { 
     95                rightIndex = middle; 
     96            }  
     97        } 
     98         
     99        this.order.splice(rightIndex, 0, nodeId); 
     100        this.indices[nodeId] = this.getZIndex(newNode); 
     101         
     102        // If the new node should be before another in the index 
     103        // order, insert the new node before the next; else, lets just 
     104        // append the new one on the end, making it the highest in the index order. 
     105        var nextIndex = rightIndex + 1; 
     106        if (nextIndex < this.order.length) { 
     107            var nextNode = OpenLayers.Util.getElement(this.order[nextIndex]); 
     108            root.insertBefore(newNode, nextNode); 
     109        } else { 
     110            root.appendChild(newNode); 
     111        } 
     112    }, 
     113     
     114    /** 
     115     * APIMethod: remove 
     116     *  
     117     * Parameters: 
     118     * node - {DOMElement} The node to be removed. 
     119     */ 
     120    remove: function(node) { 
     121        var nodeId = node.id; 
     122        var arrayIndex = OpenLayers.Util.indexOf(this.order, nodeId); 
     123        if (arrayIndex >= 0) { 
     124            // Remove it from the order array, as well as deleting the node 
     125            // from the indeces hash. 
     126            this.order.splice(arrayIndex, 1); 
     127            delete this.indices[nodeId]; 
     128             
     129            // Reset the maxium z-index based on the last item in the  
     130            // order array. 
     131            var lastId = this.order[this.order.length - 1]; 
     132            this.maxZIndex = this.indices[lastId]; 
     133        } 
     134    }, 
     135     
     136    /** 
     137     * APIMethod: exists 
     138     *  
     139     * Parameters: 
     140     * node- {DOMElement} The node to test for existence. 
     141     *  
     142     * Returns: 
     143     * {Boolean} Whether or not the node exists in the indexer? 
     144     */ 
     145    exists: function(node) { 
     146        return (this.indices[node.id] != null); 
     147    }, 
     148     
     149    /** 
     150     * APIMethod: getZIndex 
     151     * Get the z-index value for the current node from the node data itself. 
     152     *  
     153     * Parameters: 
     154     * node - {DOMElement} The node whose z-index to get. 
     155     *  
     156     * Returns: 
     157     * {Integer} The z-index value for the specified node (from the node  
     158     *     data itself). 
     159     */ 
     160    getZIndex: function(node) { 
     161        return node._style.graphicZIndex;   
     162    }, 
     163     
     164    /** 
     165     * Method: determineZIndex 
     166     * Determine the z-index for the current node if there isn't one,  
     167     *     and set the maximum value if we've found a new maximum. 
     168     *  
     169     * Parameters: 
     170     * node - {DOMElement}  
     171     */ 
     172    determineZIndex: function(node) { 
     173        var zIndex = node._style.graphicZIndex; 
     174         
     175        // Everything must have a zIndex. If none is specified, 
     176        // this means the user *must* (hint: assumption) want this 
     177        // node to succomb to drawing order. To enforce drawing order 
     178        // over all indexing methods, we'll create a new z-index that's 
     179        // greater than any currently in the indexer. 
     180        if (zIndex == null) { 
     181            zIndex = this.maxZIndex; 
     182            node._style.graphicZIndex = zIndex;  
     183        } else if (zIndex > this.maxZIndex) { 
     184            this.maxZIndex = zIndex; 
     185        } 
     186    }, 
     187     
     188    CLASS_NAME: "OpenLayers.ElementsIndexer" 
     189}); 
     190 
     191/** 
     192 * Namespace: OpenLayers.ElementsIndexer.IndexingMethods 
     193 * These are the compare methods for figuring out where a new node should be  
     194 *     placed within the indexer. These methods are very similar to general  
     195 *     sorting methods in that they return -1, 0, and 1 to specify the  
     196 *     direction in which new nodes fall in the ordering. 
     197 */ 
     198OpenLayers.ElementsIndexer.IndexingMethods = { 
     199     
     200    /** 
     201     * Method: Z_ORDER 
     202     * This compare method is used by other comparison methods. 
     203     *     It can be used individually for ordering, but is not recommended, 
     204     *     because it doesn't subscribe to drawing order. 
     205     *  
     206     * Parameters: 
     207     * indexer - {<OpenLayers.ElementsIndexer>} 
     208     * newNode - {DOMElement} 
     209     * nextNode - {DOMElement} 
     210     *  
     211     * Returns: 
     212     * {Integer} 
     213     */ 
     214    Z_ORDER: function(indexer, newNode, nextNode) { 
     215        var newZIndex = indexer.getZIndex(newNode); 
     216 
     217        var returnVal = 0; 
     218        if (nextNode) { 
     219            var nextZIndex = indexer.getZIndex(nextNode); 
     220            returnVal = newZIndex - nextZIndex;  
     221        } 
     222         
     223        return returnVal; 
     224    }, 
     225 
     226    /** 
     227     * APIMethod: Z_ORDER_DRAWING_ORDER 
     228     * This method orders nodes by their z-index, but does so in a way 
     229     *     that, if there are other nodes with the same z-index, the newest  
     230     *     drawn will be the front most within that z-index. This is the  
     231     *     default indexing method. 
     232     *  
     233     * Parameters: 
     234     * indexer - {<OpenLayers.ElementsIndexer>} 
     235     * newNode - {DOMElement} 
     236     * nextNode - {DOMElement} 
     237     *  
     238     * Returns: 
     239     * {Integer} 
     240     */ 
     241    Z_ORDER_DRAWING_ORDER: function(indexer, newNode, nextNode) { 
     242        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( 
     243            indexer,  
     244            newNode,  
     245            nextNode 
     246        ); 
     247         
     248        // Make Z_ORDER subscribe to drawing order by pushing it above 
     249        // all of the other nodes with the same z-index. 
     250        if (nextNode && returnVal == 0) { 
     251            returnVal = 1; 
     252        } 
     253         
     254        return returnVal; 
     255    }, 
     256 
     257    /** 
     258     * APIMethod: Z_ORDER_Y_ORDER 
     259     * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it 
     260     *     best describes which ordering methods have precedence (though, the  
     261     *     name would be too long). This method orders nodes by their z-index,  
     262     *     but does so in a way that, if there are other nodes with the same  
     263     *     z-index, the nodes with the lower y position will be "closer" than  
     264     *     those with a higher y position. If two nodes have the exact same y  
     265     *     position, however, then this method will revert to using drawing   
     266     *     order to decide placement. 
     267     *  
     268     * Parameters: 
     269     * indexer - {<OpenLayers.ElementsIndexer>} 
     270     * newNode - {DOMElement} 
     271     * nextNode - {DOMElement} 
     272     *  
     273     * Returns: 
     274     * {Integer} 
     275     */ 
     276    Z_ORDER_Y_ORDER: function(indexer, newNode, nextNode) { 
     277        var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER( 
     278            indexer,  
     279            newNode,  
     280            nextNode 
     281        ); 
     282         
     283        if (nextNode && returnVal == 0) { 
     284            var newLat = newNode._geometry.getBounds().bottom; 
     285            var nextLat = nextNode._geometry.getBounds().bottom; 
     286             
     287            var result = nextLat - newLat; 
     288            returnVal = (result ==0) ? 1 : result; 
     289        } 
     290         
     291        return returnVal;        
     292    } 
     293}; 
    8294 
    9295/** 
     
    43329     
    44330    /** 
     331     * Property: Indexer 
     332     * {<OpenLayers.ElementIndexer>} An instance of OpenLayers.ElementsIndexer  
     333     *     created upon initialization. 
     334     */ 
     335    indexer: null,  
     336     
     337    /** 
     338     * Constant: BACKGROUND_ID_SUFFIX 
     339     * {String} 
     340     */ 
     341    BACKGROUND_ID_SUFFIX: "_background", 
     342     
     343    /** 
    45344     * Property: minimumSymbolizer 
    46345     * {Object} 
     
    58357     * Parameters: 
    59358     * containerID - {String} 
    60      */ 
    61     initialize: function(containerID) { 
     359     * yOrdering - {Boolean} Whether or not y-ordering is enabled. 
     360     */ 
     361    initialize: function(containerID, yOrdering) { 
    62362        OpenLayers.Renderer.prototype.initialize.apply(this, arguments); 
    63363 
     
    67367        this.rendererRoot.appendChild(this.root); 
    68368        this.container.appendChild(this.rendererRoot); 
     369         
     370        this.indexer = new OpenLayers.ElementsIndexer(yOrdering); 
    69371    }, 
    70372     
     
    136438        }; 
    137439 
     440        // 
     441        if (style.backgroundGraphic) { 
     442            this.redrawBackgroundNode(geometry.id, geometry, style, featureId); 
     443        } 
     444         
    138445        if (style.display != "none") { 
    139             //first we create the basic node and add it to the root 
    140             var nodeType = this.getNodeType(geometry, style); 
    141             var node = this.nodeFactory(geometry.id, nodeType); 
    142             node._featureId = featureId; 
    143             node._geometryClass = geometry.CLASS_NAME; 
    144             node._style = style; 
    145              
    146             //now actually draw the node, and style it 
    147             node = this.drawGeometryNode(node, geometry); 
    148              
    149             // append the node to root (but only if it's new) 
    150             if (node.parentNode != this.root) {  
    151                 this.root.appendChild(node);  
    152             } 
    153             this.postDraw(node); 
     446            this.redrawNode(geometry.id, geometry, style, featureId);  
    154447        } else { 
    155448            node = OpenLayers.Util.getElement(geometry.id); 
     
    158451            } 
    159452        } 
     453    }, 
     454     
     455    /** 
     456     * Method: redrawNode 
     457     *  
     458     * Parameters: 
     459     * id - {String} 
     460     * geometry - {<OpenLayers.Geometry>} 
     461     * style - {Object} 
     462     * featureId - {String} 
     463     */ 
     464    redrawNode: function(id, geometry, style, featureId) { 
     465        // Get the node if it's already on the map. 
     466        var currentNode = OpenLayers.Util.getElement(id); 
     467         
     468        // Create a new node, or use the current one if it's 
     469        // already there. 
     470        var newNode; 
     471        if (!currentNode) { 
     472            var nodeType = this.getNodeType(geometry, style); 
     473            newNode = this.createNode(nodeType, id); 
     474        } else { 
     475            newNode = currentNode; 
     476        } 
     477         
     478        // Set the data for the node, then draw it. 
     479        newNode._featureId = featureId; 
     480        newNode._geometry = geometry; 
     481        newNode._geometryClass = geometry.CLASS_NAME; 
     482        newNode._style = style; 
     483        newNode = this.drawGeometryNode(newNode, geometry, style); 
     484         
     485        // If the node is known to the indexer, remove it so we can 
     486        // recalculate where it should go. 
     487        if (this.indexer.exists(newNode)) { 
     488            this.indexer.remove(newNode); 
     489        } 
     490         
     491        // Insert the node into the indexer so it can show us where to place it. 
     492        //  Note that this operation is O(log(n)). If there's a performance  
     493        //  problem (when dragging, for instance) this is likely where it  
     494        //  would be. 
     495        this.indexer.insert(newNode, this.root); 
     496         
     497        this.postDraw(newNode);  
     498    }, 
     499     
     500    /** 
     501     * Method: redrawBackgroundNode 
     502     * Redraws the node using special 'background' style properties. Basically 
     503     *     just calls redrawNode(), but instead of directly using the  
     504     *     'externalGraphic', 'graphicXOffset', 'graphicYOffset', and  
     505     *     'graphicZIndex' properties directly from the specified 'style'  
     506     *     parameter, we create a new style object and set those properties  
     507     *     from the corresponding 'background'-prefixed properties from  
     508     *     specified 'style' parameter. 
     509     *  
     510     * Parameters: 
     511     * id - {String} 
     512     * geometry - {<OpenLayers.Geometry>} 
     513     * style - {Object} 
     514     * featureId - {String} 
     515     */ 
     516    redrawBackgroundNode: function(id, geometry, style, featureId) { 
     517        var backgroundStyle = OpenLayers.Util.extend({}, style); 
     518         
     519        // Set regular style attributes to apply to the background styles. 
     520        backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic; 
     521        backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset; 
     522        backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset; 
     523        backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex; 
     524         
     525        // Erase background styles. 
     526        backgroundStyle.backgroundGraphic = null; 
     527        backgroundStyle.backgroundXOffset = null; 
     528        backgroundStyle.backgroundYOffset = null; 
     529        backgroundStyle.backgroundGraphicZIndex = null; 
     530         
     531        this.redrawNode( 
     532            id + this.BACKGROUND_ID_SUFFIX,  
     533            geometry,  
     534            backgroundStyle,  
     535            null 
     536        ); 
    160537    }, 
    161538 
     
    214591     * Method: postDraw 
    215592     * Things that have do be done after the geometry node is appended 
    216      * to its parent node. To be overridden by subclasses. 
     593     *     to its parent node. To be overridden by subclasses. 
    217594     *  
    218595     * Parameters: 
     
    224601     * Method: drawPoint 
    225602     * Virtual function for drawing Point Geometry.  
    226      * Should be implemented by subclasses. 
    227      * This method is only called by the renderer itself. 
     603     *     Should be implemented by subclasses. 
     604     *     This method is only called by the renderer itself. 
    228605     *  
    229606     * Parameters:  
     
    236613     * Method: drawLineString 
    237614     * Virtual function for drawing LineString Geometry.  
    238      * Should be implemented by subclasses. 
    239      * This method is only called by the renderer itself. 
     615     *     Should be implemented by subclasses. 
     616     *     This method is only called by the renderer itself. 
    240617     *  
    241618     * Parameters:  
     
    248625     * Method: drawLinearRing 
    249626     * Virtual function for drawing LinearRing Geometry.  
    250      * Should be implemented by subclasses. 
    251      * This method is only called by the renderer itself. 
     627     *     Should be implemented by subclasses. 
     628     *     This method is only called by the renderer itself. 
    252629     *  
    253630     * Parameters:  
     
    260637     * Method: drawPolygon 
    261638     * Virtual function for drawing Polygon Geometry.  
    262      * Should be implemented by subclasses. 
    263      * This method is only called by the renderer itself. 
     639     *    Should be implemented by subclasses. 
     640     *    This method is only called by the renderer itself. 
    264641     *  
    265642     * Parameters:  
     
    272649     * Method: drawRectangle 
    273650     * Virtual function for drawing Rectangle Geometry.  
    274      * Should be implemented by subclasses. 
    275      * This method is only called by the renderer itself. 
     651     *     Should be implemented by subclasses. 
     652     *     This method is only called by the renderer itself. 
    276653     *  
    277654     * Parameters:  
     
    284661     * Method: drawCircle 
    285662     * Virtual function for drawing Circle Geometry.  
    286      * Should be implemented by subclasses. 
    287      * This method is only called by the renderer itself. 
     663     *     Should be implemented by subclasses. 
     664     *     This method is only called by the renderer itself. 
    288665     *  
    289666     * Parameters:  
     
    296673     * Method: drawSurface 
    297674     * Virtual function for drawing Surface Geometry.  
    298      * Should be implemented by subclasses. 
    299      * This method is only called by the renderer itself. 
     675     *     Should be implemented by subclasses. 
     676     *     This method is only called by the renderer itself. 
    300677     *  
    301678     * Parameters:  
     
    313690     * Returns: 
    314691     * {<OpenLayers.Geometry>} A geometry from an event that  
    315      *                         happened on a layer 
     692     *     happened on a layer. 
    316693     */ 
    317694    getFeatureIdFromEvent: function(evt) { 
     
    341718            var element = OpenLayers.Util.getElement(geometry.id); 
    342719            if (element && element.parentNode) { 
     720                if (element.geometry) { 
     721                    element.geometry.destroy(); 
     722                    element.geometry = null; 
     723                } 
    343724                element.parentNode.removeChild(element); 
     725 
     726                this.indexer.remove(element); 
     727                 
     728                var backgroundId = geometry.id + this.BACKGROUND_ID_SUFFIX; 
     729                var bElem = OpenLayers.Util.getElement(backgroundId); 
     730                if (bElem && bElem.parentNode) { 
     731                    // No need to destroy the geometry since the element and the background 
     732                    // node share the same geometry. 
     733                    bElem.parentNode.removeChild(bElem); 
     734                } 
    344735            } 
    345736        } 
    346     },     
     737    }, 
    347738 
    348739    /**  
     
    355746     * Parameters: 
    356747     * id - {String} 
    357      * type - {String} type Kind of node to draw 
     748     * type - {String} type Kind of node to draw. 
    358749     *  
    359750     * Returns: 
    360      * {DOMElement} A new node of the given type and id 
     751     * {DOMElement} A new node of the given type and id. 
    361752     */ 
    362753    nodeFactory: function(id, type) { 
     
    373764    }, 
    374765     
     766    /**  
     767     * Method: createNode 
     768     *  
     769     * Parameters: 
     770     * type - {String} Kind of node to draw. 
     771     * id - {String} Id for node. 
     772     *  
     773     * Returns: 
     774     * {DOMElement} A new node of the given type and id. 
     775     *     This function must be overridden by subclasses. 
     776     */ 
     777    createNode: function(type, id) {}, 
     778 
    375779    /** 
    376780     * Method: isComplexSymbol 
     
    386790        return (graphicName != "circle") && !!graphicName; 
    387791    }, 
    388      
     792 
    389793    CLASS_NAME: "OpenLayers.Renderer.Elements" 
    390794}); 
  • trunk/openlayers/tests/Renderer/Elements.html

    <
    r7542 r7652  
    44  <script type="text/javascript"> 
    55 
    6     function test_Elements_constructor(t) { 
    7         t.plan(5); 
    8         var el = document.createElement('div'); 
    9         document.body.appendChild(el); 
    10         el.id = "foo"; 
    11          
     6    function setUp() { 
     7        // Stub out functions that are meant to be overridden by 
     8        // subclasses. 
    129        OpenLayers.Renderer.Elements.prototype._createRenderRoot = 
    1310            OpenLayers.Renderer.Elements.prototype.createRenderRoot; 
     
    2623        }; 
    2724         
    28         var r = new OpenLayers.Renderer.Elements("foo"); 
    29          
    30         t.ok(r instanceof OpenLayers.Renderer.Elements, "new OpenLayers.Renderer.Elements returns Elements object" ); 
    31         t.ok(r.rendererRoot != null, "elements rendererRoot is not null"); 
    32         t.ok(r.root != null, "elements root is not null"); 
    33          
    34         t.ok(r.root.parentNode == rendererRoot, "elements root is correctly appended to rendererRoot"); 
    35         t.ok(r.rendererRoot.parentNode == el, "elements rendererRoot is correctly appended to container"); 
    36          
     25        OpenLayers.Renderer.Elements.prototype._createNode = 
     26            OpenLayers.Renderer.Elements.prototype.createNode; 
     27         
     28        OpenLayers.Renderer.Elements.prototype.createNode = function() { 
     29            return document.createElement("div"); 
     30        }; 
     31    } 
     32     
     33    // Create a new Elements renderer based on an id and an ordering 
     34    // type. For these tests, both of these parameters are optional. 
     35    function create_renderer(id, ordering) { 
     36         
     37        rendererRoot = null; 
     38         
     39        if (id == null) { 
     40            var el = document.createElement('div'); 
     41            document.body.appendChild(el); 
     42            el.id = OpenLayers.Util.createUniqueID(); 
     43            id = el.id; 
     44        } 
     45         
     46        return new OpenLayers.Renderer.Elements(id, ordering); 
     47    } 
     48     
     49    // Cleanup stubs made in the function above. 
     50    function tearDown() { 
    3751        OpenLayers.Renderer.Elements.prototype.createRenderRoot = 
    3852            OpenLayers.Renderer.Elements.prototype._createRenderRoot; 
    3953        OpenLayers.Renderer.Elements.prototype.createRoot = 
    4054            OpenLayers.Renderer.Elements.prototype._createRoot; 
     55        OpenLayers.Renderer.Elements.prototype.createNode = 
     56            OpenLayers.Renderer.Elements.prototype._createNode; 
     57    } 
     58 
     59    function test_Elements_constructor(t) { 
     60        t.plan(6); 
     61         
     62        setUp(); 
     63         
     64        var r = create_renderer(); 
     65         
     66        t.ok(r instanceof OpenLayers.Renderer.Elements, "new OpenLayers.Renderer.Elements returns Elements object" ); 
     67        t.ok(r.rendererRoot != null, "elements rendererRoot is not null"); 
     68        t.ok(r.root != null, "elements root is not null"); 
     69        t.ok(r.indexer != null, "indexer is not null."); 
     70         
     71        t.ok(r.root.parentNode == r.rendererRoot, "elements root is correctly appended to rendererRoot"); 
     72        t.ok(r.rendererRoot.parentNode == r.container, "elements rendererRoot is correctly appended to container"); 
     73         
     74        tearDown(); 
    4175    } 
    4276     
     
    4478        t.plan(5); 
    4579         
    46         OpenLayers.Renderer.Elements.prototype._initialize = 
    47             OpenLayers.Renderer.Elements.prototype.initialize; 
    48          
    49         OpenLayers.Renderer.Elements.prototype.initialize = function() {} 
    50          
    51         var g_Clear = false, g_Destroy = false; 
    52          
     80        var elems = { 
     81            'clear': function() { 
     82                t.ok(true, "clear called"); 
     83            }, 
     84            'rendererRoot': {}, 
     85            'root': {}, 
     86            'xmlns': {} 
     87        }; 
     88 
    5389        OpenLayers.Renderer.prototype._destroy =  
    5490            OpenLayers.Renderer.prototype.destroy; 
    55              
     91 
     92        var args = [{}, {}, {}]; 
    5693        OpenLayers.Renderer.prototype.destroy = function() { 
    57             g_Destroy = true; 
    58         }; 
    59          
    60         var r = new OpenLayers.Renderer.SVG(document.body); 
    61         r.clear = function() { 
    62             g_Clear = true; 
    63         }; 
    64         r.rendererRoot = 'foo'; 
    65         r.root = 'bar'; 
    66         r.xmlns = 'dude'; 
    67          
    68         r.destroy(); 
    69          
    70         t.eq(g_Clear, true, "OpenLayers.Renderer.Elements.clear() called"); 
    71         t.eq(r.rendererRoot, null, "rendererRoot nullified"); 
    72         t.eq(r.root, null, "root nullified"); 
    73         t.eq(r.xmlns, null, "xmlns nullified"); 
    74         t.eq(g_Destroy, true, "OpenLayers.Renderer.destroy() called"); 
    75          
    76         OpenLayers.Renderer.Elements.prototype.initialize = 
    77             OpenLayers.Renderer.Elements.prototype._initialize; 
     94            t.ok((arguments[0] == args[0]) && 
     95                 (arguments[1] == args[1]) && 
     96                 (arguments[2] == args[2]), "correct arguments passed to OpenLayers.Renderer.destroy()"); 
     97        }; 
     98 
     99        OpenLayers.Renderer.Elements.prototype.destroy.apply(elems, args); 
     100 
     101        t.ok(elems.rendererRoot == null, "rendererRoot nullified"); 
     102        t.ok(elems.root == null, "root nullified"); 
     103        t.ok(elems.xmlns == null, "xmlns nullified"); 
     104 
    78105        OpenLayers.Renderer.prototype.destroy =  
    79106            OpenLayers.Renderer.prototype._destroy; 
     107 
    80108    } 
    81109     
     
    83111        t.plan(1); 
    84112         
    85         OpenLayers.Renderer.Elements.prototype._initialize = 
    86             OpenLayers.Renderer.Elements.prototype.initialize; 
    87          
    88         OpenLayers.Renderer.Elements.prototype.initialize = function() {} 
    89          
    90         var r = new OpenLayers.Renderer.Elements(); 
     113        setUp(); 
     114         
     115        var r = create_renderer(); 
    91116        var element = document.createElement("div"); 
    92117        r.root = element; 
     
    99124        t.ok(r.root.childNodes.length == 0, "root is correctly cleared"); 
    100125         
    101         OpenLayers.Renderer.Elements.prototype.initialize = 
    102             OpenLayers.Renderer.Elements.prototype._initialize; 
     126        tearDown(); 
    103127    } 
    104128     
    105129    function test_Elements_drawGeometry(t) { 
    106130        t.plan(5); 
    107          
    108         OpenLayers.Renderer.Elements.prototype._initialize = 
    109             OpenLayers.Renderer.Elements.prototype.initialize; 
    110          
    111         OpenLayers.Renderer.Elements.prototype.initialize = function() {}; 
    112          
    113         var r = new OpenLayers.Renderer.Elements(); 
     131 
     132        setUp(); 
     133 
     134        var r = create_renderer(); 
    114135         
    115136        var element = document.createElement("div"); 
     
    148169        r.drawGeometry(geometry, style, featureId); 
    149170        t.ok(g_Node.parentNode != r.root, "node is correctly removed"); 
    150         
    151         OpenLayers.Util.getElement = _getElement; 
    152         OpenLayers.Renderer.Elements.prototype.initialize = 
    153             OpenLayers.Renderer.Elements.prototype._initialize; 
     171             
     172        tearDown(); 
    154173    } 
    155174 
     
    157176        t.plan(9); 
    158177         
    159         OpenLayers.Renderer.Elements.prototype._initialize = 
    160             OpenLayers.Renderer.Elements.prototype.initialize; 
    161              
    162         OpenLayers.Renderer.Elements.prototype.initialize = function() {}; 
    163  
    164         var r = new OpenLayers.Renderer.Elements(); 
     178        setUp(); 
     179         
     180        var r = create_renderer(); 
    165181         
    166182        var element = document.createElement("div"); 
     
    282298        r.drawGeometry(geometry, style); 
    283299        t.ok(properDraw, "drawGeometry called drawPolygon when passed a multi-polygon"); 
    284          
    285         OpenLayers.Renderer.Elements.prototype.initialize = 
    286             OpenLayers.Renderer.Elements.prototype._initialize; 
     300       
     301        tearDown(); 
    287302    } 
    288303     
     
    309324     
    310325    function test_Elements_erasegeometry(t) { 
    311         t.plan(5); 
    312          
    313         var el = document.createElement('div'); 
    314         document.body.appendChild(el); 
    315         el.id = 'bar'; 
     326        t.plan(15); 
     327 
     328        var elements = { 
     329            'eraseGeometry': function(geometry) { 
     330                gErased.push(geometry); 
     331            } 
     332        }; 
     333         
    316334        var geometry = { 
    317             id: 'bar' 
    318         }; 
    319          
    320         OpenLayers.Renderer.Elements.prototype.eraseGeometry(geometry); 
    321         t.ok(el.parentNode != document.body, "element correctly removed"); 
    322  
    323         var el = document.createElement('div'); 
    324         document.body.appendChild(el); 
    325         el.id = 'bar'; 
    326         var geometry = { 
    327             CLASS_NAME: "OpenLayers.Geometry.MultiPoint", 
    328             components: [{ 
    329                 id: 'bar' 
    330             }] 
    331         }; 
    332  
    333         OpenLayers.Renderer.Elements.prototype.eraseGeometry(geometry); 
    334         t.ok(el.parentNode != document.body, "geometry components correctly removed when passed a multipoint"); 
    335          
    336         var el = document.createElement('div'); 
    337         document.body.appendChild(el); 
    338         el.id = 'bar'; 
    339         var geometry = { 
    340             CLASS_NAME: "OpenLayers.Geometry.MultiLineString", 
    341             components: [{ 
    342                 id: 'bar' 
    343             }] 
    344         }; 
    345          
    346         OpenLayers.Renderer.Elements.prototype.eraseGeometry(geometry); 
    347         t.ok(el.parentNode != document.body, "geometry components correctly removed when passed a multilinestring"); 
    348          
    349         var el = document.createElement('div'); 
    350         document.body.appendChild(el); 
    351         el.id = 'bar'; 
    352         var geometry = { 
    353             CLASS_NAME: "OpenLayers.Geometry.MultiPolygon", 
    354             components: [{ 
    355                 id: 'bar' 
    356             }] 
    357         }; 
    358          
    359         OpenLayers.Renderer.Elements.prototype.eraseGeometry(geometry); 
    360         t.ok(el.parentNode != document.body, "geometry components correctly removed when passed a multipolygon"); 
    361          
    362         var el = document.createElement('div'); 
    363         document.body.appendChild(el); 
    364         el.id = 'bar'; 
    365         var geometry = { 
    366             CLASS_NAME: "OpenLayers.Geometry.Collection", 
    367             components: [{ 
    368                 id: 'bar' 
    369             }] 
    370         }; 
    371          
    372         OpenLayers.Renderer.Elements.prototype.eraseGeometry(geometry); 
    373         t.ok(el.parentNode != document.body, "geometry components correctly removed when passed a collection"); 
    374     }     
     335            'components': [{}, {}, {}] 
     336        }; 
     337         
     338      //multipoint 
     339        geometry.CLASS_NAME = "OpenLayers.Geometry.MultiPoint"; 
     340        gErased = []; 
     341        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]); 
     342        t.ok( (gErased[0] == geometry.components[0]) && 
     343              (gErased[1] == geometry.components[1]) && 
     344              (gErased[2] == geometry.components[2]), "multipoint all components of geometry correctly erased."); 
     345 
     346      //multilinestring 
     347        geometry.CLASS_NAME = "OpenLayers.Geometry.MultiLineString"; 
     348        gErased = []; 
     349        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]); 
     350        t.ok( (gErased[0] == geometry.components[0]) && 
     351              (gErased[1] == geometry.components[1]) && 
     352              (gErased[2] == geometry.components[2]), "multilinestring all components of geometry correctly erased."); 
     353       
     354      //multipolygon 
     355        geometry.CLASS_NAME = "OpenLayers.Geometry.MultiPolygon"; 
     356        gErased = []; 
     357        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]); 
     358        t.ok( (gErased[0] == geometry.components[0]) && 
     359              (gErased[1] == geometry.components[1]) && 
     360              (gErased[2] == geometry.components[2]), "multipolygon all components of geometry correctly erased."); 
     361       
     362      //collection 
     363        geometry.CLASS_NAME = "OpenLayers.Geometry.Collection"; 
     364        gErased = []; 
     365        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]); 
     366        t.ok( (gErased[0] == geometry.components[0]) && 
     367              (gErased[1] == geometry.components[1]) && 
     368              (gErased[2] == geometry.components[2]), "collection all components of geometry correctly erased."); 
     369         
     370 
     371    // OTHERS 
     372    // 
     373        geometry.CLASS_NAME = {}; 
     374 
     375        gElement = null; 
     376        gBackElement = null; 
     377 
     378        OpenLayers.Util._getElement = OpenLayers.Util.getElement; 
     379        OpenLayers.Util.getElement = function(id) { 
     380            var retVal = null; 
     381            if (id != null) { 
     382                var hasBack = (id.indexOf(elements.BACKGROUND_ID_SUFFIX) != -1); 
     383                retVal = hasBack ? gBackElement : gElement; 
     384            } 
     385            return retVal; 
     386        }; 
     387 
     388      //element null 
     389        geometry.id = null; 
     390        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]); 
     391        // (no tests here, just make sure it doesn't bomb)       
     392       
     393      //element.parentNode null 
     394        elements.BACKGROUND_ID_SUFFIX = 'BLAHBLAHBLAH'; 
     395        geometry.id = "foo";         
     396        gElement = {}; 
     397        OpenLayers.Renderer.Elements.prototype.eraseGeometry.apply(elements, [geometry]); 
     398        // (no tests here, just make sure it doesn't bomb)       
     399       
     400      //valid element.parentNode, element.geometry 
     401        elements.indexer = { 
     402            'remove': function(elem) { 
     403                gIndexerRemoved = elem; 
     404            } 
     405        }; 
     406 
     407        gElement = { 
     408            'geometry': { 
     409                'destroy': function() {  
     410                    t.ok(true, "geometry destroyed"); 
     411                } 
     412            }, 
     413            'parentNode': { 
     414                'removeChild': function(elem) { 
     415                    gElemRemoved = elem;