| | 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 | */ |
|---|
| | 14 | OpenLayers.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 | * {Hash} 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 | * ordering - {Function} The canonical name of the indexing method to |
|---|
| | 53 | * use. See OpenLayers.ElementsIndexer.Translations below. If null, |
|---|
| | 54 | * this defaults to "z". |
|---|
| | 55 | */ |
|---|
| | 56 | initialize: function(ordering) { |
|---|
| | 57 | |
|---|
| | 58 | var compareMethod = OpenLayers.ElementsIndexer.Translations[ordering]; |
|---|
| | 59 | |
|---|
| | 60 | this.compare = compareMethod || OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER; |
|---|
| | 61 | this.order = []; |
|---|
| | 62 | this.indices = {}; |
|---|
| | 63 | this.maxZIndex = 0; |
|---|
| | 64 | }, |
|---|
| | 65 | |
|---|
| | 66 | /** |
|---|
| | 67 | * APIMethod: insert |
|---|
| | 68 | * Insert a new node into the indexer. In order to find the correct positioning for |
|---|
| | 69 | * the node to be inserted, this method uses a binary search. This makes inserting |
|---|
| | 70 | * O(log(n)). |
|---|
| | 71 | * |
|---|
| | 72 | * Parameters: |
|---|
| | 73 | * newNode - The new node to be inserted. |
|---|
| | 74 | */ |
|---|
| | 75 | insert: function(newNode) { |
|---|
| | 76 | var nodeId = this.getNodeId(newNode); |
|---|
| | 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 next = OpenLayers.Util.getElement(nextId); |
|---|
| | 89 | |
|---|
| | 90 | var placement = this.compare(this, newNode, next); |
|---|
| | 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 | // Return the next item in the array -- the id of the node that should |
|---|
| | 103 | // follow this one directly in the index order -- if any. |
|---|
| | 104 | var nextIndex = rightIndex + 1; |
|---|
| | 105 | |
|---|
| | 106 | var returnVal = null; |
|---|
| | 107 | if (nextIndex > 0 && nextIndex < this.order.length) { |
|---|
| | 108 | returnVal = this.order[nextIndex]; |
|---|
| | 109 | } |
|---|
| | 110 | return returnVal; |
|---|
| | 111 | }, |
|---|
| | 112 | |
|---|
| | 113 | remove: function(node) { |
|---|
| | 114 | var nodeId = this.getNodeId(node); |
|---|
| | 115 | var arrayIndex = this.order.indexOf(nodeId); |
|---|
| | 116 | if (arrayIndex >= 0) { |
|---|
| | 117 | // Remove it from the order array, as well as deleting the node |
|---|
| | 118 | // from the indeces hash. |
|---|
| | 119 | this.order.splice(arrayIndex, 1); |
|---|
| | 120 | delete this.indices[nodeId]; |
|---|
| | 121 | |
|---|
| | 122 | // Reset the maxium z-index based on the last item in the order array. |
|---|
| | 123 | var lastId = this.order[this.order.length - 1]; |
|---|
| | 124 | this.maxZIndex = this.indices[lastId]; |
|---|
| | 125 | } |
|---|
| | 126 | }, |
|---|
| | 127 | |
|---|
| | 128 | exists: function(node) { |
|---|
| | 129 | var nodeId = this.getNodeId(node); |
|---|
| | 130 | return this.indices[nodeId] != null; |
|---|
| | 131 | }, |
|---|
| | 132 | |
|---|
| | 133 | /** |
|---|
| | 134 | * APIMethod: getZIndex |
|---|
| | 135 | * Get the z-index value for the current node from the node data itself. |
|---|
| | 136 | */ |
|---|
| | 137 | getZIndex: function(node) { |
|---|
| | 138 | return node._style.graphicZIndex; |
|---|
| | 139 | }, |
|---|
| | 140 | |
|---|
| | 141 | /** |
|---|
| | 142 | * APIMethod: getMaxZIndex |
|---|
| | 143 | * Get the maximum z-index stored within the indexer. |
|---|
| | 144 | */ |
|---|
| | 145 | getMaxZIndex: function() { |
|---|
| | 146 | return this.maxZIndex; |
|---|
| | 147 | }, |
|---|
| | 148 | |
|---|
| | 149 | /** |
|---|
| | 150 | * APIMethod: setZIndex |
|---|
| | 151 | * Set the z-index for the given node. |
|---|
| | 152 | */ |
|---|
| | 153 | setZIndex: function(node, zIndex) { |
|---|
| | 154 | node._style.graphicZIndex = zIndex; |
|---|
| | 155 | }, |
|---|
| | 156 | |
|---|
| | 157 | /** |
|---|
| | 158 | * Method: determineZIndex |
|---|
| | 159 | * Determine the z-index for the current node if there isn't one, |
|---|
| | 160 | * and set the maximum value if we've found a new maximum. |
|---|
| | 161 | */ |
|---|
| | 162 | determineZIndex: function(node) { |
|---|
| | 163 | var zIndex = node._style.graphicZIndex; |
|---|
| | 164 | |
|---|
| | 165 | // Everything must have a zIndex. If none is specified, |
|---|
| | 166 | // this means the user *must* (hint: assumption) want this |
|---|
| | 167 | // node to succomb to drawing order. To enforce drawing order |
|---|
| | 168 | // over all indexing methods, we'll create a new z-index that's |
|---|
| | 169 | // greater than any currently in the indexer. |
|---|
| | 170 | if (zIndex == null) { |
|---|
| | 171 | zIndex = this.maxZIndex; |
|---|
| | 172 | } |
|---|
| | 173 | |
|---|
| | 174 | if (zIndex > this.maxZIndex) { |
|---|
| | 175 | this.maxZIndex = zIndex; |
|---|
| | 176 | } |
|---|
| | 177 | |
|---|
| | 178 | node._style.graphicZIndex = zIndex; |
|---|
| | 179 | }, |
|---|
| | 180 | |
|---|
| | 181 | /** |
|---|
| | 182 | * Method: getNodeId |
|---|
| | 183 | * Get the id of the node passed in. |
|---|
| | 184 | */ |
|---|
| | 185 | getNodeId: function(node) { |
|---|
| | 186 | return node.id; |
|---|
| | 187 | }, |
|---|
| | 188 | |
|---|
| | 189 | CLASS_NAME: "OpenLayers.ElementsIndexer" |
|---|
| | 190 | }); |
|---|
| | 191 | |
|---|
| | 192 | |
|---|
| | 193 | // These are the compare methods for figuring out where a new |
|---|
| | 194 | // node should be placed within the indexer. These methods are very |
|---|
| | 195 | // similar to general sorting methods in that they return -1, 0, and 1 |
|---|
| | 196 | // to specify the direction in which new nodes fall in the ordering. |
|---|
| | 197 | OpenLayers.ElementsIndexer.IndexingMethods = { |
|---|
| | 198 | /** |
|---|
| | 199 | * Method: Z_ORDER |
|---|
| | 200 | * This compare method is used by other comparison methods. |
|---|
| | 201 | * It can be used individually for ordering, but is not recommended, |
|---|
| | 202 | * because it doesn't subscribe to drawing order. |
|---|
| | 203 | */ |
|---|
| | 204 | Z_ORDER: function(indexer, newNode, next) { |
|---|
| | 205 | var newZIndex = indexer.getZIndex(newNode); |
|---|
| | 206 | |
|---|
| | 207 | var returnVal = 0; |
|---|
| | 208 | if (next) { |
|---|
| | 209 | var nextZIndex = indexer.getZIndex(next); |
|---|
| | 210 | returnVal = newZIndex - nextZIndex; |
|---|
| | 211 | } |
|---|
| | 212 | |
|---|
| | 213 | return returnVal; |
|---|
| | 214 | }, |
|---|
| | 215 | /** |
|---|
| | 216 | * API Method: Z_ORDER_DRAWING_ORDER |
|---|
| | 217 | * This method orders nodes by their z-index, but does so in a way |
|---|
| | 218 | * that, if there are other nodes with the same z-index, the newest drawn |
|---|
| | 219 | * will be the front most within that z-index. This is the default |
|---|
| | 220 | * indexing method. |
|---|
| | 221 | */ |
|---|
| | 222 | Z_ORDER_DRAWING_ORDER: function(indexer, newNode, next) { |
|---|
| | 223 | var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, next); |
|---|
| | 224 | |
|---|
| | 225 | // Make Z_ORDER subscribe to drawing order by pushing it above |
|---|
| | 226 | // all of the other nodes with the same z-index. |
|---|
| | 227 | if (next && returnVal == 0) { |
|---|
| | 228 | returnVal = 1; |
|---|
| | 229 | } |
|---|
| | 230 | |
|---|
| | 231 | return returnVal; |
|---|
| | 232 | }, |
|---|
| | 233 | /** |
|---|
| | 234 | * API Method: Z_ORDER_Y_ORDER |
|---|
| | 235 | * This one should really be called Z_ORDER_Y_ORDER_DRAWING_ORDER, as it |
|---|
| | 236 | * best describes which ordering methods have precedence (though, the name would |
|---|
| | 237 | * be too long). This method orders nodes by their z-index, but does so in a way |
|---|
| | 238 | * that, if there are other nodes with the same z-index, the nodes with the |
|---|
| | 239 | * lower y position will be "closer" than those with a higher y position. |
|---|
| | 240 | * If two nodes have the exact same y position, however, then this method |
|---|
| | 241 | * will revert to using drawing order to decide placement. |
|---|
| | 242 | */ |
|---|
| | 243 | Z_ORDER_Y_ORDER: function(indexer, newNode, next) { |
|---|
| | 244 | var returnVal = OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER(indexer, newNode, next); |
|---|
| | 245 | |
|---|
| | 246 | if (next && returnVal == 0) { |
|---|
| | 247 | var newLat = newNode._geometry.getBounds().bottom; |
|---|
| | 248 | var nextLat = next._geometry.getBounds().bottom; |
|---|
| | 249 | |
|---|
| | 250 | var result = nextLat - newLat; |
|---|
| | 251 | |
|---|
| | 252 | if (result == 0) { |
|---|
| | 253 | returnVal = 1; |
|---|
| | 254 | } else { |
|---|
| | 255 | returnVal = result; |
|---|
| | 256 | } |
|---|
| | 257 | } |
|---|
| | 258 | |
|---|
| | 259 | return returnVal; |
|---|
| | 260 | } |
|---|
| | 261 | } |
|---|
| | 262 | |
|---|
| | 263 | /** |
|---|
| | 264 | * Translate canonical names for ordering methods to their |
|---|
| | 265 | * function equivalents. |
|---|
| | 266 | */ |
|---|
| | 267 | OpenLayers.ElementsIndexer.Translations = { |
|---|
| | 268 | "z" : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_DRAWING_ORDER, |
|---|
| | 269 | "y" : OpenLayers.ElementsIndexer.IndexingMethods.Z_ORDER_Y_ORDER |
|---|
| | 270 | } |
|---|
| | 271 | |
|---|
| | 272 | /** |
|---|