8c3c09582f074e910ac40559922c635aef767dd2
[myslice.git] / plugins / googlemap / googlemap.js
1 /**
2  * Description: display a query result in a googlemap
3  * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA
4  * License: GPLv3
5  */
6
7 /*
8  * It's a best practice to pass jQuery to an IIFE (Immediately Invoked Function
9  * Expression) that maps it to the dollar sign so it can't be overwritten by
10  * another library in the scope of its execution.
11  */
12
13 (function($){
14
15     // routing calls
16     jQuery.fn.GoogleMap = function( method ) {
17                 if ( methods[method] ) {
18                         return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
19                 } else if ( typeof method === 'object' || ! method ) {
20                         return methods.init.apply( this, arguments );
21                 } else {
22                         jQuery.error( 'Method ' +  method + ' does not exist on jQuery.GoogleMap' );
23                 }    
24     };
25
26     /***************************************************************************
27      * Public methods
28      ***************************************************************************/
29
30     var methods = {
31
32         /**
33          * @brief Plugin initialization
34          * @param options : an associative array of setting values
35          * @return : a jQuery collection of objects on which the plugin is
36          *     applied, which allows to maintain chainability of calls
37          */
38         init : function( options ) {
39
40             return this.each(function(){
41          
42                 var $this = $(this);
43
44                 /* An object that will hold private variables and methods */
45                 var plugin = new GoogleMaps(options);
46                 $this.data('Manifold', plugin);
47
48                 plugin.initialize();
49
50                 /* Events */
51                 $this.on('show.GoogleMaps', methods.show);
52
53                 $this.set_query_handler(options.query_uuid, plugin.query_handler);
54                 $this.set_record_handler(options.query_uuid, plugin.record_handler); 
55                 $this.set_record_handler(options.query_all_uuid, plugin.record_handler_all); 
56
57             }); // this.each
58         }, // init
59
60         /**
61          * @brief Plugin destruction
62          * @return : a jQuery collection of objects on which the plugin is
63          *     applied, which allows to maintain chainability of calls
64          */
65         destroy : function( ) {
66
67             return this.each(function() {
68                 var $this = $(this);
69                 var hazelnut = $this.data('Manifold');
70
71                 // Unbind all events using namespacing
72                 $(window).unbind('Manifold');
73
74                 // Remove associated data
75                 hazelnut.remove();
76                 $this.removeData('Manifold');
77             });
78
79         }, // destroy
80
81         show : function( ) {
82             google.maps.event.trigger(map, 'resize');
83         } // show
84
85     }; // var methods;
86
87     /***************************************************************************
88      * Plugin object
89      ***************************************************************************/
90
91     function GoogleMaps(options) 
92     {
93         /* member variables */
94         this.options = options;
95
96         // query status
97         this.received_all = false;
98         this.received_set = false;
99         this.in_set_buffer = Array();
100
101         var object = this;
102
103         /**
104          */
105         this.initialize = function() {
106             this.map = null;
107             this.markerCluster = null;
108             this.markers = [];
109             this.coords = new Array();
110
111             var myLatlng = new google.maps.LatLng(options.latitude, options.longitude);
112             var myOptions = {
113                 zoom: options.zoom,
114                 center: myLatlng,
115                 mapTypeId: google.maps.MapTypeId.ROADMAP
116             }
117       
118             this.map = new google.maps.Map(document.getElementById("map"), myOptions);
119             this.infowindow = new google.maps.InfoWindow();
120         }
121
122         /**
123          */
124         this.new_record = function(record)
125         {
126
127             // get the coordinates
128             var latitude=get_value(record['latitude']);
129             var longitude=get_value(record['longitude']);
130             var hash = latitude + longitude;
131
132             // check to see if we've seen this hash before
133             if(this.coords[hash] == null) {
134                 // get coordinate object
135                 var myLatlng = new google.maps.LatLng(latitude, longitude);
136                 // store an indicator that we've seen this point before
137                 this.coords[hash] = 1;
138             } else {
139                 // add some randomness to this point 1500 = 100 meters, 15000 = 10 meters
140                 var lat = latitude + (Math.random() -.5) / 1500; 
141                 var lng = longitude + (Math.random() -.5) / 1500; 
142
143                 // get the coordinate object
144                 var myLatlng = new google.maps.LatLng(lat, lng);
145             }
146             // If the node is attached to the slice, action will be Remove; else action will be add to slice
147             if (typeof(record['sliver']) != 'undefined') {
148                 data.current_resources.push(record['urn']);
149                 action="del";
150                 action_class="ui-icon-circle-minus";
151                 action_message="Remove from slice";
152             }else{
153                 action="add";
154                 action_class="ui-icon-circle-plus";
155                 action_message="Add to slice";
156             }
157             // XXX not working
158             if (!(record['latitude'])) {
159                 return true;
160             }
161
162             //jQuery(".map-button").click(button_click);
163             //if(jQuery.inArray(record, rows)>-1){
164                 var marker = new google.maps.Marker({
165                     position: myLatlng,
166                     title: get_value(record['hostname']),
167                     // This should be done by the rendering
168                     content: '<p>Agent: ' + get_value(record['ip']) + ' (' + get_value(record['resource_hrn']) + ')<br/>Platform: ' + get_value(record['platform'])+'</p>' +
169                             '<div class="map-button" id="'+action+'/'+get_value(record['resource_hrn'])+'" style="cursor:pointer;">'+
170                             '<span class="ui-icon '+action_class+'" style="clear:both;float:left;"></span>'+action_message+
171                             '</div>'
172                 }); 
173
174                 this.addInfoWindow(marker, object.map);
175                 object.markers.push(marker);
176             //}
177
178         };
179
180         this.addInfoWindow = function(marker, map) {
181             google.maps.event.addListener(marker, 'click', function () {     
182                 if(object.infowindow){
183                     object.infowindow.close();
184                 }
185                 object.infowindow.setContent(marker.content);// = new google.maps.InfoWindow({ content: marker.content });
186                 object.infowindow.open(map, marker);
187                 // onload of the infowindow on the map, bind a click on a button
188                 google.maps.event.addListener(object.infowindow, 'domready', function() {
189                     jQuery('.map-button').unbind('click');
190 //                    jQuery(".map-button").click({instance: instance_, infoWindow: object.infowindow}, button_click);                     
191                 });
192             });
193         }
194
195
196         this.set_checkbox = function(record)
197         {
198             // XXX urn should be replaced by the key
199             // XXX we should enforce that both queries have the same key !!
200             //checkbox_id = "#hazelnut-checkbox-" + object.options.plugin_uuid + "-" + unfold.escape_id(record[ELEMENT_KEY].replace(/\\/g, ''))
201             //$(checkbox_id, object.table.fnGetNodes()).attr('checked', true);
202         }
203
204         this.record_handler = function(e, event_type, record)
205         {
206             // elements in set
207             switch(event_type) {
208                 case NEW_RECORD:
209                     /* NOTE in fact we are doing a join here */
210                     if (object.received_all)
211                         // update checkbox for record
212                         object.set_checkbox(record);
213                     else
214                         // store for later update of checkboxes
215                         object.in_set_buffer.push(record);
216                     break;
217                 case CLEAR_RECORDS:
218                     // nothing to do here
219                     break;
220                 case IN_PROGRESS:
221                     manifold.spin($(this));
222                     break;
223                 case DONE:
224                     if (object.received_all)
225                         manifold.spin($(this), false);
226                     object.received_set = true;
227                     break;
228             }
229         };
230
231         this.record_handler_all = function(e, event_type, record)
232         {
233             // all elements
234             switch(event_type) {
235                 case NEW_RECORD:
236                     // Add the record to the table
237                     object.new_record(record);
238                     break;
239                 case CLEAR_RECORDS:
240                     // object.table.fnClearTable();
241                     break;
242                 case IN_PROGRESS:
243                     manifold.spin($(this));
244                     break;
245                 case DONE:
246
247                     // MarkerClusterer
248                     object.markerCluster = new MarkerClusterer(object.map, object.markers, {zoomOnClick: false});
249                     google.maps.event.addListener(object.markerCluster, "clusterclick", function (cluster) {
250                         var markers = cluster.getMarkers();
251                         var bounds  = new google.maps.LatLngBounds();
252                       /* 
253                       * date: 24/05/2012
254                       * author: lbaron
255                       * Firefox JS Error - replaced $.each by JQuery.each
256                       */                  
257                       jQuery.each(markers, function(i, marker){
258                           bounds.extend(marker.getPosition()); 
259                       });
260
261                       //map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
262                       object.map.fitBounds(bounds);
263                     });
264
265                     if (object.received_set) {
266                         /* XXX needed ? XXX We uncheck all checkboxes ... */
267                         $("[id^='datatables-checkbox-" + object.options.plugin_uuid +"']").attr('checked', false);
268
269                         /* ... and check the ones specified in the resource list */
270                         $.each(object.in_set_buffer, function(i, record) {
271                             object.set_checkbox(record);
272                         });
273
274                         manifold.spin($(this), false);
275                     }
276                     object.received_all = true;
277                     break;
278             }
279         };
280
281
282         this.query_handler = function(e, event_type, query)
283         {
284             // This replaces the complex set_query function
285             // The plugin does not need to remember the query anymore
286             switch(event_type) {
287                 // Filters
288                 case FILTER_ADDED:
289                 case FILTER_REMOVED:
290                 case CLEAR_FILTERS:
291                     // XXX Here we might need to maintain the list of filters !
292                     /* Process updates in filters / current_query must be updated before this call for filtering ! */
293                     object.table.fnDraw();
294                     break;
295
296                 // Fields
297                 /* Hide/unhide columns to match added/removed fields */
298                 case FIELD_ADDED:
299                     var object = this;
300                     $.each(added_fields, function (index, field) {            
301                         var index = object.getColIndex(field,cols);
302                         if(index != -1)
303                             object.table.fnSetColumnVis(index, true);
304                     });
305                     break;
306                 case FIELD_REMOVED:
307                     var object = this;
308                     $.each(removed_fields, function (index, field) {
309                         var index = object.getColIndex(field,cols);
310                         if(index != -1)
311                             object.table.fnSetColumnVis(index, false);
312                     });            
313                     break;
314                 case CLEAR_FIELDS:
315                     alert('GoogleMaps::clear_fields() not implemented');
316                     break;
317             } // switch
318
319
320         }
321
322         function button_click(e){
323             var op_value=this.id.split("/");
324             if(op_value.length>0){
325                 var value = op_value[1];
326                 manifold.raise_event(object.options.query_uuid, (op_value[0] == 'add')?SET_ADD:SET_REMOVED, value);
327             }
328         } // function button_click()
329     } 
330
331     // clear and replace
332 //                jQuery.each(data.results, function(i, row){
333 //                    jQuery.each(query.filter, function (idx, filter){
334 //                        if(get_value(row[filter[0]])==filter[2]){
335 //                            rows.push(row);
336 //                        }
337 //                    });
338 //                });
339 //                data.markerCluster=[];
340 //                data.markers=[];
341 //                var myLatlng = new google.maps.LatLng(34.397, 150.644);
342 //                var myOptions = {
343 //                    zoom: 2,
344 //                    center: myLatlng,
345 //                    mapTypeId: google.maps.MapTypeId.ROADMAP
346 //                }
347 //                map = new google.maps.Map(jQuery('#map')[0],myOptions);
348 //                data.map=map;
349 //                //map.clearMarkers();
350 //                update_map(e, rows);
351 //            }
352 //        }
353
354 })( jQuery );