7b43ebb2031bad5b8d4feeca3737fb8e22034b51
[unfold.git] / plugins / googlemap / googlemap.js
1 /**
2  * MySlice GoogleMap plugin
3  * URL: http://trac.myslice.info
4  * Description: display a query result in a googlemap
5  * Author: The MySlice Team
6  * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA
7  * License: GPLv3
8  */
9
10 // xxx TODO -- this one could use a bit of cleaning like what was done for the first plugins
11 // especially wrt using 'instance' and 'data' in such a confusing way
12
13 (function( jQuery ){
14
15   var methods = {
16      init : function( options ) {
17
18        return this.each(function(){
19          
20          var $this = jQuery(this),
21              data = $this.data('GoogleMap'), GoogleMap = jQuery('<div />', {text : $this.attr('title')});
22          
23          // If the plugin hasn't been initialized yet
24          if ( ! data ) {
25          
26            /* Plugin initialization */
27
28             //google.load('maps', '3', { other_params: 'sensor=false' });
29             //google.setOnLoadCallback(initialize);
30
31             $this.data('map', null);
32             $this.data('markerCluster', null);
33             $this.data('markers', []);
34
35             var myLatlng = new google.maps.LatLng(34.397, 150.644);
36             var myOptions = {
37               zoom: 2,
38               center: myLatlng,
39               mapTypeId: google.maps.MapTypeId.ROADMAP
40             }
41       
42             var map = new google.maps.Map(document.getElementById("map"), myOptions);
43             $this.data('map', map);
44
45             /* End of plugin initialization */
46
47             jQuery(this).data('GoogleMap', {
48                                 plugin_uuid: options.plugin_uuid,
49                                 query_uuid: options.query_uuid,
50                 target : $this,
51                 current_resources: Array(),
52                 GoogleMap : GoogleMap
53             });
54
55             /* Subscribe to query updates */
56             jQuery.subscribe('/results/' + options.query_uuid + '/changed', {instance: $this}, update_map);
57             jQuery.subscribe('/update-set/' + options.query_uuid, {instance: $this}, on_resource_changed);
58             jQuery.subscribe('/query/' + options.query_uuid + '/changed', {instance: $this}, query_changed);
59             
60             //data = jQuery(this).data();
61             
62             // TODO: Change the status of a node based on the actions in GoogleMap plugin or in other plugins (e.g. DataTables)
63             // Can we publish a value in results row['sliver'] ???
64             // Today, the value is attached or undefined
65             // But can we think about a added/removed status ???
66             // This plugin would update the map based on the results published
67
68          }
69        });
70      },
71     destroy : function( ) {
72
73         return this.each(function(){
74             var $this = jQuery(this), data = $this.data('GoogleMap');
75                         jQuery(window).unbind('GoogleMap');
76                         data.GoogleMap.remove();
77                         $this.removeData('GoogleMap');
78                 })
79
80     },
81 /*
82     reposition : function( ) { // ... },
83     update : function( ) { // ... },
84     hide : function( ) { // ... },
85 */
86     show : function( content ) {
87         google.maps.event.trigger(map, 'resize');
88     }
89   };
90
91     jQuery.fn.GoogleMap = function( method ) {
92                 /* Method calling logic */
93                 if ( methods[method] ) {
94                         return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
95                 } else if ( typeof method === 'object' || ! method ) {
96                         return methods.init.apply( this, arguments );
97                 } else {
98                         jQuery.error( 'Method ' +  method + ' does not exist on jQuery.GoogleMap' );
99                 }    
100
101     };
102
103     /* Private methods */
104         function query_changed(e,query){
105             var data = e.data.instance.data();
106             /* Compare current and advertised query to get added and removed fields */
107             previous_query=data.current_query;
108             /* Save the query as the current query */
109             data.current_query=query;
110             
111             var rows=[];
112             if(typeof(data.results)!="undefined" && data.results.length>0){
113                 jQuery.each(data.results, function(i, row){
114                     jQuery.each(query.filter, function (idx, filter){
115                         if(get_value(row[filter[0]])==filter[2]){
116                             rows.push(row);
117                         }
118                     });
119                 });
120                 data.markerCluster=[];
121                 data.markers=[];
122                 var myLatlng = new google.maps.LatLng(34.397, 150.644);
123                 var myOptions = {
124                     zoom: 2,
125                     center: myLatlng,
126                     mapTypeId: google.maps.MapTypeId.ROADMAP
127                 }
128                 map = new google.maps.Map(jQuery('#map')[0],myOptions);
129                 data.map=map;
130                 //map.clearMarkers();
131                 update_map(e, rows);
132             }
133         }
134         function update_map(e, rows) {            
135             var data = e.data.instance.data();
136             var instance_ = e.data.instance;
137             //$plugindiv.closest('.need-spin').spin(false);
138             instance_.closest('.need-spin').spin(false);
139
140
141             if (!rows) {
142                 alert('error');
143                 return;
144             }
145
146             if(rows.length==0) {
147                 rows=data.results;
148             }
149
150             if(typeof(data.results)=="undefined" || data.results==null){
151                 data.results=rows;
152             }
153             var map = data.map;
154             var markerCluster = data.markerCluster;            
155             var markers = data.markers;
156             var coords = new Array();
157             var infowindow = new google.maps.InfoWindow();
158             /*
159             if(typeof(markers)!="undefined" && markers.length>0){
160                 map.clearMarkers();
161             }*/
162
163             data.current_resources = Array();
164
165             jQuery.each(data.results, function(i, result){
166                 // get the coordinates
167                 var latitude=get_value(result['latitude']);
168                 var longitude=get_value(result['longitude']);
169                 var hash = latitude + longitude;
170
171                 // check to see if we've seen this hash before
172                 if(coords[hash] == null) {
173                     // get coordinate object
174                     var myLatlng = new google.maps.LatLng(latitude, longitude);
175                     // store an indicator that we've seen this point before
176                     coords[hash] = 1;
177                 } else {
178                     // add some randomness to this point 1500 = 100 meters, 15000 = 10 meters
179                     var lat = latitude + (Math.random() -.5) / 1500; 
180                     var lng = longitude + (Math.random() -.5) / 1500; 
181
182                     // get the coordinate object
183                     var myLatlng = new google.maps.LatLng(lat, lng);
184                 }
185                 // If the node is attached to the slice, action will be Remove; else action will be add to slice
186                 if (typeof(result['sliver']) != 'undefined') {
187                     data.current_resources.push(result['urn']);
188                     action="del";
189                     action_class="ui-icon-circle-minus";
190                     action_message="Remove from slice";
191                 }else{
192                     action="add";
193                     action_class="ui-icon-circle-plus";
194                     action_message="Add to slice";
195                 }
196                 // XXX not working
197                 if (!(result['latitude'])) {
198                     return true;
199                 }
200
201                 //jQuery(".map-button").click(button_click);
202                 if(jQuery.inArray(result,rows)>-1){
203                     var marker = new google.maps.Marker({
204                         position: myLatlng,
205                         title: get_value(result['ip']),
206                         // This should be done by the rendering
207                         content: '<p>Agent: ' + get_value(result['ip']) + ' (' + get_value(result['urn']) + ')<br/>Platform: ' + get_value(result['platform'])+'</p>' +
208                                 '<div class="map-button" id="'+action+'/'+get_value(result['urn'])+'" style="cursor:pointer;">'+
209                                 '<span class="ui-icon '+action_class+'" style="clear:both;float:left;"></span>'+action_message+
210                                 '</div>'
211                     }); 
212
213                     google.maps.event.addListener(marker, 'click', function() {
214                             infowindow.content = this.content;
215                             infowindow.open(map, this);
216                             // onload of the infowindow on the map, bind a click on a button
217                             google.maps.event.addListener(infowindow, 'domready', function() {
218                                 jQuery('.map-button').unbind('click');
219                                 jQuery(".map-button").click({instance: instance_}, button_click);
220                             });
221                     });
222                     markers.push(marker);
223                 }
224             });
225             markerCluster = new MarkerClusterer(map, markers, {zoomOnClick: false});
226             google.maps.event.addListener(markerCluster, "clusterclick", function (cluster) {
227                 var markers = cluster.getMarkers();
228                 var bounds  = new google.maps.LatLngBounds();
229               /* 
230               * date: 24/05/2012
231               * author: lbaron
232               * Firefox JS Error - replaced $.each by JQuery.each
233               */                  
234                 jQuery.each(markers, function(i, marker){
235                     bounds.extend(marker.getPosition()); 
236                     });
237                 //map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
238                 map.fitBounds(bounds);
239             });
240             data.markerCluster=markerCluster;
241         }
242         function button_click(e){
243             var data = e.data.instance.data().GoogleMap;
244             var op_value=this.id.split("/");
245             if(op_value.length>0){
246                 jQuery.publish('selected', op_value[0]+'/'+op_value[1]);
247                 var value = op_value[1];
248
249                 if (op_value[0] == 'add') {
250                     data.current_resources.push(value);
251                 } else {
252                     tmp = jQuery.grep(data.current_resources, function(x) { return x != value; });
253                     data.current_resources = tmp;
254                 }
255
256                 /* inform slice that our selected resources have changed */
257                 jQuery.publish('/update-set/' + data.query_uuid, [data.current_resources, true, e.data.instance]);
258             }
259         }
260
261     function on_resource_changed(e, resources, instance)
262     {
263         /* TODO OPENLAB : this query determines which checkboxes must be checked */
264         if (instance == e.data.instance)
265             return;
266         data = e.data.instance.data().GoogleMap;
267
268         previous_resources = data.current_resources;
269         data.current_resources = resources;
270
271         /* TODO We uncheck all checkboxes ... */
272         //jQuery('datatables-checkbox-' + data.options.plugin_uuid).attr('checked', false);
273         /* ... and check the ones specified in the resource list */
274         //jQuery.each(data.current_resources, function(index, urn) {
275         //    jQuery('#datatables-checkbox-' + data.options.plugin_uuid + "-" + urn).attr('checked', true)
276         //});
277         
278     }
279
280 })( jQuery );