| | 8 | /** |
|---|
| | 9 | * Class: OpenLayers.ElementsIndexer |
|---|
| | 10 | * This class takes care of figuring out which order elements should be |
|---|
| | 11 | * placed in the DOM based on given indexing methods. |
|---|
| | 12 | */ |
|---|
| | 13 | OpenLayers.ElementsIndexer = OpenLayers.Class({ |
|---|
| | 14 | |
|---|
| | 15 | /** |
|---|
| | 16 | * Property: maxZIndex |
|---|
| | 17 | * {Integer} This is the largest-most z-index value for a node |
|---|
| | 18 | * contained within the indexer. |
|---|
| | 19 | */ |
|---|
| | 20 | maxZIndex: null, |
|---|
| | 21 | |
|---|
| | 22 | /** |
|---|
| | 23 | * Property: order |
|---|
| | 24 | * {Array<String>} This is an array of node id's stored in the |
|---|
| | 25 | * order that they should show up on screen. Id's higher up in the |
|---|
| | 26 | * array (higher array index) represent nodes with higher z-indeces. |
|---|
| | 27 | */ |
|---|
| | 28 | order: null, |
|---|
| | 29 | |
|---|
| | 30 | /** |
|---|
| | 31 | * Property: indices |
|---|
| | 32 | * {Hash} This is a hash that maps node ids to their z-index value |
|---|
| | 33 | * stored in the indexer. This is done to make finding a nodes z-index |
|---|
| | 34 | * value O(1). |
|---|
| | 35 | */ |
|---|
| | 36 | indices: null, |
|---|
| | 37 | |
|---|
| | 38 | /** |
|---|
| | 39 | * Property: compare |
|---|
| | 40 | * {Function} This is the function used to determine placement of |
|---|
| | 41 | * of a new node within the indexer. If null, this defaults to to |
|---|
| | 42 | * the Z_ORDER_DRAWING_ORDER comparison method. |
|---|
| | 43 | */ |
|---|
| | 44 | compare: null, |
|---|
| | 45 | |
|---|
| | 46 | /** |
|---|
| | 47 | * APIMethod: initialize |
|---|
| | 48 | * Create a new indexer with |
|---|
| | 49 | * |
|---|
| | 50 | * Parameters: |
|---|
| | 51 | * ordering - {Function} The canonical name of the indexing method to |
|---|
| | 52 | * use. See OpenLayers.ElementsIndexer.Translations below. If null, |
|---|
| | 53 | * this defaults to "z". |
|---|
| | 54 | */ |
|---|
| | 55 | initialize: function(ordering) { |
|---|
| | 56 | |
|---|
| | 57 | var compareMethod = OpenLayers.ElementsIndexer.Translations[ordering]; |
|---|
| | 58 | |
|---|
| | 59 | this.compare = compareMethod || OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; |
|---|
| | 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 positioning for |
|---|
| | 68 | * the node to be inserted, this method uses a binary search. This makes inserting |
|---|
| | 69 | * O(log(n)). |
|---|
| | 70 | * |
|---|
| | 71 | * Parameters: |
|---|
| | 72 | * newNode - The new node to be inserted. |
|---|
| | 73 | */ |
|---|
| | 74 | insert: function(newNode) { |
|---|
| | 75 | var nodeId = this.getNodeId(newNode); |
|---|
| | 76 | |
|---|
| | 77 | this.determineZIndex(newNode); |
|---|
| | 79 | var leftIndex = -1; |
|---|
| | 80 | var rightIndex = this.order.length; |
|---|
| | 81 | var middle; |
|---|
| | 82 | |
|---|
| | 83 | while (rightIndex - leftIndex > 1) { |
|---|
| | 84 | middle = parseInt((leftIndex + rightIndex) / 2); |
|---|
| | 85 | |
|---|
| | 86 | var nextId = this.order[middle]; |
|---|
| | 87 | var next = OpenLayers.Util.getElement(nextId); |
|---|
| | 88 | |
|---|
| | 89 | var placement = this.compare(this, newNode, next); |
|---|
| | 90 | |
|---|
| | 91 | if (placement > 0) { |
|---|
| | 92 | leftIndex = middle; |
|---|
| | 93 | } else { |
|---|
| | 94 | rightIndex = middle; |
|---|
| | 95 | } |
|---|
| | 96 | } |
|---|
| | 97 | |
|---|
| | 98 | this.order.splice(rightIndex, 0, nodeId); |
|---|
| | 99 | this.indices[nodeId] = this.getZIndex(newNode); |
|---|
| | 100 | |
|---|
| | 101 | // Return the next item in the array -- the id of the node that should |
|---|
| | 102 | // follow this one directly in the index order -- if any. |
|---|
| | 103 | var nextIndex = rightIndex + 1; |
|---|
| | 104 | |
|---|
| | 105 | var returnVal = null; |
|---|
| | 106 | if (nextIndex > 0 && nextIndex < this.order.length) { |
|---|
| | 107 | returnVal = this.order[nextIndex]; |
|---|
| | 108 | } |
|---|
| | 109 | return returnVal; |
|---|
| | 110 | }, |
|---|
| | 111 | |
|---|
| | 112 | remove: function(node) { |
|---|
| | 113 | var nodeId = this.getNodeId(node); |
|---|
| | 114 | var arrayIndex = this.order.indexOf(nodeId); |
|---|
| | 115 | if (arrayIndex >= 0) { |
|---|
| | 116 | // Remove it from the order array, as well as deleting the node |
|---|
| | 117 | // from the indeces hash. |
|---|
| | 118 | this.order.splice(arrayIndex, 1); |
|---|
| | 119 | delete this.indices[nodeId]; |
|---|
| | 120 | |
|---|
| | 121 | // Reset the maxium z-index based on the last item in the order array. |
|---|
| | 122 | var lastId = this.order[this.order.length - 1]; |
|---|
| | 123 | this.maxZIndex = this.indices[lastId]; |
|---|
| | 124 | } |
|---|
| | 125 | }, |
|---|
| | 126 | |
|---|
| | 127 | exists: function(node) { |
|---|
| | 128 | var nodeId = this.getNodeId(node); |
|---|
| | 129 | return this.indices[nodeId] != null; |
|---|
| | 130 | }, |
|---|
| | 131 | |
|---|
| | 132 | /** |
|---|
| | 133 | * APIMethod: getZIndex |
|---|
| | 134 | * Get the z-index value for the current node from the node data itself. |
|---|
| | 135 | */ |
|---|
| | 136 | getZIndex: function(node) { |
|---|
| | 137 | return node._style.graphicZIndex; |
|---|
| | 138 | }, |
|---|
| | 139 | |
|---|
| | 140 | /** |
|---|
| | 141 | * APIMethod: getMaxZIndex |
|---|
| | 142 | * Get the maximum z-index stored within the indexer. |
|---|
| | 143 | */ |
|---|
| | 144 | getMaxZIndex: function() { |
|---|
| | 145 | return this.maxZIndex; |
|---|
| | 146 | }, |
|---|
| | 147 | |
|---|
| | 148 | /** |
|---|
| | 149 | * APIMethod: setZIndex |
|---|
| | 150 | * Set the z-index for the given node. |
|---|
| | 151 | */ |
|---|
| | 152 | setZIndex: function(node, zIndex) { |
|---|
| | 153 | node._style.graphicZIndex = zIndex; |
|---|
| | 154 | }, |
|---|
| | 155 | |
|---|
| | 156 | /** |
|---|
| | 157 | * Method: determineZIndex |
|---|
| | 158 | * Determine the z-index for the current node if there isn't one, |
|---|
| | 159 | * and set the maximum value if we've found a new maximum. |
|---|
| | 160 | */ |
|---|
| | 161 | determineZIndex: function(node) { |
|---|
| | 162 | var zIndex = node._style.graphicZIndex; |
|---|
| | 163 | |
|---|
| | 164 | // Everything must have a zIndex. If none is specified, |
|---|
| | 165 | // this means the user *must* (hint: assumption) want this |
|---|
| | 166 | // node to succomb to drawing order. To enforce drawing order |
|---|
| | 167 | // over all indexing methods, we'll create a new z-index that's |
|---|
| | 168 | // greater than any currently in the indexer. |
|---|
| | 169 | if (zIndex == null) { |
|---|
| | 170 | zIndex = this.maxZIndex; |
|---|
| | 171 | } |
|---|
| | 172 | |
|---|
| | 173 | if (zIndex > this.maxZIndex) { |
|---|
| | 174 | this.maxZIndex = zIndex; |
|---|
| | 175 | } |
|---|
| | 176 | |
|---|
| | 177 | node._style.graphicZIndex = zIndex; |
|---|
| | 178 | }, |
|---|
| | 179 | |
|---|
| | 180 | /** |
|---|
| | 181 | * Method: getNodeId |
|---|
| | 182 | * Get the id of the node passed in. |
|---|
| | 183 | */ |
|---|
| | 184 | getNodeId: function(node) { |
|---|
| | 185 | return node.id; |
|---|
| | 186 | }, |
|---|
| | 187 | |
|---|
| | 188 | CLASS_NAME: "OpenLayers.ElementsIndexer" |
|---|
| | 189 | }); |
|---|
| | 190 | |
|---|
| | 191 | |
|---|
| | 192 | // These are the compare methods for figuring out where a new |
|---|
| | 193 | // node should be placed within the indexer. These methods are very |
|---|
| | 194 | // similar to general sorting methods in that they return -1, 0, and 1 |
|---|
| | 195 | // to specify the direction in which new nodes fall in the ordering. |
|---|
| | 196 | OpenLayers.ElementsIndexer.IndexingMethods = { |
|---|
| | 197 | /** |
|---|
| | 198 | * Method: Z_ORDER |
|---|
| | 199 | * This compare method is used by other comparison methods. |
|---|
| | 200 | * It can be used individually for ordering, but is not recommended, |
|---|
| | 201 | * because it doesn't subscribe to drawing order. |
|---|
| | 202 | */ |
|---|
| | 203 | Z_ORDER: function(indexer, newNode, next) { |
|---|
| | 204 | var newZIndex = indexer.getZIndex(newNode); |
|---|
| | 205 | |
|---|
| | 206 | var returnVal = 0; |
|---|
| | 207 | if (next) { |
|---|
| | 208 | var nextZIndex = indexer.getZIndex(next); |
|---|
| | 209 | returnVal = newZIndex - nextZIndex; |
|---|
| | 210 | } |
|---|
| | 211 | |
|---|
| | 212 | return returnVal; |
|---|
| | 213 | }, |
|---|
| | 214 | /** |
|---|
| | 215 | * API Method: Z_ORDER_DRAWING_ORDER |
|---|
| | 216 | * This method orders nodes by their z-index, but does so in a way |
|---|
| | 217 | * that, if there are other nodes with the same z-index, the newest drawn |
|---|
| | 218 | * will be the front most within that z-index. This is the default |
|---|
| | 219 | * indexing method. |
|---|
| | 220 | */ |
|---|
| | 221 | Z_ORDER_DRAWING_ORDER: function(indexer, newNode, next) { |
|---|
| | 222 | var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, next); |
|---|
| | 223 | |
|---|
| | 224 | // Make Z_ORDER subscribe to drawing order by pushing it above |
|---|
| | 225 | // all of the other nodes with the same z-index. |
|---|
| | 226 | if (next && returnVal == 0) { |
|---|
| | 227 | returnVal = 1; |
|---|
| | 228 | } |
|---|
| | 229 | |
|---|
| | 230 | return returnVal; |
|---|
| | 231 | }, |
|---|
| | 232 | /** |
|---|
| | 233 | * API Method: Z_ORDER_Y_ORDER |
|---|
| | 234 | * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it |
|---|
| | 235 | * best describes which ordering methods have precedence (though, the name would |
|---|
| | 236 | * be too long). This method orders nodes by their z-index, but does so in a way |
|---|
| | 237 | * that, if there are other nodes with the same z-index, the nodes with the |
|---|
| | 238 | * lower y position will be "closer" than those with a higher y position. |
|---|
| | 239 | * If two nodes have the exact same y position, however, then this method |
|---|
| | 240 | * will revert to using drawing order to decide placement. |
|---|
| | 241 | */ |
|---|
| | 242 | Z_ORDER_Y_ORDER: function(indexer, newNode, next) { |
|---|
| | 243 | var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, next); |
|---|
| | 244 | |
|---|
| | 245 | if (next && returnVal == 0) { |
|---|
| | 246 | var newLat = newNode._geometry.getBounds().bottom; |
|---|
| | 247 | var nextLat = next._geometry.getBounds().bottom; |
|---|
| | 248 | |
|---|
| | 249 | var result = nextLat - newLat; |
|---|
| | 250 | |
|---|
| | 251 | if (result == 0) { |
|---|
| | 252 | returnVal = 1; |
|---|
| | 253 | } else { |
|---|
| | 254 | returnVal = result; |
|---|
| | 255 | } |
|---|
| | 256 | } |
|---|
| | 257 | |
|---|
| | 258 | return returnVal; |
|---|
| | 259 | } |
|---|
| | 260 | } |
|---|
| | 261 | |
|---|
| | 429 | |
|---|
| | 430 | redrawNode: function(id, geometry, style, featureId) { |
|---|
| | 431 | // Get the node if it's already on the map. |
|---|
| | 432 | var currentNode = OpenLayers.Util.getElement(id); |
|---|
| | 433 | |
|---|
| | 434 | // Create a new node, or use the current one if it's |
|---|
| | 435 | // already there. |
|---|
| | 436 | var newNode; |
|---|
| | 437 | if (!currentNode) { |
|---|
| | 438 | var nodeType = this.getNodeType(geometry, style); |
|---|
| | 439 | newNode = this.createNode(nodeType, id); |
|---|
| | 440 | } else { |
|---|
| | 441 | newNode = currentNode; |
|---|
| | 442 | } |
|---|
| | 443 | |
|---|
| | 444 | // Set the data for the node, then draw it. |
|---|
| | 445 | newNode._featureId = featureId; |
|---|
| | 446 | newNode._geometry = geometry; |
|---|
| | 447 | newNode._geometryClass = geometry.CLASS_NAME; |
|---|
| | 448 | newNode._style = style; |
|---|
| | 449 | newNode = this.drawGeometryNode(newNode, geometry, style); |
|---|
| | 450 | |
|---|
| | 451 | // If the node is known to the indexer, remove it so we can |
|---|
| | 452 | // recalculate where it should go. |
|---|
| | 453 | if (this.indexer.exists(newNode)) { |
|---|
| | 454 | this.indexer.remove(newNode); |
|---|
| | 455 | } |
|---|
| | 456 | |
|---|
| | 457 | // Insert the node into the indexer so it can show us where to place it. |
|---|
| | 458 | // Note that this operation is O(log(n)). If there's a performance problem |
|---|
| | 459 | // (when dragging, for instance) this is likely where it would be. |
|---|
| | 460 | var nextNodeId = this.indexer.insert(newNode); |
|---|
| | 462 | // If the new node should be before another in the index |
|---|
| | 463 | // order, insert the new node before the next; else, lets just |
|---|
| | 464 | // append the new one on the end, making it the highest in the index order. |
|---|
| | 465 | if (nextNodeId) { |
|---|
| | 466 | var nextNode = OpenLayers.Util.getElement(nextNodeId); |
|---|
| | 467 | this.root.insertBefore(newNode, nextNode); |
|---|
| | 468 | } else { |
|---|
| | 469 | this.root.appendChild(newNode); |
|---|
| | 470 | } |
|---|
| | 471 | |
|---|
| | 472 | this.postDraw(newNode); |
|---|
| | 473 | }, |
|---|
| | 474 | |
|---|
| | 475 | redrawBackgroundNode: function(id, geometry, style, featureId) { |
|---|
| | 476 | var backgroundStyle = OpenLayers.Util.extend({}, style); |
|---|
| | 477 | |
|---|
| | 478 | // Set regular style attributes to apply to the background styles. |
|---|
| | 479 | backgroundStyle.externalGraphic = backgroundStyle.backgroundGraphic; |
|---|
| | 480 | backgroundStyle.graphicXOffset = backgroundStyle.backgroundXOffset; |
|---|
| | 481 | backgroundStyle.graphicYOffset = backgroundStyle.backgroundYOffset; |
|---|
| | 482 | backgroundStyle.graphicZIndex = backgroundStyle.backgroundGraphicZIndex; |
|---|
| | 483 | |
|---|
| | 484 | // Erase background styles. |
|---|
| | 485 | backgroundStyle.backgroundGraphic = null; |
|---|
| | 486 | backgroundStyle.backgroundXOffset = null; |
|---|
| | 487 | backgroundStyle.backgroundYOffset = null; |
|---|
| | 488 | backgroundStyle.backgroundGraphicZIndex = null; |
|---|
| | 489 | |
|---|
| | 490 | this.redrawNode(id + this.BACKGROUND_ID_SUFFIX, geometry, backgroundStyle, null); |
|---|
| | 491 | }, |
|---|
| | 492 | |
|---|