refinerycms-location_explorer 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/app/assets/javascripts/refinery/location_explorer/locations_map.coffee +43 -0
  2. data/app/assets/javascripts/refinery/location_explorer/map_picker.coffee +28 -0
  3. data/app/assets/javascripts/refinery/location_explorer/vendor/gmaps.js +1091 -0
  4. data/app/assets/stylesheets/refinery/location_explorer.css.scss +70 -0
  5. data/app/assets/stylesheets/refinery/location_explorer/map_picker.css.scss +26 -0
  6. data/app/controllers/refinery/location_explorer/admin/locations_controller.rb +12 -0
  7. data/app/controllers/refinery/location_explorer/locations_controller.rb +44 -0
  8. data/app/models/refinery/location_explorer/location.rb +41 -0
  9. data/app/models/refinery/location_explorer/owner_decorator.rb +46 -0
  10. data/app/views/refinery/location_explorer/admin/locations/_actions.html.erb +25 -0
  11. data/app/views/refinery/location_explorer/admin/locations/_form.html.erb +27 -0
  12. data/app/views/refinery/location_explorer/admin/locations/_location.html.erb +20 -0
  13. data/app/views/refinery/location_explorer/admin/locations/_locations.html.erb +2 -0
  14. data/app/views/refinery/location_explorer/admin/locations/_map.html.erb +29 -0
  15. data/app/views/refinery/location_explorer/admin/locations/_records.html.erb +18 -0
  16. data/app/views/refinery/location_explorer/admin/locations/_sortable_list.html.erb +5 -0
  17. data/app/views/refinery/location_explorer/admin/locations/edit.html.erb +1 -0
  18. data/app/views/refinery/location_explorer/admin/locations/index.html.erb +7 -0
  19. data/app/views/refinery/location_explorer/admin/locations/new.html.erb +1 -0
  20. data/app/views/refinery/location_explorer/locations/index.html.haml +30 -0
  21. data/app/views/refinery/location_explorer/locations/show.html.haml +16 -0
  22. data/config/locales/en.yml +30 -0
  23. data/config/locales/es.yml +31 -0
  24. data/config/locales/fr.yml +30 -0
  25. data/config/locales/nb.yml +30 -0
  26. data/config/locales/nl.yml +30 -0
  27. data/config/routes.rb +17 -0
  28. data/db/migrate/1_create_location_explorer_locations.rb +33 -0
  29. data/db/seeds.rb +25 -0
  30. data/lib/generators/refinery/location_explorer_generator.rb +23 -0
  31. data/lib/generators/refinery/templates/config/initializers/refinery/location_explorer.rb.erb +4 -0
  32. data/lib/refinery/location_explorer.rb +22 -0
  33. data/lib/refinery/location_explorer/configuration.rb +9 -0
  34. data/lib/refinery/location_explorer/engine.rb +27 -0
  35. data/lib/refinerycms-location_explorer.rb +3 -0
  36. data/lib/tasks/refinery/locations.rake +13 -0
  37. data/readme.md +51 -0
  38. metadata +148 -0
@@ -0,0 +1,43 @@
1
+ window.Application ||= {}
2
+
3
+ class Application.LocationsMap
4
+ constructor: (container_id, center, options = {}) ->
5
+ @map = new GMaps {
6
+ div: container_id,
7
+ zoom: options.zoom || 16,
8
+ mapType: 'Satellite'
9
+ lat: center[0],
10
+ lng: center[1]
11
+ }
12
+
13
+ updateLocation: (location, e) =>
14
+ $('#location_preview').show()
15
+ $('#location_preview .location_name').text(location['name'])
16
+ $('#location_preview .description_preview').text(location['description_preview'])
17
+ $('#location_preview .more_details').attr('href', location['detail_url'])
18
+ if location['preview_image_url']
19
+ $('#location_preview .image_container').html("<img src=\"#{location['preview_image_url']}\" class=\"location_image\"/>")
20
+ else
21
+ $('#location_preview .image_container').html("")
22
+
23
+ addLocations: (locations) ->
24
+ @locations = locations
25
+ for location in @locations
26
+ this.addLocation location
27
+
28
+ addLocation: (location) ->
29
+ pinColor = location['colour'] || "FE7569"
30
+ pinImage = new google.maps.MarkerImage("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|" + pinColor,
31
+ new google.maps.Size(21, 34),
32
+ new google.maps.Point(0,0),
33
+ new google.maps.Point(10, 34))
34
+
35
+ @map.addMarker {
36
+ lat: location['latitude'],
37
+ lng: location['longitude'],
38
+ icon: pinImage,
39
+ title: location['name'],
40
+ infoWindow: {content: location['name']}
41
+ click: (e)=>
42
+ this.updateLocation(location, e)
43
+ }
@@ -0,0 +1,28 @@
1
+ window.Application ||= {}
2
+
3
+ class Application.MapPicker
4
+ constructor: (container_id, center, initial_point) ->
5
+ @map = new GMaps {
6
+ div: container_id,
7
+ zoom: 16,
8
+ mapType: 'Satellite'
9
+ lat: center[0],
10
+ lng: center[1]
11
+ }
12
+ this.setPoint initial_point if initial_point
13
+ google.maps.event.addListener @map.map, 'click', this.pickPoint
14
+
15
+ pickPoint: (e) =>
16
+ $('#location_latitude').val e.latLng.lat()
17
+ $('#location_longitude').val e.latLng.lng()
18
+ point = [e.latLng.lat(), e.latLng.lng()]
19
+ this.setPoint point
20
+
21
+ setPoint: (point) ->
22
+ @map.removeMarkers()
23
+ @map.addMarker({
24
+ lat: point[0],
25
+ lng: point[1],
26
+ title: 'Selected point'
27
+ })
28
+
@@ -0,0 +1,1091 @@
1
+ /*!
2
+ * GMaps.js
3
+ * http://hpneo.github.com/gmaps/
4
+ *
5
+ * Copyright 2012, Gustavo Leon
6
+ * Released under the MIT License.
7
+ */
8
+
9
+ var GMaps = (function($) {
10
+ "use strict";
11
+
12
+ var GMaps = function(options) {
13
+ var self = this;
14
+ var context_menu_style = 'position:absolute;display:none;min-width:100px;' +
15
+ 'background:white;list-style:none;padding:8px;box-shadow:2px 2px 6px #ccc';
16
+ window.context_menu = {};
17
+
18
+ this.div = $(options.div)[0];
19
+ this.div.style.width = this.div.clientWidth || options.width;
20
+ this.div.style.height = this.div.clientHeight || options.height;
21
+
22
+ this.controls = [];
23
+ this.overlays = [];
24
+ this.layers = [];
25
+ this.markers = [];
26
+ this.polylines = [];
27
+ this.routes = [];
28
+ this.polygons = [];
29
+ this.infoWindow = null;
30
+ this.overlay_div = null;
31
+ this.zoom = options.zoom || 15;
32
+
33
+ //'Hybrid', 'Roadmap', 'Satellite' or 'Terrain'
34
+ var mapType;
35
+
36
+ if (options.mapType) {
37
+ mapType = google.maps.MapTypeId[options.mapType.toUpperCase()];
38
+ }
39
+ else {
40
+ mapType = google.maps.MapTypeId.ROADMAP;
41
+ }
42
+
43
+ var map_center = new google.maps.LatLng(options.lat, options.lng);
44
+
45
+ delete options.lat;
46
+ delete options.lng;
47
+ delete options.mapType;
48
+ delete options.width;
49
+ delete options.height;
50
+
51
+ var map_base_options = {
52
+ zoom: this.zoom,
53
+ center: map_center,
54
+ mapTypeId: mapType
55
+ };
56
+
57
+ var map_options = $.extend(map_base_options, options);
58
+
59
+ this.map = new google.maps.Map(this.div, map_options);
60
+
61
+ // Context menus
62
+ var buildContextMenuHTML = function(control, e) {
63
+ var html = '';
64
+ var options = window.context_menu[control];
65
+ for (var i in options){
66
+ if (options.hasOwnProperty(i)){
67
+ var option = options[i];
68
+ html += '<li><a id="' + control + '_' + i + '" href="#">' +
69
+ option.title + '</a></li>';
70
+ }
71
+ }
72
+ var $context_menu = $('#gmaps_context_menu');
73
+ $context_menu.html(html);
74
+ $context_menu.undelegate();
75
+ $context_menu.delegate('li a', 'click', function(ev) {
76
+ ev.preventDefault();
77
+ options[$(this).attr('id').replace(control + '_', '')].action.call(self, e);
78
+ self.hideContextMenu();
79
+ });
80
+
81
+ var left = $(self.div).offset().left + e.pixel.x - 15;
82
+ var top = $(self.div).offset().top + e.pixel.y - 15;
83
+ $context_menu.css({
84
+ left: left,
85
+ top: top
86
+ }).fadeIn(200);
87
+ };
88
+
89
+ var buildContextMenu = function(control, e) {
90
+ if (control === 'marker') {
91
+ e.pixel = {};
92
+ var overlay = new google.maps.OverlayView();
93
+ overlay.setMap(self.map);
94
+ overlay.draw = function() {
95
+ var projection = overlay.getProjection();
96
+ var position = e.marker.getPosition();
97
+ e.pixel = projection.fromLatLngToContainerPixel(position);
98
+
99
+ buildContextMenuHTML(control, e);
100
+ };
101
+ }
102
+ else {
103
+ buildContextMenuHTML(control, e);
104
+ }
105
+ };
106
+
107
+ this.setContextMenu = function(options) {
108
+ window.context_menu[options.control] = {};
109
+ var html = '<ul id="gmaps_context_menu" style="' + context_menu_style + '"></ul>';
110
+ for (var i in options.options){
111
+ if (options.options.hasOwnProperty(i)){
112
+ var option = options.options[i];
113
+ window.context_menu[options.control][option.name] = {
114
+ title: option.title,
115
+ action: option.action
116
+ };
117
+ }
118
+ }
119
+ $('body').append(html);
120
+ $('#gmaps_context_menu').mouseleave(function() {
121
+ $(this).delay(100).fadeOut(200);
122
+ });
123
+ };
124
+
125
+ this.hideContextMenu = function() {
126
+ $('#gmaps_context_menu').fadeOut(200);
127
+ };
128
+
129
+ //Events
130
+
131
+ var events_that_hide_context_menu = ['bounds_changed', 'center_changed', 'click', 'dblclick', 'drag', 'dragend', 'dragstart', 'idle', 'maptypeid_changed', 'projection_changed', 'resize', 'tilesloaded', 'zoom_changed'];
132
+ var events_that_doesnt_hide_context_menu = ['mousemove', 'mouseout', 'mouseover'];
133
+
134
+ for (var ev = 0; ev < events_that_hide_context_menu.length; ev++) {
135
+ (function(object, name) {
136
+ google.maps.event.addListener(object, name, function(e){
137
+ if (options[name])
138
+ options[name].apply(this, [e]);
139
+
140
+ self.hideContextMenu();
141
+ });
142
+ })(this.map, events_that_hide_context_menu[ev]);
143
+ }
144
+
145
+ for (var ev = 0; ev < events_that_doesnt_hide_context_menu.length; ev++) {
146
+ (function(object, name) {
147
+ google.maps.event.addListener(object, name, function(e){
148
+ if (options[name])
149
+ options[name].apply(this, [e]);
150
+ });
151
+ })(this.map, events_that_doesnt_hide_context_menu[ev]);
152
+ }
153
+
154
+ google.maps.event.addListener(this.map, 'rightclick', function(e) {
155
+ if (options.rightclick) {
156
+ options.rightclick.apply(this, [e]);
157
+ }
158
+
159
+ buildContextMenu('map', e);
160
+ });
161
+
162
+ this.refresh = function() {
163
+ google.maps.event.trigger(this.map, 'resize');
164
+ };
165
+
166
+ this.fitZoom = function() {
167
+ var latLngs = [];
168
+ var markers_length = this.markers.length;
169
+
170
+ for(var i=0; i < markers_length; i++) {
171
+ latLngs.push(this.markers[i].getPosition());
172
+ }
173
+
174
+ this.fitBounds(latLngs);
175
+ };
176
+
177
+ this.fitBounds = function(latLngs) {
178
+ var total = latLngs.length;
179
+ var bounds = new google.maps.LatLngBounds();
180
+
181
+ for(var i=0; i < total; i++) {
182
+ bounds.extend(latLngs[i]);
183
+ }
184
+
185
+ this.map.fitBounds(bounds);
186
+ };
187
+
188
+ // Map methods
189
+ this.setCenter = function(lat, lng, callback) {
190
+ this.map.panTo(new google.maps.LatLng(lat, lng));
191
+ if (callback) {
192
+ callback();
193
+ }
194
+ };
195
+
196
+ this.getCenter = function() {
197
+ return this.map.getCenter();
198
+ };
199
+
200
+ this.getDiv = function() {
201
+ return this.div;
202
+ };
203
+
204
+ this.setZoom = function(value) {
205
+ this.map.setZoom(value);
206
+ };
207
+
208
+ this.zoomIn = function(value) {
209
+ this.map.setZoom(this.map.getZoom() + value);
210
+ };
211
+
212
+ this.zoomOut = function(value) {
213
+ this.map.setZoom(this.map.getZoom() - value);
214
+ };
215
+
216
+ this.createControl = function(options) {
217
+ options.style.cursor = 'pointer';
218
+ options.style.fontFamily = 'Arial, sans-serif';
219
+ options.style.fontSize = '13px';
220
+ options.style.boxShadow = 'rgba(0, 0, 0, 0.398438) 0px 2px 4px';
221
+
222
+ var controlDiv = $('<div></div>');
223
+ controlDiv.css(options.style);
224
+ controlDiv.text(options.text);
225
+
226
+ var control = controlDiv[0];
227
+
228
+ for (var ev in options.events) {
229
+ (function(object, name) {
230
+ google.maps.event.addDomListener(object, name, function(){
231
+ options.events[name].apply(this, [this]);
232
+ });
233
+ })(control, ev);
234
+ }
235
+
236
+ control.index = 1;
237
+
238
+ return control;
239
+ };
240
+
241
+ this.addControl = function(options) {
242
+ var position = google.maps.ControlPosition[options.position.toUpperCase()];
243
+
244
+ delete options.position;
245
+
246
+ var control = this.createControl(options);
247
+ this.controls.push(control);
248
+ this.map.controls[position].push(control);
249
+
250
+ return control;
251
+ };
252
+
253
+ // Markers
254
+ this.createMarker = function(options) {
255
+ if (options.lat && options.lng) {
256
+ var self = this;
257
+ var details = options.details;
258
+ var fences = options.fences;
259
+ var outside = options.outside;
260
+ var base_options = {
261
+ position: new google.maps.LatLng(options.lat, options.lng),
262
+ map: null
263
+ };
264
+ delete options.lat;
265
+ delete options.lng;
266
+ delete options.fences;
267
+ delete options.outside;
268
+
269
+ var marker_options = $.extend(base_options, options);
270
+
271
+ var marker = new google.maps.Marker(marker_options);
272
+
273
+ marker.fences = fences;
274
+
275
+ if (options.infoWindow) {
276
+ marker.infoWindow = new google.maps.InfoWindow(options.infoWindow);
277
+
278
+ var info_window_events = ['closeclick', 'content_changed', 'domready', 'position_changed', 'zindex_changed'];
279
+
280
+ for (var ev = 0; ev < info_window_events.length; ev++) {
281
+ (function(object, name) {
282
+ google.maps.event.addListener(object, name, function(e){
283
+ if (options.infoWindow[name])
284
+ options.infoWindow[name].apply(this, [e]);
285
+ });
286
+ })(marker.infoWindow, info_window_events[ev]);
287
+ }
288
+ }
289
+
290
+ var marker_events = ['drag', 'dragstart', 'mouseout', 'mouseover', 'mouseup', 'position_changed'];
291
+
292
+ for (var ev = 0; ev < marker_events.length; ev++) {
293
+ (function(object, name) {
294
+ google.maps.event.addListener(object, name, function(){
295
+ if (options[name])
296
+ options[name].apply(this, [this]);
297
+ });
298
+ })(marker, marker_events[ev]);
299
+ }
300
+
301
+ google.maps.event.addListener(marker, 'click', function() {
302
+ this.details = details;
303
+
304
+ if (options.click) {
305
+ options.click.apply(this, [this]);
306
+ }
307
+
308
+ if (marker.infoWindow) {
309
+ self.hideInfoWindows();
310
+ marker.infoWindow.open(self.map, marker);
311
+ }
312
+ });
313
+
314
+ if (options.dragend || marker.fences) {
315
+ google.maps.event.addListener(marker, 'dragend', function() {
316
+ if (options.dragend) {
317
+ options.dragend.apply(this, [this]);
318
+ }
319
+ if (marker.fences) {
320
+ self.checkMarkerGeofence(marker, function(m, f) {
321
+ outside(m, f);
322
+ });
323
+ }
324
+ });
325
+ }
326
+
327
+ return marker;
328
+ }
329
+ else {
330
+ throw 'No latitude or longitude defined';
331
+ }
332
+ };
333
+
334
+ this.addMarker = function(options) {
335
+ if (options.lat && options.lng) {
336
+ var marker = this.createMarker(options);
337
+ marker.setMap(this.map);
338
+ this.markers.push(marker);
339
+
340
+ return marker;
341
+ }
342
+ else {
343
+ throw 'No latitude or longitude defined';
344
+ }
345
+ };
346
+
347
+ this.addMarkers = function(array) {
348
+ for (var i=0, marker; marker=array[i]; i++) {
349
+ this.addMarker(marker);
350
+ }
351
+ return this.markers;
352
+ };
353
+
354
+ this.hideInfoWindows = function() {
355
+ for (var i=0, marker; marker=this.markers[i]; i++){
356
+ if (marker.infoWindow){
357
+ marker.infoWindow.close();
358
+ }
359
+ }
360
+ };
361
+
362
+ this.removeMarkers = function() {
363
+ for (var i=0, marker; marker=this.markers[i]; i++){
364
+ marker.setMap(null);
365
+ }
366
+ this.markers = [];
367
+ };
368
+
369
+ // Overlays
370
+ this.drawOverlay = function(options) {
371
+ var overlay = new google.maps.OverlayView();
372
+ overlay.setMap(self.map);
373
+
374
+ overlay.onAdd = function() {
375
+ var div = document.createElement('div');
376
+ div.style.borderStyle = "none";
377
+ div.style.borderWidth = "0px";
378
+ div.style.position = "absolute";
379
+ div.innerHTML = options.content;
380
+
381
+ self.overlay_div = div;
382
+
383
+ var panes = this.getPanes();
384
+ if (!options.layer) {
385
+ options.layer = 'overlayLayer';
386
+ }
387
+ var overlayLayer = panes[options.layer];
388
+ overlayLayer.appendChild(div);
389
+ };
390
+
391
+ overlay.draw = function() {
392
+ var projection = this.getProjection();
393
+ var pixel = projection.fromLatLngToDivPixel(new google.maps.LatLng(options.lat, options.lng));
394
+
395
+ options.horizontalOffset = options.horizontalOffset || 0;
396
+ options.verticalOffset = options.verticalOffset || 0;
397
+
398
+ var div = self.overlay_div;
399
+ var content = div.children;
400
+
401
+ switch (options.verticalAlign) {
402
+ case 'top':
403
+ div.style.top = (pixel.y - $(content).height() + options.verticalOffset) + 'px';
404
+ break;
405
+ default:
406
+ case 'middle':
407
+ div.style.top = (pixel.y - ($(content).height() / 2) + options.verticalOffset) + 'px';
408
+ break;
409
+ case 'bottom':
410
+ div.style.top = (pixel.y + options.verticalOffset) + 'px';
411
+ break;
412
+ }
413
+
414
+ switch (options.horizontalAlign) {
415
+ case 'left':
416
+ div.style.left = (pixel.x - $(content).width() + options.horizontalOffset) + 'px';
417
+ break;
418
+ default:
419
+ case 'center':
420
+ div.style.left = (pixel.x - ($(content).width() / 2) + options.horizontalOffset) + 'px';
421
+ break;
422
+ case 'right':
423
+ div.style.left = (pixel.x + options.horizontalOffset) + 'px';
424
+ break;
425
+ }
426
+ };
427
+
428
+ overlay.onRemove = function() {
429
+ self.overlay_div.parentNode.removeChild(self.overlay_div);
430
+ self.overlay_div = null;
431
+ };
432
+ self.overlays.push(overlay);
433
+ return overlay;
434
+ };
435
+
436
+ this.removeOverlay = function(overlay) {
437
+ overlay.setMap(null);
438
+ };
439
+
440
+ this.removeOverlays = function() {
441
+ for (var i=0, item; item=self.overlays[i]; i++){
442
+ item.setMap(null);
443
+ }
444
+ self.overlays = [];
445
+ };
446
+
447
+ this.drawPolyline = function(options) {
448
+ var path = [];
449
+ var points = options.path;
450
+
451
+ if (points.length){
452
+ if (points[0][0] === undefined){
453
+ path = points;
454
+ }
455
+ else {
456
+ for (var i=0, latlng; latlng=points[i]; i++){
457
+ path.push(new google.maps.LatLng(latlng[0], latlng[1]));
458
+ }
459
+ }
460
+ }
461
+
462
+ var polyline = new google.maps.Polyline({
463
+ map: this.map,
464
+ path: path,
465
+ strokeColor: options.strokeColor,
466
+ strokeOpacity: options.strokeOpacity,
467
+ strokeWeight: options.strokeWeight
468
+ });
469
+
470
+ var polyline_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
471
+
472
+ for (var ev = 0; ev < polyline_events.length; ev++) {
473
+ (function(object, name) {
474
+ google.maps.event.addListener(object, name, function(e){
475
+ if (options[name])
476
+ options[name].apply(this, [e]);
477
+ });
478
+ })(polyline, polyline_events[ev]);
479
+ }
480
+
481
+ this.polylines.push(polyline);
482
+
483
+ return polyline;
484
+ };
485
+
486
+ this.drawCircle = function(options) {
487
+ options = $.extend({
488
+ map: this.map,
489
+ center: new google.maps.LatLng(options.lat, options.lng)
490
+ }, options);
491
+
492
+ delete options.lat;
493
+ delete options.lng;
494
+ var polygon = new google.maps.Circle(options);
495
+
496
+ var polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
497
+
498
+ for (var ev = 0; ev < polygon_events.length; ev++) {
499
+ (function(object, name) {
500
+ google.maps.event.addListener(object, name, function(e){
501
+ if (options[name])
502
+ options[name].apply(this, [e]);
503
+ });
504
+ })(polygon, polygon_events[ev]);
505
+ }
506
+
507
+ return polygon;
508
+ };
509
+
510
+ this.drawPolygon = function(options) {
511
+ options = $.extend({
512
+ map: this.map
513
+ }, options);
514
+
515
+ if($.isArray(options.paths)) {
516
+ if($.isArray(options.paths[0])) {
517
+ options.paths = $.map(options.paths, arrayToLatLng);
518
+ }
519
+ }
520
+
521
+ var polygon = new google.maps.Polygon(options);
522
+
523
+ var polygon_events = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'rightclick'];
524
+
525
+ for (var ev = 0; ev < polygon_events.length; ev++) {
526
+ (function(object, name) {
527
+ google.maps.event.addListener(object, name, function(e){
528
+ if (options[name])
529
+ options[name].apply(this, [e]);
530
+ });
531
+ })(polygon, polygon_events[ev]);
532
+ }
533
+
534
+ this.polygons.push(polygon);
535
+
536
+ return polygon;
537
+
538
+ function arrayToLatLng(coords) {
539
+ return new google.maps.LatLng(coords[0], coords[1]);
540
+ }
541
+ };
542
+
543
+ this.getFromFusionTables = function(options) {
544
+ var events = options.events;
545
+
546
+ delete options.events;
547
+
548
+ var fusion_tables_options = options;
549
+
550
+ var layer = new google.maps.FusionTablesLayer(fusion_tables_options);
551
+
552
+ for (var ev in events) {
553
+ (function(object, name) {
554
+ google.maps.event.addListener(object, name, function(e){
555
+ events[name].apply(this, [e]);
556
+ });
557
+ })(layer, ev);
558
+ }
559
+
560
+ this.layers.push(layer);
561
+
562
+ return layer;
563
+ };
564
+
565
+ this.loadFromFusionTables = function(options) {
566
+ var layer = this.getFromFusionTables(options);
567
+ layer.setMap(this.map);
568
+
569
+ return layer;
570
+ };
571
+
572
+ this.getFromKML = function(options) {
573
+ var url = options.url;
574
+ var events = options.events;
575
+
576
+ delete options.url;
577
+ delete options.events;
578
+
579
+ var kml_options = options;
580
+
581
+ var layer = new google.maps.KmlLayer(url, kml_options);
582
+
583
+ for (var ev in events) {
584
+ (function(object, name) {
585
+ google.maps.event.addListener(object, name, function(e){
586
+ events[name].apply(this, [e]);
587
+ });
588
+ })(layer, ev);
589
+ }
590
+
591
+ this.layers.push(layer);
592
+
593
+ return layer;
594
+ };
595
+
596
+ this.loadFromKML = function(options) {
597
+ var layer = this.getFromKML(options);
598
+ layer.setMap(this.map);
599
+
600
+ return layer;
601
+ };
602
+
603
+ // Services
604
+ var travelMode, unitSystem;
605
+ this.getRoutes = function(options) {
606
+ switch (options.travelMode) {
607
+ case 'bicycling':
608
+ travelMode = google.maps.TravelMode.BICYCLING;
609
+ break;
610
+ case 'driving':
611
+ travelMode = google.maps.TravelMode.DRIVING;
612
+ break;
613
+ // case 'walking':
614
+ default:
615
+ travelMode = google.maps.TravelMode.WALKING;
616
+ break;
617
+ }
618
+
619
+ if (options.unitSystem === 'imperial') {
620
+ unitSystem = google.maps.UnitSystem.IMPERIAL;
621
+ }
622
+ else {
623
+ unitSystem = google.maps.UnitSystem.METRIC;
624
+ }
625
+
626
+ var base_options = {
627
+ avoidHighways: false,
628
+ avoidTolls: false,
629
+ optimizeWaypoints: false,
630
+ waypoints: []
631
+ };
632
+ var request_options = $.extend(base_options, options);
633
+ request_options.origin = new google.maps.LatLng(options.origin[0], options.origin[1]);
634
+ request_options.destination = new google.maps.LatLng(options.destination[0], options.destination[1]);
635
+ request_options.travelMode = travelMode;
636
+ request_options.unitSystem = unitSystem;
637
+
638
+ delete request_options.callback;
639
+
640
+ var self = this;
641
+ var service = new google.maps.DirectionsService();
642
+
643
+ service.route(request_options, function(result, status) {
644
+ if (status === google.maps.DirectionsStatus.OK) {
645
+ for (var r in result.routes) {
646
+ if (result.routes.hasOwnProperty(r)) {
647
+ self.routes.push(result.routes[r]);
648
+ }
649
+ }
650
+ }
651
+ if (options.callback) {
652
+ options.callback(self.routes);
653
+ }
654
+ });
655
+ };
656
+
657
+ this.removePolylines = function(){
658
+ var index;
659
+ for(index in this.polylines){
660
+ this.polylines[index].setMap(null);
661
+ }
662
+ this.polylines = [];
663
+ }
664
+
665
+ // Alias for the method "drawRoute"
666
+ this.cleanRoute = this.removePolylines;
667
+
668
+ this.drawRoute = function(options) {
669
+ var self = this;
670
+ this.getRoutes({
671
+ origin: options.origin,
672
+ destination: options.destination,
673
+ travelMode: options.travelMode,
674
+ callback: function(e) {
675
+ if (e.length > 0) {
676
+ self.drawPolyline({
677
+ path: e[e.length - 1].overview_path,
678
+ strokeColor: options.strokeColor,
679
+ strokeOpacity: options.strokeOpacity,
680
+ strokeWeight: options.strokeWeight
681
+ });
682
+ if (options.callback) {
683
+ options.callback(e[e.length - 1]);
684
+ }
685
+ }
686
+ }
687
+ });
688
+ };
689
+
690
+ this.travelRoute = function(options) {
691
+ if (options.origin && options.destination) {
692
+ this.getRoutes({
693
+ origin: options.origin,
694
+ destination: options.destination,
695
+ travelMode: options.travelMode,
696
+ callback: function(e) {
697
+ if (e.length > 0 && options.step) {
698
+ var route = e[e.length - 1];
699
+ if (route.legs.length > 0) {
700
+ var steps = route.legs[0].steps;
701
+ for (var i=0, step; step=steps[i]; i++) {
702
+ step.step_number = i;
703
+ options.step(step);
704
+ }
705
+ }
706
+ }
707
+ }
708
+ });
709
+ }
710
+ else if (options.route) {
711
+ if (options.route.legs.length > 0) {
712
+ var steps = options.route.legs[0].steps;
713
+ for (var i=0, step; step=steps[i]; i++) {
714
+ step.step_number = i;
715
+ options.step(step);
716
+ }
717
+ }
718
+ }
719
+ };
720
+
721
+ this.drawSteppedRoute = function(options) {
722
+ if (options.origin && options.destination) {
723
+ this.getRoutes({
724
+ origin: options.origin,
725
+ destination: options.destination,
726
+ travelMode: options.travelMode,
727
+ callback: function(e) {
728
+ if (e.length > 0 && options.step) {
729
+ var route = e[e.length - 1];
730
+ if (route.legs.length > 0) {
731
+ var steps = route.legs[0].steps;
732
+ for (var i=0, step; step=steps[i]; i++) {
733
+ step.step_number = i;
734
+ self.drawPolyline({
735
+ path: step.path,
736
+ strokeColor: options.strokeColor,
737
+ strokeOpacity: options.strokeOpacity,
738
+ strokeWeight: options.strokeWeight
739
+ });
740
+ options.step(step);
741
+ }
742
+ }
743
+ }
744
+ }
745
+ });
746
+ }
747
+ else if (options.route) {
748
+ if (options.route.legs.length > 0) {
749
+ var steps = options.route.legs[0].steps;
750
+ for (var i=0, step; step=steps[i]; i++) {
751
+ step.step_number = i;
752
+ self.drawPolyline({
753
+ path: step.path,
754
+ strokeColor: options.strokeColor,
755
+ strokeOpacity: options.strokeOpacity,
756
+ strokeWeight: options.strokeWeight
757
+ });
758
+ options.step(step);
759
+ }
760
+ }
761
+ }
762
+ };
763
+
764
+ // Geofence
765
+ this.checkGeofence = function(lat, lng, fence) {
766
+ return fence.containsLatLng(new google.maps.LatLng(lat, lng));
767
+ };
768
+
769
+ this.checkMarkerGeofence = function(marker, outside_callback) {
770
+ if (marker.fences) {
771
+ for (var i=0, fence; fence=marker.fences[i]; i++) {
772
+ var pos = marker.getPosition();
773
+ if (!self.checkGeofence(pos.lat(), pos.lng(), fence)) {
774
+ outside_callback(marker, fence);
775
+ }
776
+ }
777
+ }
778
+ };
779
+ };
780
+
781
+ GMaps.Route = function(options) {
782
+ this.map = options.map;
783
+ this.route = options.route;
784
+ this.step_count = 0;
785
+ this.steps = this.route.legs[0].steps;
786
+ this.steps_length = this.steps.length;
787
+
788
+ this.polyline = this.map.drawPolyline({
789
+ path: new google.maps.MVCArray(),
790
+ strokeColor: options.strokeColor,
791
+ strokeOpacity: options.strokeOpacity,
792
+ strokeWeight: options.strokeWeight
793
+ }).getPath();
794
+
795
+ this.back = function() {
796
+ if (this.step_count > 0) {
797
+ this.step_count--;
798
+ var path = this.route.legs[0].steps[this.step_count].path;
799
+ for (var p in path){
800
+ if (path.hasOwnProperty(p)){
801
+ this.polyline.pop();
802
+ }
803
+ }
804
+ }
805
+ };
806
+
807
+ this.forward = function() {
808
+ if (this.step_count < this.steps_length) {
809
+ var path = this.route.legs[0].steps[this.step_count].path;
810
+ for (var p in path){
811
+ if (path.hasOwnProperty(p)){
812
+ this.polyline.push(path[p]);
813
+ }
814
+ }
815
+ this.step_count++;
816
+ }
817
+ };
818
+ };
819
+
820
+ // Geolocation (Modern browsers only)
821
+ GMaps.geolocate = function(options) {
822
+ if (navigator.geolocation) {
823
+ navigator.geolocation.getCurrentPosition(function(position) {
824
+ options.success(position);
825
+ if (options.always) {
826
+ options.always();
827
+ }
828
+ }, function(error) {
829
+ options.error(error);
830
+ if (options.always) {
831
+ options.always();
832
+ }
833
+ }, options.options);
834
+ }
835
+ else {
836
+ options.not_supported();
837
+ if (options.always) {
838
+ options.always();
839
+ }
840
+ }
841
+ };
842
+
843
+ // Geocoding
844
+ GMaps.geocode = function(options) {
845
+ this.geocoder = new google.maps.Geocoder();
846
+ var callback = options.callback;
847
+ if (options.lat && options.lng) {
848
+ options.latLng = new google.maps.LatLng(options.lat, options.lng);
849
+ }
850
+
851
+ delete options.lat;
852
+ delete options.lng;
853
+ delete options.callback;
854
+ this.geocoder.geocode(options, function(results, status) {
855
+ callback(results, status);
856
+ });
857
+ };
858
+
859
+ // Static maps
860
+ GMaps.staticMapURL = function(options){
861
+ var parameters = [];
862
+ var data;
863
+
864
+ var static_root = 'http://maps.googleapis.com/maps/api/staticmap';
865
+ if (options.url){
866
+ static_root = options.url;
867
+ delete options.url;
868
+ }
869
+ static_root += '?';
870
+
871
+ var markers = options.markers;
872
+ delete options.markers;
873
+ if (!markers && options.marker){
874
+ markers = [options.marker];
875
+ delete options.marker;
876
+ }
877
+
878
+ var polyline = options.polyline;
879
+ delete options.polyline;
880
+
881
+ /** Map options **/
882
+ if (options.center){
883
+ parameters.push('center=' + options.center);
884
+ delete options.center;
885
+ }
886
+ else if (options.address){
887
+ parameters.push('center=' + options.address);
888
+ delete options.address;
889
+ }
890
+ else if (options.lat){
891
+ parameters.push(['center=', options.lat, ',', options.lng].join(''));
892
+ delete options.lat;
893
+ delete options.lng;
894
+ }
895
+ else if (options.visible){
896
+ var visible = encodeURI(options.visible.join('|'));
897
+ parameters.push('visible=' + visible);
898
+ }
899
+
900
+ var size = options.size;
901
+ if (size){
902
+ if (size.join){
903
+ size = size.join('x');
904
+ }
905
+ delete options.size;
906
+ }
907
+ else {
908
+ size = '630x300';
909
+ }
910
+ parameters.push('size=' + size);
911
+
912
+ if (!options.zoom){
913
+ options.zoom = 15;
914
+ }
915
+
916
+ var sensor = options.hasOwnProperty('sensor') ? !!options.sensor : true;
917
+ delete options.sensor;
918
+ parameters.push('sensor=' + sensor);
919
+
920
+ for (var param in options){
921
+ if (options.hasOwnProperty(param)){
922
+ parameters.push(param + '=' + options[param]);
923
+ }
924
+ }
925
+
926
+ /** Markers **/
927
+ if (markers){
928
+ var marker, loc;
929
+
930
+ for (var i=0; data=markers[i]; i++){
931
+ marker = [];
932
+
933
+ if (data.size && data.size !== 'normal'){
934
+ marker.push('size:' + data.size);
935
+ }
936
+ else if (data.icon){
937
+ marker.push('icon:' + encodeURI(data.icon));
938
+ }
939
+
940
+ if (data.color){
941
+ marker.push('color:' + data.color.replace('#', '0x'));
942
+ }
943
+
944
+ if (data.label){
945
+ marker.push('label:' + data.label[0].toUpperCase());
946
+ }
947
+
948
+ loc = (data.address ? data.address : data.lat + ',' + data.lng);
949
+
950
+ if (marker.length || i === 0){
951
+ marker.push(loc);
952
+ marker = marker.join('|');
953
+ parameters.push('markers=' + encodeURI(marker));
954
+ }
955
+ // New marker without styles
956
+ else {
957
+ marker = parameters.pop() + encodeURI('|' + loc);
958
+ parameters.push(marker);
959
+ }
960
+ }
961
+ }
962
+
963
+ /** Polylines **/
964
+ function parseColor(color, opacity){
965
+ if (color[0] === '#'){
966
+ color = color.replace('#', '0x');
967
+
968
+ if (opacity){
969
+ opacity = parseFloat(opacity);
970
+ opacity = Math.min(1, Math.max(opacity, 0));
971
+ if (opacity === 0){
972
+ return '0x00000000';
973
+ }
974
+ opacity = (opacity * 255).toString(16);
975
+ if (opacity.length === 1){
976
+ opacity += opacity;
977
+ }
978
+
979
+ color = color.slice(0,8) + opacity;
980
+ }
981
+ }
982
+ return color;
983
+ }
984
+
985
+ if (polyline){
986
+ data = polyline;
987
+ polyline = [];
988
+
989
+ if (data.strokeWeight){
990
+ polyline.push('weight:' + parseInt(data.strokeWeight, 10));
991
+ }
992
+
993
+ if (data.strokeColor){
994
+ var color = parseColor(data.strokeColor, data.strokeOpacity);
995
+ polyline.push('color:' + color);
996
+ }
997
+
998
+ if (data.fillColor){
999
+ var fillcolor = parseColor(data.fillColor, data.fillOpacity);
1000
+ polyline.push('fillcolor:' + fillcolor);
1001
+ }
1002
+
1003
+ var path = data.path;
1004
+ if (path.join){
1005
+ for (var j=0, pos; pos=path[j]; j++){
1006
+ polyline.push(pos.join(','));
1007
+ }
1008
+ }
1009
+ else {
1010
+ polyline.push('enc:' + path);
1011
+ }
1012
+
1013
+ polyline = polyline.join('|');
1014
+ parameters.push('path=' + encodeURI(polyline));
1015
+ }
1016
+
1017
+ parameters = parameters.join('&');
1018
+ return static_root + parameters;
1019
+ };
1020
+
1021
+ //==========================
1022
+ // Polygon containsLatLng
1023
+ // https://github.com/tparkin/Google-Maps-Point-in-Polygon
1024
+ // Poygon getBounds extension - google-maps-extensions
1025
+ // http://code.google.com/p/google-maps-extensions/source/browse/google.maps.Polygon.getBounds.js
1026
+ if (!google.maps.Polygon.prototype.getBounds) {
1027
+ google.maps.Polygon.prototype.getBounds = function(latLng) {
1028
+ var bounds = new google.maps.LatLngBounds();
1029
+ var paths = this.getPaths();
1030
+ var path;
1031
+
1032
+ for (var p = 0; p < paths.getLength(); p++) {
1033
+ path = paths.getAt(p);
1034
+ for (var i = 0; i < path.getLength(); i++) {
1035
+ bounds.extend(path.getAt(i));
1036
+ }
1037
+ }
1038
+
1039
+ return bounds;
1040
+ };
1041
+ }
1042
+
1043
+ // Polygon containsLatLng - method to determine if a latLng is within a polygon
1044
+ google.maps.Polygon.prototype.containsLatLng = function(latLng) {
1045
+ // Exclude points outside of bounds as there is no way they are in the poly
1046
+ var bounds = this.getBounds();
1047
+
1048
+ if (bounds !== null && !bounds.contains(latLng)) {
1049
+ return false;
1050
+ }
1051
+
1052
+ // Raycast point in polygon method
1053
+ var inPoly = false;
1054
+
1055
+ var numPaths = this.getPaths().getLength();
1056
+ for (var p = 0; p < numPaths; p++) {
1057
+ var path = this.getPaths().getAt(p);
1058
+ var numPoints = path.getLength();
1059
+ var j = numPoints - 1;
1060
+
1061
+ for (var i = 0; i < numPoints; i++) {
1062
+ var vertex1 = path.getAt(i);
1063
+ var vertex2 = path.getAt(j);
1064
+
1065
+ if (vertex1.lng() < latLng.lng() && vertex2.lng() >= latLng.lng() || vertex2.lng() < latLng.lng() && vertex1.lng() >= latLng.lng()) {
1066
+ if (vertex1.lat() + (latLng.lng() - vertex1.lng()) / (vertex2.lng() - vertex1.lng()) * (vertex2.lat() - vertex1.lat()) < latLng.lat()) {
1067
+ inPoly = !inPoly;
1068
+ }
1069
+ }
1070
+
1071
+ j = i;
1072
+ }
1073
+ }
1074
+
1075
+ return inPoly;
1076
+ };
1077
+
1078
+ google.maps.LatLngBounds.prototype.containsLatLng = function(latLng) {
1079
+ return this.contains(latLng);
1080
+ };
1081
+
1082
+ google.maps.Marker.prototype.setFences = function(fences) {
1083
+ this.fences = fences;
1084
+ };
1085
+
1086
+ google.maps.Marker.prototype.addFence = function(fence) {
1087
+ this.fences.push(fence);
1088
+ };
1089
+
1090
+ return GMaps;
1091
+ }(jQuery || $));