Ticket #22: treenodes.3.patch
| File treenodes.3.patch, 42.7 kB (added by dwins, 1 year ago) |
|---|
-
tests/lib/GeoExt/widgets/MapPanel.html
old new 35 35 } 36 36 37 37 function test_mappanel(t) { 38 t.plan( 2)38 t.plan(3) 39 39 40 40 loadMapPanel(); 41 41 t.eq(mapPanel.map.getCenter().toString(), "lon=5,lat=45", "Map center set correctly"); 42 42 t.eq(mapPanel.map.getZoom(), 4, "Zoom set correctly"); 43 t.eq(GeoExt.MapPanel.guess().id, mapPanel.id, "MapPanel guessed correctly"); 43 44 } 44 45 45 46 function test_allOverlays(t) { -
lib/GeoExt/widgets/tree/LayerParamsNode.js
old new 1 /** 2 * Copyright (c) 2008 The Open Planning Project 3 */ 4 Ext.namespace("GeoExt.tree"); 5 6 /** 7 * Class: GeoExt.tree.LayerParamsNode 8 * 9 * A subclass of GeoExt.tree.TristateCheckboxNode that provides a filter for a 10 * parent node (usually {<GeoExt.tree.LayerParamsNode>} or 11 * {<GeoExt.tree.FilteredLayerNode>}). 12 * 13 * The default node class (cls property) for this node type is 14 * "cql-filter-node", except for nodes that have no filter property set. 15 * Those will have the "cql-filter-group-node" class. LayerParamsNodes without 16 * a filter property set are used for filter grouping. 17 * 18 * To use this node type in a JSON config, set nodeType to "olCqlFilter". 19 * 20 * Inherits from: 21 * - <GeoExt.tree.TristateCheckboxNode> 22 */ 23 GeoExt.tree.LayerParamsNode = Ext.extend(GeoExt.tree.TristateCheckboxNode, { 24 25 /** 26 * ConfigProperty: params 27 * {Object} an object of layer params to apply to a parent layer if this node 28 * is checked. 29 */ 30 params: null, 31 32 /** 33 * Constructor: GeoExt.tree.LayerParamsNode 34 * 35 * Parameters: 36 * config - {Object} 37 */ 38 constructor: function(config) { 39 this.params = config.params; 40 41 this.defaultUI = this.defaultUI || GeoExt.tree.TristateCheckboxNodeUI; 42 43 config.cls = config.cls || "layerparams-node"; 44 config.leaf = config.leaf || !config.children; 45 GeoExt.tree.LayerParamsNode.superclass.constructor.apply(this, arguments); 46 }, 47 48 /** 49 * Method: updateParams 50 * 51 * Returns: 52 * {Object} 53 */ 54 updateParams: function() { 55 var params = this.attributes.checked ? Ext.applyIf({}, this.params) : {}; 56 var node, childParams; 57 for(var i=0; i<this.childNodes.length; ++i) { 58 node = this.childNodes[i]; 59 childParams = node.updateParams(); 60 for(var p in childParams) { 61 params[p] = (params[p] || []).concat(childParams[p]); 62 } 63 } 64 return params; 65 } 66 }); 67 68 GeoExt.tree.LayerParamsNode.add = function(parent) { 69 if(!parent.loaded) { 70 var subLayers = []; 71 var layer = parent.layer; 72 var parentAttrs = parent.attributes; 73 if(layer.params[parent.attributes.param] instanceof Array) { 74 subLayers = layer.params[parentAttrs.param]; 75 parent.loaded = true; 76 parent.leaf = false; 77 } 78 for(var i=0; i<subLayers.length; i++) { 79 subLayer = subLayers[i]; 80 var params = {}; 81 params[parentAttrs.param] = subLayer; 82 parent.appendChild( 83 new GeoExt.tree.LayerParamsNode({ 84 params: params, 85 text: parentAttrs.texts ? parentAttrs.texts[i] : subLayer, 86 checked: true 87 }) 88 ); 89 } 90 parent.on("childcheckchange", function() { 91 var params = GeoExt.tree.LayerParamsNode.prototype.updateParams.call(parent) || {}; 92 var delimiter = parent.attributes.paramsDelimiter; 93 var join; 94 for(var p in params) { 95 join = delimiter && delimiter[p]; 96 params[p] = join ? params[p].join(join) : params[p]; 97 } 98 99 parent.layer.mergeNewParams(params); 100 101 return params; 102 }, parent); 103 } 104 }; 105 106 /** 107 * NodeType: gx_layerparams 108 */ 109 Ext.tree.TreePanel.nodeTypes.gx_layerparams = GeoExt.tree.LayerParamsNode; -
lib/GeoExt/widgets/tree/BaseLayerContainer.js
old new 1 /** 2 * Copyright (c) 2008 The Open Planning Project 3 */ 4 Ext.namespace("GeoExt.tree"); 5 6 /** 7 * Class: GeoExt.tree.BaseLayerContainer 8 * 9 * A layer container that will collect all base layers of an OpenLayers map. 10 * Only layers that have displayInLayerSwitcher set to true will be included. 11 * 12 * To use this node type in JSON config, set nodeType to 13 * "olBaseLayerContainer". 14 * 15 * Inherits from: 16 * - <GeoExt.tree.LayerContainer> 17 */ 18 GeoExt.tree.BaseLayerContainer = Ext.extend(GeoExt.tree.LayerContainer, { 19 20 /** 21 * Constructor: GeoExt.tree.BaseLayerContainer 22 * 23 * Parameters: 24 * config - {Object} 25 */ 26 constructor: function(config) { 27 config.text = config.text || "Base Layer"; 28 GeoExt.tree.BaseLayerContainer.superclass.constructor.apply(this, arguments); 29 }, 30 31 /** 32 * Method: addLayerNode 33 * Adds a child node representing a base layer of the map 34 * 35 * Parameters: 36 * layerRecord - {Ext.data.Record} the layer record to add a node for 37 */ 38 addLayerNode: function(layerRecord) { 39 var layer = layerRecord.get("layer"); 40 if (layer.isBaseLayer == true) { 41 GeoExt.tree.BaseLayerContainer.superclass.addLayerNode.call(this, 42 layerRecord); 43 } 44 }, 45 46 /** 47 * Method: removeLayerNode 48 * Removes a child node representing a base layer of the map 49 * 50 * Parameters: 51 * layerRecord - {Ext.data.Record} the layer record to remove the node for 52 */ 53 removeLayerNode: function(layer) { 54 var layer = layerRecord.get("layer"); 55 if (layer.isBaseLayer == true) { 56 GeoExt.tree.BaseLayerContainer.superclass.removeLayerNode.call(this, 57 layerRecord); 58 } 59 } 60 }); 61 62 /** 63 * NodeType: gx_baselayercontainer 64 */ 65 Ext.tree.TreePanel.nodeTypes.gx_baselayercontainer = GeoExt.tree.BaseLayerContainer; -
lib/GeoExt/widgets/tree/TristateCheckboxNode.js
old new 1 /** 2 * Copyright (c) 2008 The Open Planning Project 3 */ 4 Ext.namespace("GeoExt.tree"); 5 6 /** 7 * Class: GeoExt.tree.TristateCheckboxNodeUI 8 * 9 * Inherits from: 10 * - <Ext.tree.TreeNodeUI> 11 */ 12 GeoExt.tree.TristateCheckboxNodeUI = Ext.extend(Ext.tree.TreeNodeUI, { 13 14 /** 15 * Constructor: GeoExt.tree.TristateCheckbosNodeUI 16 * 17 * Parameters: 18 * config - {Object} 19 */ 20 constructor: function(config) { 21 GeoExt.tree.TristateCheckboxNodeUI.superclass.constructor.apply(this, arguments); 22 }, 23 24 /** 25 * Method: toggleCheck 26 * 27 * Parameters: 28 * value - {Boolean} checked status 29 * thirdState - {Boolean} 30 * options - {Object} Hash of options for this method 31 * 32 * Currently supported options: 33 * silent - {Boolean} set to true if no checkchange event should be 34 * fired 35 */ 36 toggleCheck: function(value, thirdState, options) { 37 options = options || {} 38 var cb = this.checkbox; 39 if(thirdState == true) { 40 if(cb) { 41 Ext.get(cb).setOpacity(0.5); 42 } 43 this.node.attributes.thirdState = true; 44 } else { 45 if(cb) { 46 Ext.get(cb).clearOpacity(); 47 } 48 delete this.node.attributes.thirdState; 49 } 50 51 options.silent && this.node.suspendEvents(); 52 GeoExt.tree.TristateCheckboxNodeUI.superclass.toggleCheck.call(this, 53 value); 54 options.silent && this.node.resumeEvents(); 55 } 56 }); 57 58 /** 59 * Class: GeoExt.tree.TristateCheckboxNode 60 * 61 * Provides a tree node that will have a third state (stored in 62 * attributes.thirdState) to have a proper semi-checked state for 63 * nodes with only *some* children checked. attributes.thirdState will be 64 * undefined if the node is checked or unchecked, and true if the node is 65 * semi-checked. 66 * 67 * This node has also a childcheckchange event that will be triggered with a 68 * child node and it's checked state to notify listeners when a the checked 69 * state of a child's node has changed. 70 * 71 * Applications using this class should not rely on the checkchange event to 72 * determine the checked state of non-leaf nodes. Instead, applications should 73 * also listen to the childcheckchange event and read out attributes.checked 74 * and attributes.thirdState to get the node's checked state. 75 * 76 * To use this node type in a JSON config, set nodeType to "tristateCheckbox". 77 * 78 * Inherits from: 79 * - <Ext.tree.AsyncTreeNode> 80 */ 81 GeoExt.tree.TristateCheckboxNode = Ext.extend(Ext.tree.AsyncTreeNode, { 82 83 /** 84 * Property: checkedChildNodes 85 * {Object} Hash of 0.1 for thirdState nodes and 1 for fully checked 86 * nodes, keyed by node ids. In combination with <checkedCount>, 87 * this provides an efficient way of keeping track of the childnodes' 88 * checked state. 89 */ 90 checkedChildNodes: null, 91 92 /** 93 * Property: checkedCount 94 * {Number} A cache for the sum of checkedChildNodes' values. 95 */ 96 checkedCount: null, 97 98 /** 99 * Constructor: GeoExt.tree.TristateCheckboxNode 100 * 101 * Parameters: 102 * config - {Object} 103 */ 104 constructor: function(config) { 105 this.checkedChildNodes = {}; 106 this.checkedCount = 0; 107 108 this.defaultUI = this.defaultUI || GeoExt.tree.TristateCheckboxNodeUI; 109 this.addEvents.apply(this, GeoExt.tree.TristateCheckboxNode.EVENT_TYPES); 110 111 GeoExt.tree.TristateCheckboxNode.superclass.constructor.apply(this, arguments); 112 113 this.on("childcheckchange", this.updateCheckedChildNodes, this); 114 }, 115 116 /** 117 * Method: render 118 * 119 * Parameters: 120 * bulkRender - {Boolean} 121 */ 122 render: function(bulkRender) { 123 var rendered = this.rendered; 124 var checked = this.attributes.checked; 125 this.attributes.checked = 126 typeof this.attributes.checked == "undefined" ? false : 127 this.attributes.checked; 128 GeoExt.tree.TristateCheckboxNode.superclass.render.call(this, bulkRender); 129 var ui = this.getUI(); 130 if(!rendered) { 131 if(typeof checked == "undefined" && this.parentNode.ui.checkbox) { 132 ui.toggleCheck(this.parentNode.ui.checkbox.checked); 133 } 134 this.parentNode.on("checkchange", function(node, checked) { 135 ui.toggleCheck(checked); 136 }, this); 137 } 138 }, 139 140 /** 141 * Method: updateCheckedChildNodes 142 * Updates the status cache of checked child nodes. 143 * 144 * Parameters: 145 * node - {Ext.tree.Node} child node that has changed 146 * checked - {Boolean} new checked status of the changed child node 147 */ 148 updateCheckedChildNodes: function(node, checked) { 149 if(checked) { 150 this.addChecked(node, node.attributes.thirdState); 151 } else { 152 this.removeChecked(node); 153 } 154 155 var childrenChecked, childrenThirdState; 156 if(this.checkedCount.toFixed() == this.childNodes.length) { 157 childrenChecked = true; 158 childrenThirdState = false; 159 } else if(this.checkedCount.toFixed(1) == 0) { 160 childrenChecked = false; 161 childrenThirdState = false; 162 } else { 163 childrenChecked = true; 164 childrenThirdState = true; 165 } 166 // do a special silent toggleCheck to avoid checkchange events being 167 // triggered 168 this.getUI().toggleCheck(childrenChecked, childrenThirdState, 169 {silent: true}); 170 if(this.parentNode) { 171 this.parentNode.fireEvent("childcheckchange", this, 172 childrenChecked); 173 } 174 }, 175 176 /** 177 * Method: appendChild 178 * 179 * Parameters: 180 * node - {Ext.tree.Node} 181 */ 182 appendChild: function(node) { 183 GeoExt.tree.TristateCheckboxNode.superclass.appendChild.call(this, node); 184 if(this.attributes.checked || node.attributes.checked) { 185 this.addChecked(node); 186 } 187 // We do not want this event handler to trigger checkchange events on 188 // parent nodes, because this would cause bouncing between this 189 // handler and the handler for (un-)checking children on a parent's 190 // checkchange event. So we introduce a special childcheckchange 191 // event with a handler that will also trigger this event on the 192 // parent. 193 node.on("checkchange", function(node, checked) { 194 if(this.childrenRendered) { 195 this.fireEvent("childcheckchange", node, checked); 196 } 197 }, this); 198 }, 199 200 /** 201 * Method: addChecked 202 * Adds a child node to the checkedChildNodes hash. Adds 1 for fully 203 * checked nodes, 0.1 for third state checked nodes. 204 * 205 * Parameters: 206 * node - {Ext.tree.Node} 207 * thirdState - {Boolean} 208 */ 209 addChecked: function(node, thirdState) { 210 // subtract current value (if any). This is needed to change from a 211 // tristate to a fully checked state and vice versa. 212 this.checkedCount -= (this.checkedChildNodes[node.id] || 0); 213 214 var add = thirdState ? 0.1 : 1; 215 this.checkedChildNodes[node.id] = add; 216 this.checkedCount += add; 217 }, 218 219 /** 220 * Method: removeChecked 221 * Removes a child node from the checkedChildNodes hash. 222 * 223 * Parameters: 224 * node - {Ext.tree.Node} 225 */ 226 removeChecked: function(node) { 227 var remove = this.checkedChildNodes[node.id] 228 if(remove) { 229 delete this.checkedChildNodes[node.id]; 230 this.checkedCount -= remove; 231 } 232 } 233 }); 234 235 /** 236 * Constant: EVENT_TYPES 237 * {Array(String)} - supported event types 238 * 239 * Event types supported for this class, in additon to the ones inherited 240 * from {<Ext.tree.AsyncTreeNode>}: 241 * - *childcheckchange* fired to notify a parent node that the status of 242 * its checked child nodes has changed 243 */ 244 GeoExt.tree.TristateCheckboxNode.EVENT_TYPES = ["childcheckchange"]; 245 246 /** 247 * NodeType: gx_tristatecheckbox 248 */ 249 Ext.tree.TreePanel.nodeTypes.gx_tristatecheckbox = GeoExt.tree.TristateCheckboxNode; -
lib/GeoExt/widgets/tree/LayerContainer.js
old new 1 /** 2 * Copyright (c) 2008 The Open Planning Project 3 */ 4 Ext.namespace("GeoExt.tree"); 5 6 /** 7 * Class: GeoExt.tree.LayerContainer 8 * 9 * A subclass of {Ext.tree.TreeNode} that will collect all layers of an 10 * OpenLayers map. Only layers that have displayInLayerSwitcher set to true 11 * will be included. The childrens' iconCls will be set to "baselayer-icon" 12 * for base layers, and to "layer-icon" for overlay layers. 13 * 14 * To use this node type in JSON config, set nodeType to "olLayerContainer". 15 * 16 * Inherits from: 17 * - <Ext.tree.TreeNode> 18 */ 19 GeoExt.tree.LayerContainer = Ext.extend(Ext.tree.TreeNode, { 20 21 /** 22 * ConfigProperty: layerStore 23 * {<GeoExt.data.LayerStore>} The layer store containing layers to be 24 * displayed in the container. 25 */ 26 layerStore: null, 27 28 /** 29 * ConfigProperty: defaults 30 * {Object} a configuration object passed to all nodes that this 31 * LayerContainer creates. 32 */ 33 defaults: null, 34 35 /** 36 * Constructor: GeoExt.tree.LayerContainer 37 * 38 * Parameters: 39 * config - {Object} 40 */ 41 constructor: function(config) { 42 this.layerStore = config.layerStore; 43 this.defaults = config.defaults; 44 GeoExt.tree.LayerContainer.superclass.constructor.apply(this, arguments); 45 }, 46 47 /** 48 * Method: render 49 * 50 * Parameters: 51 * bulkRender - {Boolean} 52 */ 53 render: function(bulkRender) { 54 if (!this.rendered) { 55 if(!this.layerStore) { 56 this.layerStore = GeoExt.MapPanel.guess().layers; 57 } 58 this.layerStore.each(function(record) { 59 this.addLayerNode(record); 60 }, this); 61 this.layerStore.on({ 62 "add": function(store, records, index) { 63 var nodeIndex = this.recordIndexToNodeIndex(index); 64 console.log(index, nodeIndex); 65 for(var i=0; i<records.length; ++i) { 66 this.addLayerNode(records[i], nodeIndex); 67 } 68 }, 69 "remove": function(store, record, index) { 70 this.removeLayerNode(record); 71 }, 72 scope: this 73 }); 74 } 75 GeoExt.tree.LayerContainer.superclass.render.call(this, bulkRender); 76 }, 77 78 /** 79 * Method: recordIndexToNodeIndex 80 * Convert a record index into a child node index. 81 * 82 * Parameters: 83 * index - {Number} The record index in the layer store. 84 * 85 * Returns: 86 * {Number} The appropriate child node index for the record. 87 */ 88 recordIndexToNodeIndex: function(index) { 89 var store = this.layerStore; 90 var count = store.getCount(); 91 var nodeIndex = -1; 92 for(var i=count-1; i>=0; --i) { 93 if(store.getAt(i).get("layer").displayInLayerSwitcher) { 94 ++nodeIndex; 95 if(index === i) { 96 break; 97 } 98 } 99 }; 100 return nodeIndex; 101 }, 102 103 /** 104 * Method: addLayerNode 105 * Adds a child node representing a layer of the map 106 * 107 * Parameters: 108 * layerRecord - {Ext.data.Record} the layer record to add the layer for 109 * index - {Number} Optional index for the new layer. Default is 0. 110 */ 111 addLayerNode: function(layerRecord, index) { 112 index = index || 0; 113 var layer = layerRecord.get("layer"); 114 if (layer.displayInLayerSwitcher == true) { 115 var node = new GeoExt.tree.LayerNode(Ext.applyIf({ 116 iconCls: layer.isBayeLayer ? 'baselayer-icon' : 'layer-icon', 117 layer: layer, 118 layerStore: this.layerStore 119 }, this.defaults)); 120 var sibling = this.item(index); 121 if(sibling) { 122 this.insertBefore(node, sibling); 123 } else { 124 this.appendChild(node); 125 } 126 } 127 }, 128 129 /** 130 * Method: removeLayerNode 131 * Removes a child node representing a layer of the map 132 * 133 * Parameters: 134 * layerRecord - {Ext.data.Record} the layer record to remove the node for 135 */ 136 removeLayerNode: function(layerRecord) { 137 var layer = layerRecord.get("layer"); 138 if (layer.displayInLayerSwitcher == true) { 139 var node = this.findChildBy(function(node) { 140 return node.layer == layer; 141 }); 142 if(node) { 143 node.remove(); 144 } 145 } 146 } 147 }); 148 149 /** 150 * NodeType: gx_layercontainer 151 */ 152 Ext.tree.TreePanel.nodeTypes.gx_layercontainer = GeoExt.tree.LayerContainer; -
lib/GeoExt/widgets/tree/OverlayLayerContainer.js
old new 1 /** 2 * Copyright (c) 2008 The Open Planning Project 3 */ 4 Ext.namespace("GeoExt.tree"); 5 6 /** 7 * Class: GeoExt.tree.OverlayLayerContainer 8 * 9 * A layer container that will collect all overlay layers of an OpenLayers map. 10 * Only layers that have displayInLayerSwitcher set to true will be included. 11 * 12 * To use this node type in JSON config, set nodeType to 13 * "olOverlayLayerContainer". 14 * 15 * Inherits from: 16 * - <GeoExt.tree.LayerContainer> 17 */ 18 GeoExt.tree.OverlayLayerContainer = Ext.extend(GeoExt.tree.LayerContainer, { 19 20 /** 21 * Constructor: GeoExt.tree.OverlayLayerContainer 22 * 23 * Parameters: 24 * config - {Object} 25 */ 26 constructor: function(config) { 27 config.text = config.text || "Overlays"; 28 GeoExt.tree.OverlayLayerContainer.superclass.constructor.apply(this, 29 arguments); 30 }, 31 32 /** 33 * Method: addLayerNode 34 * Adds a child node representing a overlay layer of the map 35 * 36 * Parameters: 37 * layerRecord - {Ext.data.Record} the layer record to add a node for 38 */ 39 addLayerNode: function(layerRecord) { 40 var layer = layerRecord.get("layer"); 41 if (layer.isBaseLayer == false) { 42 GeoExt.tree.OverlayLayerContainer.superclass.addLayerNode.call(this, 43 layerRecord); 44 } 45 }, 46 47 /** 48 * Method: removeLayerNode 49 * Removes a child node representing an overlay layer of the map 50 * 51 * Parameters: 52 * layerRecord - {Ext.data.Record} the layer record to remove the node for 53 */ 54 removeLayerNode: function(layerRecord) { 55 var layer = layerRecord.get("layer"); 56 if (layer.isBaseLayer == false) { 57 GeoExt.tree.OverlayLayerContainer.superclass.removeLayerNode.call( 58 this, layerRecord); 59 } 60 } 61 }); 62 63 /** 64 * NodeType: gx_overlaylayercontainer 65 */ 66 Ext.tree.TreePanel.nodeTypes.gx_overlaylayercontainer = GeoExt.tree.OverlayLayerContainer; -
lib/GeoExt/widgets/tree/LayerNode.js
old new 1 /** 2 * Copyright (c) 2008 The Open Planning Project 3 */ 4 Ext.namespace("GeoExt.tree"); 5 6 /** 7 * Class: GeoExt.tree.LayerNodeUI 8 * 9 * Inherits from: 10 * - <GeoExt.tree.TristateCheckboxNodeUI> 11 */ 12 GeoExt.tree.LayerNodeUI = Ext.extend(GeoExt.tree.TristateCheckboxNodeUI, { 13 14 /** 15 * Property: radio 16 * {Ext.Element} 17 */ 18 radio: null, 19 20 /** 21 * Constructor: GeoExt.tree.LayerNodeUI 22 * 23 * Parameters: 24 * config - {Object} 25 */ 26 constructor: function(config) { 27 GeoExt.tree.LayerNodeUI.superclass.constructor.apply(this, arguments); 28 }, 29 30 /** 31 * Method: render 32 * 33 * Parameters: 34 * bulkRender - {Boolean} 35 */ 36 render: function(bulkRender) { 37 GeoExt.tree.LayerNodeUI.superclass.render.call(this, bulkRender); 38 var a = this.node.attributes; 39 if (a.radioGroup && !this.radio) { 40 this.radio = Ext.DomHelper.insertAfter(this.checkbox, 41 ['<input type="radio" class="x-tree-node-cb" name="', 42 a.radioGroup, '_querylayer"></input>'].join("")); 43 } 44 }, 45 46 /** 47 * Method: onClick 48 * 49 * Parameters: 50 * e - {Object} 51 */ 52 onClick: function(e) { 53 if (e.getTarget('input[type=radio]', 1)) { 54 this.fireEvent("querylayerchange", this.node.layer); 55 } else { 56 GeoExt.tree.LayerNodeUI.superclass.onClick.call(this, e); 57 } 58 }, 59 60 /** 61 * Method: toggleCheck 62 * 63 * Parameters: 64 * value - {Boolean} 65 */ 66 toggleCheck: function(value) { 67 GeoExt.tree.LayerNodeUI.superclass.toggleCheck.apply(this, arguments); 68 var node = this.node; 69 var layer = this.node.layer; 70 node.visibilityChanging = true; 71 if(this.checkbox && (layer.getVisibility() != this.isChecked())) { 72 layer.setVisibility(this.isChecked()); 73 } 74 node.visibilityChanging = false; 75 }, 76 77 /** 78 * Method: destroy 79 */ 80 destroy: function() { 81 GeoExt.tree.LayerNodeUI.superclass.destroy.call(this); 82 delete this.radio; 83 } 84 }); 85 86 87 /** 88 * Class: GeoExt.tree.LayerNode 89 * 90 * A subclass of {<GeoExt.tree.TristateCheckboxNode>} that is connected to an 91 * {OpenLayers.Layer} by setting the node's layer property. Checking or 92 * unchecking the checkbox of this node will directly affect the layer and 93 * vice versa. The default iconCls for this node's icon is "layer-icon", 94 * unless it has children. 95 * 96 * This node can contain children, e.g. filter nodes. 97 * 98 * Setting the node's layer property to a layer name instead of an object 99 * will also work. As soon as a layer is found, it will be stored as layer 100 * property in the attributes hash. 101 * 102 * The node's text property defaults to the layer name. 103 * 104 * If the layer has a queryable property set to true, the node will render a 105 * radio button to select the query layer. Clicking on the radio button will 106 * fire the querychange event, with the layer as argument. A queryGroup 107 * attribute, set to the map's id, will be added to the attributes hash. 108 * 109 * To use this node type in a JSON config, set nodeType to "olLayer". 110 * 111 * Inherits from: 112 * - <GeoExt.tree.TristateCheckboxNode> 113 */ 114 GeoExt.tree.LayerNode = Ext.extend(GeoExt.tree.TristateCheckboxNode, { 115 116 /** 117 * ConfigProperty: layer 118 * {OpenLayers.Layer|String} The layer that this layer node will 119 * be bound to, or the name of the layer (has to match the layer's 120 * name property). If a layer name is provided, <layerStore> also has 121 * to be provided. 122 */ 123 layer: null, 124 125 /** 126 * ConfigProperty: layerStore 127 * {<GeoExt.data.LayerStore|"auto"} The layer store containing the layer 128 * that this node represents. If set to "auto", the node will query 129 * the ComponentManager for a <GeoExt.MapPanel>, take the first one it 130 * finds and takes its layer store. This property is only required 131 * if <layer> is provided as a string. 132 */ 133 layerStore: null, 134 135 /** 136 * ConfigProperty: childNodeType 137 * {Ext.tree.Node|String} node class or nodeType of childnodes for this 138 * node. A node type provided here needs to have an add method, with 139 * a scope argument. This method will be run by this node in the 140 * context of this node, to create child nodes. See 141 * {<GeoExt.tree.LayerParamsNode>} for an example implementation that 142 * adds nodes based on a layer's params object. 143 */ 144 childNodeType: null, 145 146 /** 147 * Property: visibilityChanging 148 * {Boolean} private property indicating layer visibility being changed 149 * by this node in order to prevent visibilitychanged events bouncing 150 * back and forth 151 */ 152 visibilityChanging: false, 153 154 /** 155 * Constructor: GeoExt.tree.LayerNode 156 * 157 * Parameters: 158 * config - {Object} 159 */ 160 constructor: function(config) { 161 config.leaf = config.leaf || !config.children; 162 163 config.iconCls = typeof config.iconCls == "undefined" && 164 !config.children ? "layer-icon" : config.iconCls; 165 // checked status will be set by layer event, so setting it to false 166 // to always get the checkbox rendered 167 config.checked = false; 168 169 this.defaultUI = this.defaultUI || GeoExt.tree.LayerNodeUI; 170 this.addEvents.apply(this, GeoExt.tree.LayerNode.EVENT_TYPES); 171 172 Ext.apply(this, { 173 layer: config.layer, 174 layerStore: config.layerStore, 175 childNodeType: config.childNodeType 176 }); 177 GeoExt.tree.LayerNode.superclass.constructor.apply(this, arguments); 178 }, 179 180 /** 181 * Method: render 182 * 183 * Properties: 184 * bulkRender {Boolean} - optional 185 * layer {<OpenLayers.Layer>} - optional 186 */ 187 render: function(bulkRender) { 188 var layer = this.layer instanceof OpenLayers.Layer && this.layer; 189 if(!layer) { 190 // guess the store if not provided 191 if(!this.layerStore || this.layerStore == "auto") { 192 this.layerStore = GeoExt.MapPanel.guess().layers; 193 } 194 // now we try to find the layer by its name in the layer store 195 var i = this.layerStore.findBy(function(o) { 196 return o.get("title") == this.layer; 197 }, this); 198 if(i != -1) { 199 // if we found the layer, we can assign it and everything 200 // will be fine 201 layer = this.layerStore.getAt(i).get("layer"); 202 } 203 } 204 if (!this.rendered || !layer) { 205 var ui = this.getUI(); 206 207 if(layer) { 208 this.layer = layer; 209 if(!this.text) { 210 this.text = layer.name; 211 } 212 213 if(this.childNodeType) { 214 this.addChildNodes(); 215 } 216 217 ui.show(); 218 ui.toggleCheck(layer.getVisibility()); 219 this.addVisibilityEventHandlers(); 220 // set initial checked status 221 this.attributes.checked = layer.getVisibility(); 222 } else { 223 ui.hide(); 224 } 225 226 if(this.layerStore instanceof GeoExt.data.LayerStore) { 227 this.addStoreEventHandlers(layer); 228 } 229 } 230 GeoExt.tree.LayerNode.superclass.render.call(this, bulkRender); 231 }, 232 233 /** 234 * Method: addVisibilityHandlers 235 * Adds handlers that sync the checkbox state with the layer's visibility 236 * state 237 */ 238 addVisibilityEventHandlers: function() { 239 this.layer.events.register("visibilitychanged", this, function() { 240 if(!this.visibilityChanging && 241 this.attributes.checked != this.layer.getVisibility()) { 242 this.getUI().toggleCheck(this.layer.getVisibility()); 243 } 244 }); 245 this.on({ 246 "checkchange": function(node, checked) { 247 if (checked && this.layer.isBaseLayer) { 248 this.layer.map.setBaseLayer(this.layer); 249 } 250 this.layer.setVisibility(checked); 251 }, 252 scope: this 253 }); 254 }, 255 256 /** 257 * Method: addStoreEventHandlers 258 * Adds handlers that make sure the node disappeares when the layer is 259 * removed from the store, and appears when it is re-added. 260 */ 261 addStoreEventHandlers: function() { 262 this.layerStore.on({ 263 "add": function(store, records, index) { 264 var l; 265 for(var i=0; i<records.length; ++i) { 266 l = records[i].get("layer"); 267 if(this.layer == l) { 268 this.getUI().show(); 269 } else if (this.layer == l.name) { 270 // layer is a string, which means the node has not yet 271 // been rendered because the layer was not found. But 272 // now we have the layer and can render. 273 this.render(bulkRender); 274 return; 275 } 276 } 277 }, 278 "remove": function(store, record, index) { 279 if(this.layer == record.get("layer")) { 280 this.getUI().hide(); 281 } 282 }, 283 scope: this 284 }); 285 }, 286 287 /** 288 * Method: addChildNodes 289 * If the layer's layers param is an array of layer names, a subnode for 290 * each name will be created. 291 */ 292 addChildNodes: function() { 293 if(typeof this.childNodeType == "string") { 294 Ext.tree.TreePanel.nodeTypes[this.childNodeType].add(this); 295 } else if(this.childNodeType.add) { 296 this.childNodeType.add(this); 297 } 298 }, 299 300 /** 301 * Method: updateCheckedChildNodes 302 * 303 * Parameters: 304 * node - {Ext.tree.node} 305 * checked - {Boolean} 306 */ 307 updateCheckedChildNodes: function(node, checked) { 308 GeoExt.tree.LayerNode.superclass.updateCheckedChildNodes.call(this, 309 node, checked); 310 } 311 }); 312 313 /** 314 * Constant: GeoExt.tree.LayerNode.EVENT_TYPES 315 * {Array(String)} - supported event types 316 * 317 * Event types supported for this class, in additon to the ones inherited 318 * from {<GeoExt.tree.TristateCheckboxNode>}: 319 * - *querylayerchange* notifies listener when the query layer has 320 * changed. Will be called with the new query layer as argument. 321 */ 322 GeoExt.tree.LayerNode.EVENT_TYPES = ["querylayerchange"]; 323 324 /** 325 * NodeType: gx_layer 326 */ 327 Ext.tree.TreePanel.nodeTypes.gx_layer = GeoExt.tree.LayerNode; -
lib/GeoExt/widgets/MapPanel.js
old new 193 193 }); 194 194 195 195 /** 196 * APIFunction GeoExt.MapPanel.guess 197 * Convenience function for guessing the map panel of an application. This can 198 * reliably be used for all applications that just have one map panel in the 199 * viewport. 200 * 201 * Returns: 202 * {<GeoExt.MapPanel>} The first map panel found by the Ext component manager. 203 */ 204 GeoExt.MapPanel.guess = function() { 205 return Ext.ComponentMgr.all.find(function(o) { 206 return o instanceof GeoExt.MapPanel; 207 }); 208 } 209 210 211 /** 196 212 * XType: gx_mappanel 197 213 */ 198 214 Ext.reg('gx_mappanel', GeoExt.MapPanel); -
examples/tree.html
old new 1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head> 3 <link rel="stylesheet" type="text/css" href="../../../ext/2.2/resources/css/ext-all.css"></link> 4 <script type="text/javascript" src="../../../openlayers/lib/OpenLayers.js"></script> 5 <script type="text/javascript" src="../../../ext/2.2/adapter/ext/ext-base.js"></script> 6 <script type="text/javascript" src="../../../ext/2.2/ext-all-debug.js"></script> 7 <script type="text/javascript" src="../lib/GeoExt.js"></script> 8 <script type="text/javascript" src="../lib/GeoExt/widgets/tree/TristateCheckboxNode.js"></script> 9 <script type="text/javascript" src="../lib/GeoExt/widgets/tree/LayerNode.js"></script> 10 <script type="text/javascript" src="../lib/GeoExt/widgets/tree/LayerParamsNode.js"></script> 11 <script type="text/javascript" src="../lib/GeoExt/widgets/tree/LayerContainer.js"></script> 12 <script type="text/javascript" src="../lib/GeoExt/widgets/tree/BaseLayerContainer.js"></script> 13 <script type="text/javascript" src="../lib/GeoExt/widgets/tree/OverlayLayerContainer.js"></script> 14 <script> 15 var mapPanel; 16 Ext.onReady(function() { 17 var treeConfig = new OpenLayers.Format.JSON().write([{ 18 nodeType: "gx_baselayercontainer" 19 }, { 20 nodeType: "gx_overlaylayercontainer", 21 defaults: { 22 childNodeType: "gx_layerparams", 23 param: "LAYERS", 24 texts: ["Water", "Cities"] 25 } 26 }, { 27 nodeType: "gx_layer", 28 layer: "Tasmania Roads", 29 childNodeType: "gx_layerparams", 30 paramsDelimiter: { 31 "CQL_FILTER": " or " 32 }, 33 children: [{ 34 nodeType: "gx_layerparams", 35 text: "Highway", 36 params: {CQL_FILTER: "TYPE='highway'"} 37 }, { 38 nodeType: "gx_layerparams", 39 text: "Lane", 40 params: {CQL_FILTER: "TYPE='lane'"} 41 }, { 42 nodeType: "gx_layerparams", 43 text: "Road", 44 params: {CQL_FILTER: "TYPE='road'"} 45 }, { 46 nodeType: "gx_layerparams", 47 text: "Alley", 48 params: {CQL_FILTER: "TYPE='alley'"} 49 }, { 50 nodeType: "gx_layerparams", 51 text: "Gravel", 52 params: {CQL_FILTER: "TYPE='gravel'"} 53 }, { 54 nodeType: "gx_layerparams", 55 text: "Logging", 56 params: {CQL_FILTER: "TYPE='logging'"} 57 }] 58 }], true); 59 60 mapPanel = new GeoExt.MapPanel({ 61 border: true, 62 region: "center", 63 map: new OpenLayers.Map({ 64 maxExtent: new OpenLayers.Bounds( 65 143.60260815000004, -43.851764249999995, 66 148.71135685000002, -39.370182750000005 67 ), 68 maxResolution: 0.0199560496093749, 69 }), 70 }); 71 72 mapPanel.map.addLayers([ 73 new OpenLayers.Layer.WMS("Tasmania State Boundaries", 74 "http://sigma.openplans.org/geoserver/wms", { 75 layers: "topp:tasmania_state_boundaries" 76 }, { 77 buffer: 0 78 }), 79 new OpenLayers.Layer.WMS("Blue Marble", 80 "http://sigma.openplans.org/geoserver/wms", { 81 layers: "bluemarble" 82 }, { 83 buffer: 0, 84 displayOutsideMaxExtent: true 85 }), 86 new OpenLayers.Layer.WMS("Water, Cities", 87 "http://sigma.openplans.org/geoserver/wms", { 88 layers: ["topp:tasmania_water_bodies","topp:tasmania_cities"], 89 transparent: "true", 90 format: "image/gif" 91 }, { 92 queryable: true, 93 isBaseLayer: false, 94 buffer: 0 95 }), 96 new OpenLayers.Layer.WMS("Tasmania Roads", 97 "http://sigma.openplans.org/geoserver/wms", { 98 layers: "topp:tasmania_roads", 99 transparent: "true", 100 format: "image/gif", 101 cql_filter: "TYPE='highway' or TYPE='lane' or TYPE='road' or TYPE='alley' or TYPE='gravel' or TYPE='logging'" 102 }, { 103 queryable: true, 104 isBaseLayer: false, 105 displayInLayerSwitcher: false, 106 singleTile: true, 107 ratio: 1 108 }) 109 ]); 110 111 var treeConfigWin = new Ext.Window({ 112 layout: "fit", 113 hideBorders: true, 114 closeAction: "hide", 115 width: 300, 116 height: 400, 117 title: "Tree Configuration", 118 items: [{ 119 xtype: "form", 120 layout: "fit", 121 items: [{ 122 id: "treeconfig", 123 xtype: "textarea" 124 }], 125 buttons: [{ 126 text: "Save", 127 handler: function() { 128 var value = Ext.getCmp("treeconfig").getValue() 129 try { 130 var root = tree.getRootNode(); 131 root.attributes.children = Ext.decode(value); 132 tree.getLoader().load(root); 133 } catch(e) { 134 alert("Invalid JSON"); 135 return; 136 } 137 treeConfig = value; 138 treeConfigWin.hide(); 139 } 140 }, { 141 text: "Cancel", 142 handler: function() { 143 treeConfigWin.hide(); 144 } 145 }, { 146 text: "Help", 147 tooltipType: "title", 148 tooltip: "Opens a window with a list of nodeTypes provided by GeoExt", 149 handler: function() { 150 window.open("../doc/index/NodeTypes.html"); 151 } 152 }] 153 }] 154 }); 155 156 var toolbar = new Ext.Toolbar({ 157 items: [{ 158 text: "Show/Edit Tree Config", 159 handler: function() { 160 treeConfigWin.show(); 161 Ext.getCmp("treeconfig").setValue(treeConfig); 162 } 163 }] 164 }); 165 166 var tree = new Ext.tree.TreePanel({ 167 border: true, 168 region: "west", 169 title: "Layers and Filters", 170 width: 200, 171 split: true, 172 collapsible: true, 173 collapseMode: "mini", 174 autoScroll: true, 175 loader: new Ext.tree.TreeLoader({ 176 clearOnLoad: true 177 }), 178 root: { 179 nodeType: "async", 180 children: Ext.decode(treeConfig) 181 }, 182 rootVisible: false, 183 lines: false, 184 bbar: toolbar 185 }); 186 187 tree.on("append", function(tree, parent, node) { 188 node.on("querylayerchange", function(layer) { 189 alert(layer.name+" is now the query layer."); 190 }) 191 }); 192 193 new Ext.Viewport({ 194 layout: "fit", 195 hideBorders: true, 196 items: { 197 layout: "border", 198 deferredRender: false, 199 items: [mapPanel, tree] 200 } 201 }); 202 }); 203 </script> 204 </head> 205 <body> 206 </body> 207 </html>