[76] | 1 | /** |
---|
| 2 | * Copyright (c) 2008-2010 The Open Source Geospatial Foundation |
---|
| 3 | * |
---|
| 4 | * Published under the BSD license. |
---|
| 5 | * See http://svn.geoext.org/core/trunk/geoext/license.txt for the full text |
---|
| 6 | * of the license. |
---|
| 7 | */ |
---|
| 8 | |
---|
| 9 | /** |
---|
| 10 | * @include GeoExt/widgets/tree/LayerNode.js |
---|
| 11 | * @include GeoExt/widgets/tree/LayerContainer.js |
---|
| 12 | */ |
---|
| 13 | Ext.namespace("GeoExt.tree"); |
---|
| 14 | |
---|
| 15 | /** api: (define) |
---|
| 16 | * module = GeoExt.tree |
---|
| 17 | * class = LayerLoader |
---|
| 18 | * base_link = `Ext.util.Observable <http://dev.sencha.com/deploy/dev/docs/?class=Ext.util.Observable>`_ |
---|
| 19 | */ |
---|
| 20 | |
---|
| 21 | /** api: constructor |
---|
| 22 | * .. class:: LayerLoader |
---|
| 23 | * |
---|
| 24 | * A loader that will load all layers of a :class:`GeoExt.data.LayerStore` |
---|
| 25 | * By default, only layers that have displayInLayerSwitcher set to true |
---|
| 26 | * will be included. The childrens' iconCls defaults to |
---|
| 27 | * "gx-tree-layer-icon". |
---|
| 28 | */ |
---|
| 29 | GeoExt.tree.LayerLoader = function(config) { |
---|
| 30 | Ext.apply(this, config); |
---|
| 31 | this.addEvents( |
---|
| 32 | |
---|
| 33 | /** api: event[beforeload] |
---|
| 34 | * Triggered before loading children. Return false to avoid |
---|
| 35 | * loading children. |
---|
| 36 | * |
---|
| 37 | * Listener arguments: |
---|
| 38 | * |
---|
| 39 | * * loader - :class:`GeoExt.tree.LayerLoader` this loader |
---|
| 40 | * * node - ``Ex.tree.TreeNode`` the node that this loader is |
---|
| 41 | * configured with |
---|
| 42 | */ |
---|
| 43 | "beforeload", |
---|
| 44 | |
---|
| 45 | /** api: event[load] |
---|
| 46 | * Triggered after children wer loaded. |
---|
| 47 | * |
---|
| 48 | * Listener arguments: |
---|
| 49 | * |
---|
| 50 | * * loader - :class:`GeoExt.tree.LayerLoader` this loader |
---|
| 51 | * * node - ``Ex.tree.TreeNode`` the node that this loader is |
---|
| 52 | * configured with |
---|
| 53 | */ |
---|
| 54 | "load" |
---|
| 55 | ); |
---|
| 56 | |
---|
| 57 | GeoExt.tree.LayerLoader.superclass.constructor.call(this); |
---|
| 58 | }; |
---|
| 59 | |
---|
| 60 | Ext.extend(GeoExt.tree.LayerLoader, Ext.util.Observable, { |
---|
| 61 | |
---|
| 62 | /** api: config[store] |
---|
| 63 | * :class:`GeoExt.data.LayerStore` |
---|
| 64 | * The layer store containing layers to be added by this loader. |
---|
| 65 | */ |
---|
| 66 | store: null, |
---|
| 67 | |
---|
| 68 | /** api: config[filter] |
---|
| 69 | * ``Function`` |
---|
| 70 | * A function, called in the scope of this loader, with a layer record |
---|
| 71 | * as argument. Is expected to return true for layers to be loaded, false |
---|
| 72 | * otherwise. By default, the filter checks for displayInLayerSwitcher: |
---|
| 73 | * |
---|
| 74 | * .. code-block:: javascript |
---|
| 75 | * |
---|
| 76 | * filter: function(record) { |
---|
| 77 | * return record.getLayer().displayInLayerSwitcher == true |
---|
| 78 | * } |
---|
| 79 | */ |
---|
| 80 | filter: function(record) { |
---|
| 81 | return record.getLayer().displayInLayerSwitcher == true; |
---|
| 82 | }, |
---|
| 83 | |
---|
| 84 | /** api: config[baseAttrs] |
---|
| 85 | * An object containing attributes to be added to all nodes created by |
---|
| 86 | * this loader. |
---|
| 87 | */ |
---|
| 88 | baseAttrs: null, |
---|
| 89 | |
---|
| 90 | /** api: config[uiProviders] |
---|
| 91 | * ``Object`` |
---|
| 92 | * An optional object containing properties which specify custom |
---|
| 93 | * GeoExt.tree.LayerNodeUI implementations. If the optional uiProvider |
---|
| 94 | * attribute for child nodes is a string rather than a reference to a |
---|
| 95 | * TreeNodeUI implementation, then that string value is used as a |
---|
| 96 | * property name in the uiProviders object. If not provided, the |
---|
| 97 | * uiProviders object will be taken from the ownerTree's loader. |
---|
| 98 | */ |
---|
| 99 | uiProviders: null, |
---|
| 100 | |
---|
| 101 | /** private: method[load] |
---|
| 102 | * :param node: ``Ext.tree.TreeNode`` The node to add children to. |
---|
| 103 | * :param callback: ``Function`` |
---|
| 104 | */ |
---|
| 105 | load: function(node, callback) { |
---|
| 106 | if(this.fireEvent("beforeload", this, node)) { |
---|
| 107 | this.removeStoreHandlers(); |
---|
| 108 | while (node.firstChild) { |
---|
| 109 | node.removeChild(node.firstChild); |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | if(!this.uiProviders) { |
---|
| 113 | this.uiProviders = node.getOwnerTree().getLoader().uiProviders; |
---|
| 114 | } |
---|
| 115 | |
---|
| 116 | if(!this.store) { |
---|
| 117 | this.store = GeoExt.MapPanel.guess().layers; |
---|
| 118 | } |
---|
| 119 | this.store.each(function(record) { |
---|
| 120 | this.addLayerNode(node, record); |
---|
| 121 | }, this); |
---|
| 122 | this.addStoreHandlers(node); |
---|
| 123 | |
---|
| 124 | if(typeof callback == "function"){ |
---|
| 125 | callback(); |
---|
| 126 | } |
---|
| 127 | |
---|
| 128 | this.fireEvent("load", this, node); |
---|
| 129 | } |
---|
| 130 | }, |
---|
| 131 | |
---|
| 132 | /** private: method[onStoreAdd] |
---|
| 133 | * :param store: ``Ext.data.Store`` |
---|
| 134 | * :param records: ``Array(Ext.data.Record)`` |
---|
| 135 | * :param index: ``Number`` |
---|
| 136 | * :param node: ``Ext.tree.TreeNode`` |
---|
| 137 | * |
---|
| 138 | * Listener for the store's add event. |
---|
| 139 | */ |
---|
| 140 | onStoreAdd: function(store, records, index, node) { |
---|
| 141 | if(!this._reordering) { |
---|
| 142 | var nodeIndex = node.recordIndexToNodeIndex(index+records.length-1); |
---|
| 143 | for(var i=0; i<records.length; ++i) { |
---|
| 144 | this.addLayerNode(node, records[i], nodeIndex); |
---|
| 145 | } |
---|
| 146 | } |
---|
| 147 | }, |
---|
| 148 | |
---|
| 149 | /** private: method[onStoreRemove] |
---|
| 150 | * :param store: ``Ext.data.Store`` |
---|
| 151 | * :param record: ``Ext.data.Record`` |
---|
| 152 | * :param index: ``Number`` |
---|
| 153 | * :param node: ``Ext.tree.TreeNode`` |
---|
| 154 | * |
---|
| 155 | * Listener for the store's remove event. |
---|
| 156 | */ |
---|
| 157 | onStoreRemove: function(store, record, index, node) { |
---|
| 158 | if(!this._reordering) { |
---|
| 159 | this.removeLayerNode(node, record); |
---|
| 160 | } |
---|
| 161 | }, |
---|
| 162 | |
---|
| 163 | /** private: method[addLayerNode] |
---|
| 164 | * :param node: ``Ext.tree.TreeNode`` The node that the layer node will |
---|
| 165 | * be added to as child. |
---|
| 166 | * :param layerRecord: ``Ext.data.Record`` The layer record containing the |
---|
| 167 | * layer to be added. |
---|
| 168 | * :param index: ``Number`` Optional index for the new layer. Default is 0. |
---|
| 169 | * |
---|
| 170 | * Adds a child node representing a layer of the map |
---|
| 171 | */ |
---|
| 172 | addLayerNode: function(node, layerRecord, index) { |
---|
| 173 | index = index || 0; |
---|
| 174 | if (this.filter(layerRecord) === true) { |
---|
| 175 | var child = this.createNode({ |
---|
| 176 | nodeType: 'gx_layer', |
---|
| 177 | layer: layerRecord.getLayer(), |
---|
| 178 | layerStore: this.store |
---|
| 179 | }); |
---|
| 180 | var sibling = node.item(index); |
---|
| 181 | if(sibling) { |
---|
| 182 | node.insertBefore(child, sibling); |
---|
| 183 | } else { |
---|
| 184 | node.appendChild(child); |
---|
| 185 | } |
---|
| 186 | child.on("move", this.onChildMove, this); |
---|
| 187 | } |
---|
| 188 | }, |
---|
| 189 | |
---|
| 190 | /** private: method[removeLayerNode] |
---|
| 191 | * :param node: ``Ext.tree.TreeNode`` The node that the layer node will |
---|
| 192 | * be removed from as child. |
---|
| 193 | * :param layerRecord: ``Ext.data.Record`` The layer record containing the |
---|
| 194 | * layer to be removed. |
---|
| 195 | * |
---|
| 196 | * Removes a child node representing a layer of the map |
---|
| 197 | */ |
---|
| 198 | removeLayerNode: function(node, layerRecord) { |
---|
| 199 | if (this.filter(layerRecord) === true) { |
---|
| 200 | var child = node.findChildBy(function(node) { |
---|
| 201 | return node.layer == layerRecord.getLayer(); |
---|
| 202 | }); |
---|
| 203 | if(child) { |
---|
| 204 | child.un("move", this.onChildMove, this); |
---|
| 205 | child.remove(); |
---|
| 206 | node.reload(); |
---|
| 207 | } |
---|
| 208 | } |
---|
| 209 | }, |
---|
| 210 | |
---|
| 211 | /** private: method[onChildMove] |
---|
| 212 | * :param tree: ``Ext.data.Tree`` |
---|
| 213 | * :param node: ``Ext.tree.TreeNode`` |
---|
| 214 | * :param oldParent: ``Ext.tree.TreeNode`` |
---|
| 215 | * :param newParent: ``Ext.tree.TreeNode`` |
---|
| 216 | * :param index: ``Number`` |
---|
| 217 | * |
---|
| 218 | * Listener for child node "move" events. This updates the order of |
---|
| 219 | * records in the store based on new node order if the node has not |
---|
| 220 | * changed parents. |
---|
| 221 | */ |
---|
| 222 | onChildMove: function(tree, node, oldParent, newParent, index) { |
---|
| 223 | this._reordering = true; |
---|
| 224 | // remove the record and re-insert it at the correct index |
---|
| 225 | var record = this.store.getByLayer(node.layer); |
---|
| 226 | |
---|
| 227 | if(newParent instanceof GeoExt.tree.LayerContainer && |
---|
| 228 | this.store === newParent.loader.store) { |
---|
| 229 | newParent.loader._reordering = true; |
---|
| 230 | this.store.remove(record); |
---|
| 231 | var newRecordIndex; |
---|
| 232 | if(newParent.childNodes.length > 1) { |
---|
| 233 | // find index by neighboring node in the same container |
---|
| 234 | var searchIndex = (index === 0) ? index + 1 : index - 1; |
---|
| 235 | newRecordIndex = this.store.findBy(function(r) { |
---|
| 236 | return newParent.childNodes[searchIndex].layer === r.getLayer(); |
---|
| 237 | }); |
---|
| 238 | index === 0 && newRecordIndex++; |
---|
| 239 | } else if(oldParent.parentNode === newParent.parentNode){ |
---|
| 240 | // find index by last node of a container above |
---|
| 241 | var prev = newParent; |
---|
| 242 | do { |
---|
| 243 | prev = prev.previousSibling; |
---|
| 244 | } while (prev && !(prev instanceof GeoExt.tree.LayerContainer && prev.lastChild)); |
---|
| 245 | if(prev) { |
---|
| 246 | newRecordIndex = this.store.findBy(function(r) { |
---|
| 247 | return prev.lastChild.layer === r.getLayer(); |
---|
| 248 | }); |
---|
| 249 | } else { |
---|
| 250 | // find indext by first node of a container below |
---|
| 251 | var next = newParent; |
---|
| 252 | do { |
---|
| 253 | next = next.nextSibling; |
---|
| 254 | } while (next && !(next instanceof GeoExt.tree.LayerContainer && next.firstChild)); |
---|
| 255 | if(next) { |
---|
| 256 | newRecordIndex = this.store.findBy(function(r) { |
---|
| 257 | return next.firstChild.layer === r.getLayer(); |
---|
| 258 | }); |
---|
| 259 | } |
---|
| 260 | newRecordIndex++; |
---|
| 261 | } |
---|
| 262 | } |
---|
| 263 | if(newRecordIndex !== undefined) { |
---|
| 264 | this.store.insert(newRecordIndex, [record]); |
---|
| 265 | window.setTimeout(function() { |
---|
| 266 | newParent.reload(); |
---|
| 267 | oldParent.reload(); |
---|
| 268 | }); |
---|
| 269 | } else { |
---|
| 270 | this.store.insert(oldRecordIndex, [record]); |
---|
| 271 | } |
---|
| 272 | delete newParent.loader._reordering; |
---|
| 273 | } |
---|
| 274 | delete this._reordering; |
---|
| 275 | }, |
---|
| 276 | |
---|
| 277 | /** private: method[addStoreHandlers] |
---|
| 278 | * :param node: :class:`GeoExt.tree.LayerNode` |
---|
| 279 | */ |
---|
| 280 | addStoreHandlers: function(node) { |
---|
| 281 | if(!this._storeHandlers) { |
---|
| 282 | this._storeHandlers = { |
---|
| 283 | "add": this.onStoreAdd.createDelegate(this, [node], true), |
---|
| 284 | "remove": this.onStoreRemove.createDelegate(this, [node], true) |
---|
| 285 | }; |
---|
| 286 | for(var evt in this._storeHandlers) { |
---|
| 287 | this.store.on(evt, this._storeHandlers[evt], this); |
---|
| 288 | } |
---|
| 289 | } |
---|
| 290 | }, |
---|
| 291 | |
---|
| 292 | /** private: method[removeStoreHandlers] |
---|
| 293 | */ |
---|
| 294 | removeStoreHandlers: function() { |
---|
| 295 | if(this._storeHandlers) { |
---|
| 296 | for(var evt in this._storeHandlers) { |
---|
| 297 | this.store.un(evt, this._storeHandlers[evt], this); |
---|
| 298 | } |
---|
| 299 | delete this._storeHandlers; |
---|
| 300 | } |
---|
| 301 | }, |
---|
| 302 | |
---|
| 303 | /** api: method[createNode] |
---|
| 304 | * :param attr: ``Object`` attributes for the new node |
---|
| 305 | * |
---|
| 306 | * Override this function for custom TreeNode node implementation, or to |
---|
| 307 | * modify the attributes at creation time. |
---|
| 308 | */ |
---|
| 309 | createNode: function(attr) { |
---|
| 310 | if(this.baseAttrs){ |
---|
| 311 | Ext.apply(attr, this.baseAttrs); |
---|
| 312 | } |
---|
| 313 | if(typeof attr.uiProvider == 'string'){ |
---|
| 314 | attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider); |
---|
| 315 | } |
---|
| 316 | attr.nodeType = attr.nodeType || "gx_layer"; |
---|
| 317 | |
---|
| 318 | return new Ext.tree.TreePanel.nodeTypes[attr.nodeType](attr); |
---|
| 319 | }, |
---|
| 320 | |
---|
| 321 | /** private: method[destroy] |
---|
| 322 | */ |
---|
| 323 | destroy: function() { |
---|
| 324 | this.removeStoreHandlers(); |
---|
| 325 | } |
---|
| 326 | }); |
---|