1 /**
  2 * @fileOverview  
  3 * Copyright (c) 2013 Regione Autonoma della Sardegna
  4 * Published under the GPL license.<br>
  5 * See <a href="https://sardegnageoportale.it/webgis/license.txt">https://sardegnageoportale.it/webgis/license.txt</a>
  6 * for the full text of the license.
  7 * @version 0.1
  8 */
  9 
 10 /**
 11  * @require plugins/LayerTree.js
 12  * @require GeoExt/plugins/TreeNodeComponent.js
 13  * @require GeoExt/widgets/WMSLegend.js
 14  * @require GeoExt/widgets/VectorLegend.js
 15  */
 16 
 17 /**
 18  * @namespace framework.plugins
 19  */
 20 Ext.namespace("framework.plugins");
 21 
 22 /** @example
 23  *  If you want to change the vendor-specific legend_options parameter that 
 24  *  is sent to the WMS for GetLegendGraphic you can use ``baseAttrs`` on the
 25  *  ``loader`` config:
 26  *
 27  *  .. code-block:: javascript
 28  *
 29  *    var layerManager = new gxp.plugins.LayerManager({
 30  *        loader: {
 31  *            baseAttrs: {
 32  *                baseParams: {
 33  *                    legend_options: "fontAntiAliasing:true;fontSize:11;fontName:Arial;fontColor:#FFFFFF"
 34  *                }
 35  *            }
 36  *        }
 37  *    });
 38  *
 39  */
 40 
 41 /** 
 42  *  Plugin for adding a tree of layers with their legend to a 'gxp.Viewer'. 
 43  *  Also provides a context menu on layer nodes.
 44  *  @name_ LayerManager
 45  *  @class Plugin for adding a tree of layers with their legend
 46  *  @constructor
 47  *  @extends <a target="_blank" href="http://gxp.opengeo.org/master/doc/lib/plugins/LayerTree.html">gxp.plugins.LayerTree</a>
 48  */
 49 framework.plugins.LayerManager = Ext.extend(gxp.plugins.LayerTree, 
 50 /** 
 51 * @lends framework.plugins.LayerManager.prototype 
 52 */
 53 {
 54     /**
 55     * gxp_layermanager plugin type.
 56     * @public
 57     * @type String
 58     */
 59     ptype: "framework_layermanager",
 60 
 61     /** 
 62      * Text for baselayer node of layer tree (i18n).
 63      * @public
 64      * @type String
 65      */    
 66     baseNodeText: "Base Maps",
 67     
 68     /** 
 69      *  'Object' The groups to show in the layer tree. Keys are group names,
 70      *  and values are either group titles or an object with 'title' and
 71      *  ``exclusive`` properties. 'exclusive' means that nodes will have
 72      *  radio buttons instead of checkboxes, so only one layer of the group can
 73      *  be active at a time. Optional, the default is
 74      *
 75      *  .. code-block:: javascript
 76      *
 77      *      groups: {
 78      *          "default": "Overlays", // title can be overridden with overlayNodeText
 79      *          "background": {
 80      *              title: "Base Maps", // can be overridden with baseNodeText
 81      *              exclusive: true
 82      *          }
 83      *      }
 84      */
 85     
 86     /** 
 87      * output configuration
 88      * @private
 89     */ 
 90     createOutputConfig: function() {
 91         this.tree = this.createPreOutputConfig(this, arguments);
 92         var self = this;
 93         this.target.layer_group = this.groups;
 94         self.target.addEvents('nodeselectionchange');
 95         self.tree.selModel.on({
 96             selectionchange: function(obj, node, last) {
 97                     self.target.fireEvent('nodeselectionchange',obj.tree.root, node, last);
 98                 }
 99         });
100         Ext.applyIf(this.tree, Ext.apply({
101             cls: "gxp-layermanager-tree",
102             lines: false,
103             useArrows: false,
104             plugins: [{
105                 ptype: "gx_treenodecomponent"
106             }]
107         }, this.treeConfig));
108         return this.tree;        
109     },
110     
111     /** 
112      * layer node configuration
113      * @private
114     */ 
115     configureLayerNode: function(loader, attr) {
116         framework.plugins.LayerManager.superclass.configureLayerNode.apply(this, arguments);
117         var legendXType;
118         // add a WMS legend to each node created
119         if (OpenLayers.Layer.WMS && attr.layer instanceof OpenLayers.Layer.WMS) {
120             legendXType = "gx_wmslegend";
121         } else if (OpenLayers.Layer.Vector && attr.layer instanceof OpenLayers.Layer.Vector) {
122             legendXType = "gx_vectorlegend";
123         }
124         if (legendXType) {
125             var baseParams;
126             var clsParam; //hide legend in layers inactive on default
127             if (loader && loader.baseAttrs && loader.baseAttrs.baseParams) {
128                 baseParams = loader.baseAttrs.baseParams;
129             }
130 			
131 			//hide legend in layers inactive on default
132             if (this.target.mapPanel.layers.getByLayer(attr.layer).data.layer.options.visibility && 
133             this.target.mapPanel.layers.getByLayer(attr.layer).data.layer.minScale >= this.target.mapPanel.layers.getByLayer(attr.layer).data.layer.scales[this.target.mapPanel.zoom])
134             	clsParam = "legend";
135             else
136             	clsParam = "legend x-hide-display";
137             
138             Ext.apply(attr, {
139                 component: {
140                     xtype: legendXType,
141                     // TODO these baseParams were only tested with GeoServer,
142                     // so maybe they should be configurable - and they are
143                     // only relevant for gx_wmslegend.
144                     hidden: !attr.layer.getVisibility(),
145                     baseParams: Ext.apply({
146                         transparent: true
147                       //  format: "image/png", //da disattivare per ArcGIS online... forzato per i layer gruppi di GeoServer
148                       //  legend_options: "fontAntiAliasing:true;fontSize:11;fontName:Arial;forceLabels:on;"
149                     }, baseParams),
150                     layerRecord: this.target.mapPanel.layers.getByLayer(attr.layer),
151                     showTitle: false,
152                     // custom class for css positioning
153                     // see tree-legend.html
154                     cls: clsParam
155                 }
156             });
157      
158         }
159     },
160             
161      /** 
162      * @private
163      * @returns: 'Object' Configuration object for an Ext.tree.TreePanel
164      */
165     createPreOutputConfig: function() {
166         var treeRoot = new Ext.tree.TreeNode({
167             text: this.rootNodeText,
168             expanded: true,
169             isTarget: false,
170             allowDrop: true
171         });
172 
173         var baseAttrs;
174         if (this.initialConfig.loader && this.initialConfig.loader.baseAttrs) {
175             baseAttrs = this.initialConfig.loader.baseAttrs;
176         }
177         
178         var defaultGroup = this.defaultGroup,
179             plugin = this,
180             groupConfig,
181             exclusive;
182         for (var group in this.groups) {
183             groupConfig = typeof this.groups[group] == "string" ?
184                 {title: this.groups[group]} : this.groups[group];
185             exclusive = groupConfig.exclusive;
186             var newNode = this.createNode(plugin,group,groupConfig,defaultGroup,exclusive,baseAttrs);
187             newNode.addVisibilityEventHandlers();
188             treeRoot.appendChild(newNode);
189         }
190         
191         return {
192             xtype: "treepanel",
193             root: treeRoot,
194             rootVisible: false,
195             shortTitle: this.shortTitle,
196             border: false,
197             enableDD: true,
198             selModel: new Ext.tree.DefaultSelectionModel({
199                 listeners: {
200                     beforeselect: this.handleBeforeSelect,
201                     scope: this
202                 }
203             }),
204             listeners: {
205                 contextmenu: this.handleTreeContextMenu,
206                 beforemovenode: this.handleBeforeMoveNode,                
207                 scope: this
208             },
209             contextMenu: new Ext.menu.Menu({
210                 items: []
211             })
212         };
213     },
214     
215     /** create group node
216     * @public
217     * @param {plugin} 'framework.plugins.LayerManager' object self
218     * @param {group} 'String' group name
219     * @param {groupConfig} 'object' group object
220     * @param {defaultGroup} 'String' default group name
221     * @param {exclusive} 'boolean' exclusive content (one layer on for time)
222     * @param {baseAttrs} 'object' configuration attrubutes
223     * @returns {framework.plugins.RASGroupNode} group node
224     */
225     createNode: function(plugin,group,groupConfig,defaultGroup,exclusive,baseAttrs){
226         return new framework.plugins.RASGroupNode(Ext.apply({
227                 text: groupConfig.title,
228                 iconCls: "gxp-folder",
229                 expanded: true,
230                 groupKey: group,
231                 group: group == defaultGroup ? undefined : group,
232                 loader: new GeoExt.tree.LayerLoader({
233                     baseAttrs: exclusive ?
234                         Ext.apply({checkedGroup: Ext.isString(exclusive) ? exclusive : group}, baseAttrs) :
235                         baseAttrs,
236                     store: this.target.mapPanel.layers,
237                     filter: (function(group) {
238                         return function(record) {
239                             return (record.get("group") || defaultGroup) == group &&
240                                 record.getLayer().displayInLayerSwitcher == true;
241                         };
242                     })(group),
243                     createNode: function(attr) {
244                         plugin.configureLayerNode(this, attr);
245                         return GeoExt.tree.LayerLoader.prototype.createNode.apply(this, arguments);
246                     }
247                 }),
248                 singleClickExpand: true,
249                 allowDrag: true,
250                 listeners: {
251                     append: function(tree, node) {
252                         node.expand();
253                     },
254                     move: function( tree, obj, oldParent, newParent, newIndex ){
255                        var groups = self.group_layer;
256                        var value,key;
257                        //test
258 //                        var keysInit = [];
259 //                        for(var k in groups){
260 //                            keysInit.push(k); 
261 //                        }                    
262                        
263                        for(var child=0;child<oldParent.childNodes.length;child++){
264                            key = oldParent.childNodes[child].attributes.groupKey;
265                            value = groups[key];
266                            delete groups[key];
267                            groups[key] = value;
268                        }                       
269                        //test
270 //                        var keys = [];
271 //                        for(var k in groups){
272 //                            keys.push(k); 
273 //                        }
274                         
275                         /** Order layer on map by tree position
276                         * @private
277                         * @param {node} node with layer to order
278                         * @param {parent} father node
279                         * @param {index} index node inside parent
280                         */
281                         orderLayer = function(node, parent, index) {
282                             this._reordering = true;
283                             // remove the record and re-insert it at the correct index
284                             this.store = obj.loader.store;
285                             var record = this.store.getByLayer(node.layer);
286                             if(parent instanceof GeoExt.tree.LayerContainer) {
287                                 parent.loader._reordering = true;
288                                 this.store.remove(record);
289                                 var newRecordIndex;
290                                 if(index > 0) {
291                                     // find index by neighboring node in the same container
292                                     var searchIndex = (index === 0) ? index + 1 : index - 1;
293                                     newRecordIndex = this.store.findBy(function(r) {
294                                         return parent.childNodes[searchIndex].layer === r.getLayer();
295                                     });
296                                     index === 0 && newRecordIndex++;
297                                 } else {
298                                     // find index by last node of a container above
299                                     var prev = parent;
300                                     do {
301                                         prev = prev.previousSibling;
302                                     } while (prev && !(prev instanceof GeoExt.tree.LayerContainer && prev.lastChild));
303                                     if(prev) {
304                                         newRecordIndex = this.store.findBy(function(r) {
305                                             return prev.lastChild.layer === r.getLayer();
306                                         });
307                                     } else {
308                                         // find indext by first node of a container below
309                                         var next = parent;
310                                         do {
311                                             next = next.nextSibling;
312                                         } while (next && !(next instanceof GeoExt.tree.LayerContainer && next.firstChild));
313                                         if(next) {
314                                             newRecordIndex = this.store.findBy(function(r) {
315                                                 return next.firstChild.layer === r.getLayer();
316                                             });
317                                         }
318                                         newRecordIndex++;
319                                     }
320                                 }
321                                 if(newRecordIndex !== undefined) {
322                                     this.store.insert(newRecordIndex, [record]);
323                                     window.setTimeout(function() {
324                                         parent.reload();
325                                         //oldParent.reload();
326                                     });
327                                 } else {
328                                     this.store.insert(oldRecordIndex, [record]);
329                                 }
330                                 delete parent.loader._reordering;
331                             }
332                             delete this._reordering;
333                         }
334                         //reorder layers on map
335                         for(var i=0;i<obj.childNodes.length;i++){
336                              orderLayer(obj.childNodes[i],  obj, i);
337                         }
338                     }
339                 }
340             }, groupConfig));
341     },
342       
343     /** Add a group with given group/group name
344     * @public
345     * @param {group} group object or group name
346     */
347     addGroup: function(group){
348         var plugin = this;
349         var groupConfig = typeof group == "string" ? {title: group, checked:false} : group;
350         for(var group_ in this.groups) {
351             objGroup = typeof this.groups[group_] == "string" ? {title: this.groups[group_]} : this.groups[group_];
352             if(objGroup.title === groupConfig.title){
353                 alert('Gruppo giĆ  esistente!');
354                 return;
355             } 
356         }
357         var keyGroup = groupConfig.title.replace(/\s/g, "_");
358         this.groups[keyGroup] = groupConfig;
359         //riordina posizioni groups
360         for(var group_ in this.groups) {
361             objGroup = typeof this.groups[group_] == "string" ? {title: this.groups[group_]} : this.groups[group_];
362             if(objGroup.title !== groupConfig.title){
363                var obj = this.groups[group_];
364                delete this.groups[group_];
365                this.groups[group_] = obj;
366             } 
367         }
368         var exclusive = groupConfig.exclusive;
369         var baseAttrs;
370         if (this.initialConfig.loader && this.initialConfig.loader.baseAttrs) {
371             baseAttrs = this.initialConfig.loader.baseAttrs;
372         }
373         var firstChild = this.tree.root.item(0);
374         var newNode = this.createNode(plugin,keyGroup,groupConfig,this.defaultGroup,exclusive,baseAttrs);
375         newNode.addVisibilityEventHandlers();
376         this.tree.root.insertBefore(newNode,firstChild);
377         
378     },
379             
380      /** private: method[handleBeforeMoveNode]
381      */
382     handleBeforeMoveNode: function(tree, node, oldParent, newParent, i) {
383         // change the group when moving to a new container
384         if(oldParent !== newParent) {
385             var store = newParent.loader.store;
386             var index = store.findBy(function(r) {
387                 return r.getLayer() === node.layer;
388             });
389             var record = store.getAt(index);
390             record.set("group", newParent.attributes.group);
391         }
392         
393         //CONTROLLO LAYER TMS "NON DISPONIBILE
394         Ext.each(this.target.mapPanel.map.layers, function(l){               
395                 if (l.layerNT && l.visibility == true) {
396                     l.layerNT.display(false);
397                 }
398          });
399     }
400 });
401 
402 Ext.preg(framework.plugins.LayerManager.prototype.ptype, framework.plugins.LayerManager);
403