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