jquery-gmap3-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jquery-gmap3-rails.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Erick Dennis
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,46 @@
1
+ # Jquery::Gmap3::Rails
2
+
3
+ This gem provides your Rails 3 application with Jean-Baptiste DEMONTE's [GMAP3 Plugin for JQuery](http://gmap3.net/).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'jquery-gmap3-rails'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install jquery-gmap3-rails
18
+
19
+ ## Usage
20
+
21
+ If you're using rails 3.0 or are using rails 3.1 and above and have the asset pipeline disabled you can also use this
22
+ generator to install the static files:
23
+
24
+ $ rails g jquery:gmap3:install
25
+
26
+ Once you have the libraries installed you will first need to include the google maps API in your application layout:
27
+
28
+ javascript_include_tag "http://maps.googleapis.com/maps/api/js?sensor=true"
29
+
30
+ Then you have to load the GMAP3 library by adding the following line to your app/assets/javascripts/application.js:
31
+
32
+ //= require gmap3
33
+
34
+ After that it's as simple as calling the gmap3() method on the div that you want to display the map in:
35
+
36
+ $("#map_canvas").gmap3();
37
+
38
+ For further details on the usage of GMAP3 please see the [documentation](http://gmap3.net/documentation.html).
39
+
40
+ ## Contributing
41
+
42
+ 1. Fork it
43
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
44
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
45
+ 4. Push to the branch (`git push origin my-new-feature`)
46
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/jquery/gmap3/rails/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Erick Dennis"]
6
+ gem.email = ["erick.dennis@gmail.com"]
7
+ gem.description = "This gem provides your Rails 3 application with the GMAP3 Plugin for JQuery."
8
+ gem.summary = "Use GMAP3 Plugin for JQuery with Rails 3"
9
+ gem.homepage = "http://github.com/erickd/jquery-gmap3-rails"
10
+
11
+ # todo: add the correct jquery-rails version
12
+ gem.add_dependency "jquery-rails", ">= 1.0"
13
+
14
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ gem.name = "jquery-gmap3-rails"
18
+ gem.require_paths = ["lib"]
19
+ gem.version = Jquery::Gmap3::Rails::VERSION
20
+ end
@@ -0,0 +1,41 @@
1
+ require 'rails'
2
+
3
+ # Supply generator for Rails 3.0.x or if asset pipeline is not enabled
4
+ if ::Rails.version < "3.1" || !::Rails.application.config.assets.enabled
5
+ module Jquery
6
+ module Gmap3
7
+ module Generators
8
+ class InstallGenerator < ::Rails::Generators::Base
9
+
10
+ desc "This generator installs the GMAP3 Plugin for JQuery #{Jquery::Gmap3::Rails::JQUERY_GMAP3_VERSION}"
11
+ source_root File.expand_path('../../../../../vendor/assets/javascripts', __FILE__)
12
+
13
+ def copy_gmap3
14
+ say_status("copying", "GMAP3 Plugin for jQuery (#{Jquery::Gmap3::Rails::JQUERY_GMAP3_VERSION})", :green)
15
+ copy_file "gmap3.js", "public/javascripts/gmap3.js"
16
+ copy_file "gmap3.min.js", "public/javascripts/gmap3.min.js"
17
+ end
18
+
19
+ end
20
+ end
21
+ end
22
+ end
23
+ else
24
+ module Jquery
25
+ module Gmap3
26
+ module Generators
27
+ class InstallGenerator < ::Rails::Generators::Base
28
+ desc "Show instructions so people will know what to do when mistakenly using generator for Rails 3.1 apps"
29
+
30
+ def do_nothing
31
+ say_status("deprecated", "You are using Rails #{::Rails.version} with the asset pipeline enabled, so this generator is not needed.")
32
+ say_status("", "The necessary files are already in your asset pipeline.")
33
+ say_status("", "Just add `//= require gmap3` to your app/assets/javascripts/application.js")
34
+ say_status("", "If you upgraded your app from Rails 3.0 and still have gmap3.js in your javascripts, be sure to remove it.")
35
+ say_status("", "If you do not want the asset pipeline enabled, you may turn it off in application.rb and re-run this generator.")
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1 @@
1
+ require "jquery/gmap3/rails"
@@ -0,0 +1,13 @@
1
+ require "jquery/gmap3/rails/version"
2
+
3
+ if defined? Rails && Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR >= 1
4
+ require "jquery/gmap3/rails/engine"
5
+ end
6
+
7
+ module Jquery
8
+ module Gmap3
9
+ module Rails
10
+ # Your code goes here...
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ module Jquery
2
+ module Gmap3
3
+ module Rails
4
+ class Engine < ::Rails::Engine
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ module Jquery
2
+ module Gmap3
3
+ module Rails
4
+ VERSION = "0.1.0"
5
+ JQUERY_GMAP3_VERSION = "4.1"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,2222 @@
1
+ /*
2
+ * GMAP3 Plugin for JQuery
3
+ * Version : 4.1
4
+ * Date : 2011-11-18
5
+ * Licence : GPL v3 : http://www.gnu.org/licenses/gpl.html
6
+ * Author : DEMONTE Jean-Baptiste
7
+ * Contact : jbdemonte@gmail.com
8
+ * Web site : http://gmap3.net
9
+ *
10
+ * Copyright (c) 2010-2011 Jean-Baptiste DEMONTE
11
+ * All rights reserved.
12
+ *
13
+ * Redistribution and use in source and binary forms, with or without
14
+ * modification, are permitted provided that the following conditions are met:
15
+ *
16
+ * - Redistributions of source code must retain the above copyright
17
+ * notice, this list of conditions and the following disclaimer.
18
+ * - Redistributions in binary form must reproduce the above
19
+ * copyright notice, this list of conditions and the following
20
+ * disclaimer in the documentation and/or other materials provided
21
+ * with the distribution.
22
+ * - Neither the name of the author nor the names of its contributors
23
+ * may be used to endorse or promote products derived from this
24
+ * software without specific prior written permission.
25
+ *
26
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
27
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
30
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36
+ * POSSIBILITY OF SUCH DAMAGE.
37
+ */
38
+
39
+ (function ($) {
40
+
41
+ /***************************************************************************/
42
+ /* STACK */
43
+ /***************************************************************************/
44
+ function Stack (){
45
+ var st = [];
46
+ this.empty = function (){
47
+ for(var i = 0; i < st.length; i++){
48
+ if (st[i]){
49
+ return false
50
+ }
51
+ }
52
+ return true;
53
+ }
54
+ this.add = function(v){
55
+ st.push(v);
56
+ }
57
+ this.addNext = function ( v){
58
+ var t=[], i, k = 0;
59
+ for(i = 0; i < st.length; i++){
60
+ if (!st[i]){
61
+ continue;
62
+ }
63
+ if (k == 1) {
64
+ t.push(v);
65
+ }
66
+ t.push(st[i]);
67
+ k++;
68
+ }
69
+ if (k < 2) {
70
+ t.push(v);
71
+ }
72
+ st = t;
73
+ }
74
+ this.get = function (){
75
+ for(var i = 0; i < st.length; i++){
76
+ if (st[i]) {
77
+ return st[i];
78
+ }
79
+ }
80
+ return false;
81
+ }
82
+ this.ack = function (){
83
+ for(var i = 0; i < st.length; i++){
84
+ if (st[i]) {
85
+ delete st[i];
86
+ break;
87
+ }
88
+ }
89
+ if (this.empty()){
90
+ st = [];
91
+ }
92
+ }
93
+ }
94
+
95
+ /***************************************************************************/
96
+ /* STORE */
97
+ /***************************************************************************/
98
+ function Store(){
99
+ var store = {};
100
+
101
+ /**
102
+ * add a mixed to the store
103
+ **/
104
+ this.add = function(name, obj, todo){
105
+ name = name.toLowerCase();
106
+ if (!store[name]){
107
+ store[name] = [];
108
+ }
109
+ store[name].push({obj:obj, tag:ival(todo, 'tag')});
110
+ return name + '-' + (store[name].length-1);
111
+ }
112
+
113
+ /**
114
+ * return a stored mixed
115
+ **/
116
+ this.get = function(name, last, tag){
117
+ var i, idx, add;
118
+ name = name.toLowerCase();
119
+ if (!store[name] || !store[name].length){
120
+ return null;
121
+ }
122
+ idx = last ? store[name].length : -1;
123
+ add = last ? -1 : 1;
124
+ for(i=0; i<store[name].length; i++){
125
+ idx += add;
126
+ if (store[name][idx]){
127
+ if (tag !== undefined) {
128
+ if ( (store[name][idx].tag === undefined) || ($.inArray(store[name][idx].tag, tag) < 0) ){
129
+ continue;
130
+ }
131
+ }
132
+ return store[name][idx].obj;
133
+ }
134
+ }
135
+ return null;
136
+ }
137
+
138
+ /**
139
+ * return all stored mixed
140
+ **/
141
+ this.all = function(name, tag){
142
+ var i, result = [];
143
+ name = name.toLowerCase();
144
+ if (!store[name] || !store[name].length){
145
+ return result;
146
+ }
147
+ for(i=0; i<store[name].length; i++){
148
+ if (!store[name][i]){
149
+ continue;
150
+ }
151
+ if ( (tag !== undefined) && ( (store[name][i].tag === undefined) || ($.inArray(store[name][i].tag, tag) < 0) ) ){
152
+ continue;
153
+ }
154
+ result.push(store[name][i].obj);
155
+ }
156
+ return result;
157
+ }
158
+
159
+ /**
160
+ * return all storation groups
161
+ **/
162
+ this.names = function(){
163
+ var name, result = [];
164
+ for(name in store){
165
+ result.push(name);
166
+ }
167
+ return result;
168
+ }
169
+
170
+ /**
171
+ * return an object from its reference
172
+ **/
173
+ this.refToObj = function(ref){
174
+ ref = ref.split('-'); // name - idx
175
+ if ((ref.length == 2) && store[ref[0]] && store[ref[0]][ref[1]]){
176
+ return store[ref[0]][ref[1]].obj;
177
+ }
178
+ return null;
179
+ }
180
+
181
+ /**
182
+ * remove one object from the store
183
+ **/
184
+ this.rm = function(name, tag, pop){
185
+ var idx, i, tmp;
186
+ name = name.toLowerCase();
187
+ if (!store[name]) {
188
+ return false;
189
+ }
190
+ if (tag !== undefined){
191
+ if (pop){
192
+ for(idx = store[name].length - 1; idx >= 0; idx--){
193
+ if ( (store[name][idx] !== undefined) && (store[name][idx].tag !== undefined) && ($.inArray(store[name][idx].tag, tag) >= 0) ){
194
+ break;
195
+ }
196
+ }
197
+ } else {
198
+ for(idx = 0; idx < store[name].length; idx++){
199
+ if ( (store[name][idx] !== undefined) && (store[name][idx].tag !== undefined) && ($.inArray(store[name][idx].tag, tag) >= 0) ){
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ } else {
205
+ idx = pop ? store[name].length - 1 : 0;
206
+ }
207
+ if ( !(idx in store[name]) ) {
208
+ return false;
209
+ }
210
+ // Google maps element
211
+ if (typeof(store[name][idx].obj.setMap) === 'function') {
212
+ store[name][idx].obj.setMap(null);
213
+ }
214
+ // jQuery
215
+ if (typeof(store[name][idx].obj.remove) === 'function') {
216
+ store[name][idx].obj.remove();
217
+ }
218
+ // internal (cluster)
219
+ if (typeof(store[name][idx].obj.free) === 'function') {
220
+ store[name][idx].obj.free();
221
+ }
222
+ delete store[name][idx].obj;
223
+ if (tag !== undefined){
224
+ tmp = [];
225
+ for(i=0; i<store[name].length; i++){
226
+ if (i !== idx){
227
+ tmp.push(store[name][i]);
228
+ }
229
+ }
230
+ store[name] = tmp;
231
+ } else {
232
+ if (pop) {
233
+ store[name].pop();
234
+ } else {
235
+ store[name].shift();
236
+ }
237
+ }
238
+ return true;
239
+ }
240
+
241
+ /**
242
+ * remove objects from the store
243
+ **/
244
+ this.clear = function(list, last, first, tag){
245
+ var k, i, name;
246
+ if (!list || !list.length){
247
+ list = [];
248
+ for(k in store){
249
+ list.push(k);
250
+ }
251
+ } else {
252
+ list = array(list);
253
+ }
254
+ for(i=0; i<list.length; i++){
255
+ if (list[i]){
256
+ name = list[i].toLowerCase();
257
+ if (!store[name]){
258
+ continue;
259
+ }
260
+ if (last){
261
+ this.rm(name, tag, true);
262
+ } else if (first){
263
+ this.rm(name, tag, false);
264
+ } else {
265
+ // all
266
+ while (this.rm(name, tag, false));
267
+ }
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ /***************************************************************************/
274
+ /* CLUSTERER */
275
+ /***************************************************************************/
276
+
277
+ function Clusterer(){
278
+ var markers = [], events=[], stored=[], latest=[], redrawing = false, redraw;
279
+
280
+ this.events = function(){
281
+ for(var i=0; i<arguments.length; i++){
282
+ events.push(arguments[i]);
283
+ }
284
+ }
285
+
286
+ this.startRedraw = function(){
287
+ if (!redrawing){
288
+ redrawing = true;
289
+ return true;
290
+ }
291
+ return false;
292
+ }
293
+
294
+ this.endRedraw = function(){
295
+ redrawing = false;
296
+ }
297
+
298
+ this.redraw = function(){
299
+ var i, args = [], that = this;
300
+ for(i=0; i<arguments.length; i++){
301
+ args.push(arguments[i]);
302
+ }
303
+ if (this.startRedraw){
304
+ redraw.apply(that, args);
305
+ this.endRedraw();
306
+ } else {
307
+ setTimeout(function(){
308
+ that.redraw.apply(that, args);
309
+ },
310
+ 50
311
+ );
312
+ }
313
+ };
314
+
315
+ this.setRedraw = function(fnc){
316
+ redraw = fnc;
317
+ }
318
+
319
+ this.store = function(data, obj, shadow){
320
+ stored.push({data:data, obj:obj, shadow:shadow});
321
+ }
322
+
323
+ this.free = function(){
324
+ for(var i = 0; i < events.length; i++){
325
+ google.maps.event.removeListener(events[i]);
326
+ }
327
+ events=[];
328
+ this.freeAll();
329
+ }
330
+
331
+ this.freeIndex = function(i){
332
+ if (typeof(stored[i].obj.setMap) === 'function') {
333
+ stored[i].obj.setMap(null);
334
+ }
335
+ if (typeof(stored[i].obj.remove) === 'function') {
336
+ stored[i].obj.remove();
337
+ }
338
+ if (stored[i].shadow){ // only overlays has shadow
339
+ if (typeof(stored[i].shadow.remove) === 'function') {
340
+ stored[i].obj.remove();
341
+ }
342
+ if (typeof(stored[i].shadow.setMap) === 'function') {
343
+ stored[i].shadow.setMap(null);
344
+ }
345
+ delete stored[i].shadow;
346
+ }
347
+ delete stored[i].obj;
348
+ delete stored[i].data;
349
+ delete stored[i];
350
+ }
351
+
352
+ this.freeAll = function(){
353
+ var i;
354
+ for(i = 0; i < stored.length; i++){
355
+ if (stored[i]) {
356
+ this.freeIndex(i);
357
+ }
358
+ }
359
+ stored = [];
360
+ }
361
+
362
+ this.freeDiff = function(clusters){
363
+ var i, j, same = {}, idx = [];
364
+ for(i=0; i<clusters.length; i++){
365
+ idx.push( clusters[i].idx.join('-') );
366
+ }
367
+ for(i = 0; i < stored.length; i++){
368
+ if (!stored[i]) {
369
+ continue;
370
+ }
371
+ j = $.inArray(stored[i].data.idx.join('-'), idx);
372
+ if (j >= 0){
373
+ same[j] = true;
374
+ } else {
375
+ this.freeIndex(i);
376
+ }
377
+ }
378
+ return same;
379
+ }
380
+
381
+ this.add = function(latLng, marker){
382
+ markers.push({latLng:latLng, marker:marker});
383
+ }
384
+
385
+ this.get = function(i){
386
+ return markers[i];
387
+ }
388
+
389
+ this.clusters = function(map, radius, maxZoom, force){
390
+ var proj = map.getProjection(),
391
+ nwP = proj.fromLatLngToPoint(
392
+ new google.maps.LatLng(
393
+ map.getBounds().getNorthEast().lat(),
394
+ map.getBounds().getSouthWest().lng()
395
+ )
396
+ ),
397
+ i, j, j2, p, x, y, k, k2,
398
+ z = map.getZoom(),
399
+ pos = {},
400
+ saved = {},
401
+ unik = {},
402
+ clusters = [],
403
+ cluster,
404
+ chk,
405
+ lat, lng, keys, cnt,
406
+ bounds = map.getBounds(),
407
+ noClusters = maxZoom && (maxZoom <= map.getZoom()),
408
+ chkContain = map.getZoom() > 2;
409
+
410
+ cnt = 0;
411
+ keys = {};
412
+ for(i = 0; i < markers.length; i++){
413
+ if (chkContain && !bounds.contains(markers[i].latLng)){
414
+ continue;
415
+ }
416
+ p = proj.fromLatLngToPoint(markers[i].latLng);
417
+ pos[i] = [
418
+ Math.floor((p.x - nwP.x) * Math.pow(2, z)),
419
+ Math.floor((p.y - nwP.y) * Math.pow(2, z))
420
+ ];
421
+ keys[i] = true;
422
+ cnt++;
423
+ }
424
+ // check if visible markers have changed
425
+ if (!force && !noClusters){
426
+ for(k = 0; k < latest.length; k++){
427
+ if( k in keys ){
428
+ cnt--;
429
+ } else {
430
+ break;
431
+ }
432
+ }
433
+ if (!cnt){
434
+ return false; // no change
435
+ }
436
+ }
437
+
438
+ // save current keys to check later if an update has been done
439
+ latest = keys;
440
+
441
+ keys = [];
442
+ for(i in pos){
443
+ x = pos[i][0];
444
+ y = pos[i][1];
445
+ if ( !(x in saved) ){
446
+ saved[x] = {};
447
+ }
448
+ if (!( y in saved[x]) ) {
449
+ saved[x][y] = i;
450
+ unik[i] = {};
451
+ keys.push(i);
452
+ }
453
+ unik[ saved[x][y] ][i] = true;
454
+ }
455
+ radius = Math.pow(radius, 2);
456
+ delete(saved);
457
+
458
+ k = 0;
459
+ while(1){
460
+ while((k <keys.length) && !(keys[k] in unik)){
461
+ k++;
462
+ }
463
+ if (k == keys.length){
464
+ break;
465
+ }
466
+ i = keys[k];
467
+ lat = pos[i][0];
468
+ lng = pos[i][1];
469
+ saved = null;
470
+
471
+
472
+ if (noClusters){
473
+ saved = {lat:lat, lng:lng, idx:[i]};
474
+ } else {
475
+ do{
476
+ cluster = {lat:0, lng:0, idx:[]};
477
+ for(k2 = k; k2<keys.length; k2++){
478
+ if (!(keys[k2] in unik)){
479
+ continue;
480
+ }
481
+ j = keys[k2];
482
+ if ( Math.pow(lat - pos[j][0], 2) + Math.pow(lng-pos[j][1], 2) <= radius ){
483
+ for(j2 in unik[j]){
484
+ cluster.lat += markers[j2].latLng.lat();
485
+ cluster.lng += markers[j2].latLng.lng();
486
+ cluster.idx.push(j2);
487
+ }
488
+ }
489
+ }
490
+ cluster.lat /= cluster.idx.length;
491
+ cluster.lng /= cluster.idx.length;
492
+ if (!saved){
493
+ chk = cluster.idx.length > 1;
494
+ saved = cluster;
495
+ } else {
496
+ chk = cluster.idx.length > saved.idx.length;
497
+ if (chk){
498
+ saved = cluster;
499
+ }
500
+ }
501
+ if (chk){
502
+ p = proj.fromLatLngToPoint( new google.maps.LatLng(saved.lat, saved.lng) );
503
+ lat = Math.floor((p.x - nwP.x) * Math.pow(2, z));
504
+ lng = Math.floor((p.y - nwP.y) * Math.pow(2, z));
505
+ }
506
+ } while(chk);
507
+ }
508
+
509
+ for(k2 = 0; k2 < saved.idx.length; k2++){
510
+ if (saved.idx[k2] in unik){
511
+ delete(unik[saved.idx[k2]]);
512
+ }
513
+ }
514
+ clusters.push(saved);
515
+ }
516
+ return clusters;
517
+ }
518
+
519
+ this.getBounds = function(){
520
+ var i, bounds = new google.maps.LatLngBounds();
521
+ for(i=0; i<markers.length; i++){
522
+ bounds.extend(markers[i].latLng);
523
+ }
524
+ return bounds;
525
+ }
526
+ }
527
+
528
+ /***************************************************************************/
529
+ /* GMAP3 GLOBALS */
530
+ /***************************************************************************/
531
+
532
+ var _default = {
533
+ verbose:false,
534
+ queryLimit:{
535
+ attempt:5,
536
+ delay:250, // setTimeout(..., delay + random);
537
+ random:250
538
+ },
539
+ init:{
540
+ mapTypeId : google.maps.MapTypeId.ROADMAP,
541
+ center:[46.578498,2.457275],
542
+ zoom: 2
543
+ },
544
+ classes:{
545
+ Map : google.maps.Map,
546
+ Marker : google.maps.Marker,
547
+ InfoWindow : google.maps.InfoWindow,
548
+ Circle : google.maps.Circle,
549
+ Rectangle : google.maps.Rectangle,
550
+ OverlayView : google.maps.OverlayView,
551
+ StreetViewPanorama: google.maps.StreetViewPanorama,
552
+ KmlLayer : google.maps.KmlLayer,
553
+ TrafficLayer : google.maps.TrafficLayer,
554
+ BicyclingLayer : google.maps.BicyclingLayer,
555
+ GroundOverlay : google.maps.GroundOverlay,
556
+ StyledMapType : google.maps.StyledMapType
557
+ }
558
+ },
559
+ _properties = ['events','onces','options','apply', 'callback', 'data', 'tag'],
560
+ _noInit = ['init', 'geolatlng', 'getlatlng', 'getroute', 'getelevation', 'getdistance', 'addstyledmap', 'setdefault', 'destroy'],
561
+ _directs = ['get'],
562
+ geocoder = directionsService = elevationService = maxZoomService = distanceMatrixService = null;
563
+
564
+ function setDefault(values){
565
+ for(var k in values){
566
+ if (typeof(_default[k]) === 'object'){
567
+ _default[k] = $.extend({}, _default[k], values[k]);
568
+ } else {
569
+ _default[k] = values[k];
570
+ }
571
+ }
572
+ }
573
+
574
+ function autoInit(iname){
575
+ if (!iname){
576
+ return true;
577
+ }
578
+ for(var i = 0; i < _noInit.length; i++){
579
+ if (_noInit[i] === iname) {
580
+ return false;
581
+ }
582
+ }
583
+ return true;
584
+ }
585
+
586
+
587
+ /**
588
+ * return true if action has to be executed directly
589
+ **/
590
+ function isDirect (todo){
591
+ var action = ival(todo, 'action');
592
+ for(var i = 0; i < _directs.length; i++){
593
+ if (_directs[i] === action) {
594
+ return true;
595
+ }
596
+ }
597
+ return false;
598
+ }
599
+
600
+ //-----------------------------------------------------------------------//
601
+ // Objects tools
602
+ //-----------------------------------------------------------------------//
603
+
604
+ /**
605
+ * return the real key by an insensitive seach
606
+ **/
607
+ function ikey (object, key){
608
+ if (key.toLowerCase){
609
+ key = key.toLowerCase();
610
+ for(var k in object){
611
+ if (k.toLowerCase && (k.toLowerCase() == key)) {
612
+ return k;
613
+ }
614
+ }
615
+ }
616
+ return false;
617
+ }
618
+
619
+ /**
620
+ * return the value of real key by an insensitive seach
621
+ **/
622
+ function ival (object, key, def){
623
+ var k = ikey(object, key);
624
+ return k ? object[k] : def;
625
+ }
626
+
627
+ /**
628
+ * return true if at least one key is set in object
629
+ * nb: keys in lowercase
630
+ **/
631
+ function hasKey (object, keys){
632
+ var n, k;
633
+ if (!object || !keys) {
634
+ return false;
635
+ }
636
+ keys = array(keys);
637
+ for(n in object){
638
+ if (n.toLowerCase){
639
+ n = n.toLowerCase();
640
+ for(k in keys){
641
+ if (n == keys[k]) {
642
+ return true;
643
+ }
644
+ }
645
+ }
646
+ }
647
+ return false;
648
+ }
649
+
650
+ /**
651
+ * return a standard object
652
+ * nb: include in lowercase
653
+ **/
654
+ function extractObject (todo, include, result/* = {} */){
655
+ if (hasKey(todo, _properties) || hasKey(todo, include)){ // #1 classical object definition
656
+ var i, k;
657
+ // get defined properties values from todo
658
+ for(i=0; i<_properties.length; i++){
659
+ k = ikey(todo, _properties[i]);
660
+ result[ _properties[i] ] = k ? todo[k] : {};
661
+ }
662
+ if (include && include.length){
663
+ for(i=0; i<include.length; i++){
664
+ if(k = ikey(todo, include[i])){
665
+ result[ include[i] ] = todo[k];
666
+ }
667
+ }
668
+ }
669
+ return result;
670
+ } else { // #2 simplified object (all excepted "action" are options properties)
671
+ result.options= {};
672
+ for(k in todo){
673
+ if (k !== 'action'){
674
+ result.options[k] = todo[k];
675
+ }
676
+ }
677
+ return result;
678
+ }
679
+ }
680
+
681
+ /**
682
+ * identify object from object list or parameters list : [ objectName:{data} ] or [ otherObject:{}, ] or [ object properties ]
683
+ * nb: include, exclude in lowercase
684
+ **/
685
+ function getObject(name, todo, include, exclude){
686
+ var iname = ikey(todo, name),
687
+ i, result = {}, keys=['map'];
688
+ // include callback from high level
689
+ result['callback'] = ival(todo, 'callback');
690
+ include = array(include);
691
+ exclude = array(exclude);
692
+ if (iname) {
693
+ return extractObject(todo[iname], include, result);
694
+ }
695
+ if (exclude && exclude.length){
696
+ for(i=0; i<exclude.length; i++) {
697
+ keys.push(exclude[i]);
698
+ }
699
+ }
700
+ if (!hasKey(todo, keys)){
701
+ result = extractObject(todo, include, result);
702
+ }
703
+ // initialize missing properties
704
+ for(i=0; i<_properties.length; i++){
705
+ if (_properties[i] in result){
706
+ continue;
707
+ }
708
+ result[ _properties[i] ] = {};
709
+ }
710
+ return result;
711
+ }
712
+
713
+ //-----------------------------------------------------------------------//
714
+ // Service tools
715
+ //-----------------------------------------------------------------------//
716
+
717
+ function getGeocoder(){
718
+ if (!geocoder) {
719
+ geocoder = new google.maps.Geocoder();
720
+ }
721
+ return geocoder;
722
+ }
723
+
724
+ function getDirectionsService(){
725
+ if (!directionsService) {
726
+ directionsService = new google.maps.DirectionsService();
727
+ }
728
+ return directionsService;
729
+ }
730
+
731
+ function getElevationService(){
732
+ if (!elevationService) {
733
+ elevationService = new google.maps.ElevationService();
734
+ }
735
+ return elevationService;
736
+ }
737
+
738
+ function getMaxZoomService(){
739
+ if (!maxZoomService) {
740
+ maxZoomService = new google.maps.MaxZoomService();
741
+ }
742
+ return maxZoomService;
743
+ }
744
+
745
+ function getDistanceMatrixService(){
746
+ if (!distanceMatrixService) {
747
+ distanceMatrixService = new google.maps.DistanceMatrixService();
748
+ }
749
+ return distanceMatrixService;
750
+ }
751
+
752
+ //-----------------------------------------------------------------------//
753
+ // Unit tools
754
+ //-----------------------------------------------------------------------//
755
+
756
+ /**
757
+ * return true if mixed is usable as number
758
+ **/
759
+ function numeric(mixed){
760
+ return (typeof(mixed) === 'number' || typeof(mixed) === 'string') && mixed !== '' && !isNaN(mixed);
761
+ }
762
+
763
+ /**
764
+ * convert data to array
765
+ **/
766
+ function array(mixed){
767
+ var k, a = [];
768
+ if (mixed !== undefined){
769
+ if (typeof(mixed) === 'object'){
770
+ if (typeof(mixed.length) === 'number') {
771
+ a = mixed;
772
+ } else {
773
+ for(k in mixed) {
774
+ a.push(mixed[k]);
775
+ }
776
+ }
777
+ } else{
778
+ a.push(mixed);
779
+ }
780
+ }
781
+ return a;
782
+ }
783
+
784
+ /**
785
+ * convert mixed [ lat, lng ] objet to google.maps.LatLng
786
+ **/
787
+ function toLatLng (mixed, emptyReturnMixed, noFlat){
788
+ var empty = emptyReturnMixed ? mixed : null;
789
+ if (!mixed || (typeof(mixed) === 'string')){
790
+ return empty;
791
+ }
792
+ // defined latLng
793
+ if (mixed.latLng) {
794
+ return toLatLng(mixed.latLng);
795
+ }
796
+ // google.maps.LatLng object
797
+ if (typeof(mixed.lat) === 'function') {
798
+ return mixed;
799
+ }
800
+ // {lat:X, lng:Y} object
801
+ else if ( numeric(mixed.lat) ) {
802
+ return new google.maps.LatLng(mixed.lat, mixed.lng);
803
+ }
804
+ // [X, Y] object
805
+ else if ( !noFlat && mixed.length){ // and "no flat" object allowed
806
+ if ( !numeric(mixed[0]) || !numeric(mixed[1]) ) {
807
+ return empty;
808
+ }
809
+ return new google.maps.LatLng(mixed[0], mixed[1]);
810
+ }
811
+ return empty;
812
+ }
813
+
814
+ /**
815
+ * convert mixed [ sw, ne ] object by google.maps.LatLngBounds
816
+ **/
817
+ function toLatLngBounds(mixed, flatAllowed, emptyReturnMixed){
818
+ var ne, sw, empty;
819
+ if (!mixed) {
820
+ return null;
821
+ }
822
+ empty = emptyReturnMixed ? mixed : null;
823
+ if (typeof(mixed.getCenter) === 'function') {
824
+ return mixed;
825
+ }
826
+ if (mixed.length){
827
+ if (mixed.length == 2){
828
+ ne = toLatLng(mixed[0]);
829
+ sw = toLatLng(mixed[1]);
830
+ } else if (mixed.length == 4){
831
+ ne = toLatLng([mixed[0], mixed[1]]);
832
+ sw = toLatLng([mixed[2], mixed[3]]);
833
+ }
834
+ } else {
835
+ if ( ('ne' in mixed) && ('sw' in mixed) ){
836
+ ne = toLatLng(mixed.ne);
837
+ sw = toLatLng(mixed.sw);
838
+ } else if ( ('n' in mixed) && ('e' in mixed) && ('s' in mixed) && ('w' in mixed) ){
839
+ ne = toLatLng([mixed.n, mixed.e]);
840
+ sw = toLatLng([mixed.s, mixed.w]);
841
+ }
842
+ }
843
+ if (ne && sw){
844
+ return new google.maps.LatLngBounds(sw, ne);
845
+ }
846
+ return empty;
847
+ }
848
+
849
+ /***************************************************************************/
850
+ /* GMAP3 */
851
+ /***************************************************************************/
852
+
853
+ function Gmap3($this){
854
+
855
+ var stack = new Stack(),
856
+ store = new Store(),
857
+ map = null,
858
+ styles = {},
859
+ running = false;
860
+
861
+ //-----------------------------------------------------------------------//
862
+ // Stack tools
863
+ //-----------------------------------------------------------------------//
864
+
865
+ /**
866
+ * store actions to execute in a stack manager
867
+ **/
868
+ this._plan = function(list){
869
+ for(var k = 0; k < list.length; k++) {
870
+ stack.add(list[k]);
871
+ }
872
+ this._run();
873
+ }
874
+
875
+ /**
876
+ * store one action to execute in a stack manager after the current
877
+ **/
878
+ this._planNext = function(todo){
879
+ stack.addNext(todo);
880
+ }
881
+
882
+ /**
883
+ * execute action directly
884
+ **/
885
+ this._direct = function(todo){
886
+ var action = ival(todo, 'action');
887
+ return this[action]($.extend({}, action in _default ? _default[action] : {}, todo.args ? todo.args : todo));
888
+ }
889
+
890
+ /**
891
+ * called when action in finished, to acknoledge the current in stack and start next one
892
+ **/
893
+ this._end = function(){
894
+ running = false;
895
+ stack.ack();
896
+ this._run();
897
+ },
898
+ /**
899
+ * if not running, start next action in stack
900
+ **/
901
+ this._run = function(){
902
+ if (running) {
903
+ return;
904
+ }
905
+ var todo = stack.get();
906
+ if (!todo) {
907
+ return;
908
+ }
909
+ running = true;
910
+ this._proceed(todo);
911
+ }
912
+
913
+ //-----------------------------------------------------------------------//
914
+ // Call tools
915
+ //-----------------------------------------------------------------------//
916
+
917
+ /**
918
+ * run the appropriated function
919
+ **/
920
+ this._proceed = function(todo){
921
+ todo = todo || {};
922
+ var action = ival(todo, 'action') || 'init',
923
+ iaction = action.toLowerCase(),
924
+ ok = true,
925
+ target = ival(todo, 'target'),
926
+ args = ival(todo, 'args'),
927
+ out;
928
+ // check if init should be run automatically
929
+ if ( !map && autoInit(iaction) ){
930
+ this.init($.extend({}, _default.init, todo.args && todo.args.map ? todo.args.map : todo.map ? todo.map : {}), true);
931
+ }
932
+
933
+ // gmap3 function
934
+ if (!target && !args && (iaction in this) && (typeof(this[iaction]) === 'function')){
935
+ this[iaction]($.extend({}, iaction in _default ? _default[iaction] : {}, todo.args ? todo.args : todo)); // call fnc and extends defaults data
936
+ } else {
937
+ // "target" object function
938
+ if (target && (typeof(target) === 'object')){
939
+ if (ok = (typeof(target[action]) === 'function')){
940
+ out = target[action].apply(target, todo.args ? todo.args : []);
941
+ }
942
+ // google.maps.Map direct function : no result so not rewrited, directly wrapped using array "args" as parameters (ie. setOptions, addMapType, ...)
943
+ } else if (map){
944
+ if (ok = (typeof(map[action]) === 'function')){
945
+ out = map[action].apply(map, todo.args ? todo.args : [] );
946
+ }
947
+ }
948
+ if (!ok && _default.verbose) {
949
+ alert("unknown action : " + action);
950
+ }
951
+ this._callback(out, todo);
952
+ this._end();
953
+ }
954
+ }
955
+
956
+ /**
957
+ * returns the geographical coordinates from an address and call internal or given method
958
+ **/
959
+ this._resolveLatLng = function(todo, method, all, attempt){
960
+ var address = ival(todo, 'address'),
961
+ params,
962
+ that = this,
963
+ fnc = typeof(method) === 'function' ? method : that[method];
964
+ if ( address ){
965
+ if (!attempt){ // convert undefined to int
966
+ attempt = 0;
967
+ }
968
+ if (typeof(address) === 'object'){
969
+ params = address;
970
+ } else {
971
+ params = {'address': address};
972
+ }
973
+ getGeocoder().geocode(
974
+ params,
975
+ function(results, status) {
976
+ if (status === google.maps.GeocoderStatus.OK){
977
+ fnc.apply(that, [todo, all ? results : results[0].geometry.location]);
978
+ } else if ( (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < _default.queryLimit.attempt) ){
979
+ setTimeout(function(){
980
+ that._resolveLatLng(todo, method, all, attempt+1);
981
+ },
982
+ _default.queryLimit.delay + Math.floor(Math.random() * _default.queryLimit.random)
983
+ );
984
+ } else {
985
+ if (_default.verbose){
986
+ alert('Geocode error : ' + status);
987
+ }
988
+ fnc.apply(that, [todo, false]);;
989
+ }
990
+ }
991
+ );
992
+ } else {
993
+ fnc.apply(that, [todo, toLatLng(todo, false, true)]);
994
+ }
995
+ }
996
+
997
+ /**
998
+ * returns the geographical coordinates from an array of object using "address" and call internal method
999
+ **/
1000
+ this._resolveAllLatLng = function(todo, property, method){
1001
+ var that = this,
1002
+ i = -1,
1003
+ solveNext = function(){
1004
+ do{
1005
+ i++;
1006
+ }while( (i < todo[property].length) && !('address' in todo[property][i]) );
1007
+ if (i < todo[property].length){
1008
+ (function(todo){
1009
+ that._resolveLatLng(
1010
+ todo,
1011
+ function(todo, latLng){
1012
+ todo.latLng = latLng;
1013
+ solveNext.apply(that, []); // solve next or execute exit method
1014
+ }
1015
+ );
1016
+ })(todo[property][i]);
1017
+ } else {
1018
+ that[method](todo);
1019
+ }
1020
+ };
1021
+ solveNext();
1022
+ }
1023
+
1024
+ /**
1025
+ * call a function of framework or google map object of the instance
1026
+ **/
1027
+ this._call = function(/* fncName [, ...] */){
1028
+ var i, fname = arguments[0], args = [];
1029
+ if ( !arguments.length || !map || (typeof(map[fname]) !== 'function') ){
1030
+ return;
1031
+ }
1032
+ for(i=1; i<arguments.length; i++){
1033
+ args.push(arguments[i]);
1034
+ }
1035
+ return map[fname].apply(map, args);
1036
+ }
1037
+
1038
+ /**
1039
+ * init if not and manage map subcall (zoom, center)
1040
+ **/
1041
+ this._subcall = function(todo, latLng){
1042
+ var opts = {};
1043
+ if (!todo.map) return;
1044
+ if (!latLng) {
1045
+ latLng = ival(todo.map, 'latlng');
1046
+ }
1047
+ if (!map){
1048
+ if (latLng) {
1049
+ opts = {center:latLng};
1050
+ }
1051
+ this.init($.extend({}, todo.map, opts), true);
1052
+ } else {
1053
+ if (todo.map.center && latLng){
1054
+ this._call("setCenter", latLng);
1055
+ }
1056
+ if (todo.map.zoom !== undefined){
1057
+ this._call("setZoom", todo.map.zoom);
1058
+ }
1059
+ if (todo.map.mapTypeId !== undefined){
1060
+ this._call("setMapTypeId", todo.map.mapTypeId);
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ /**
1066
+ * attach an event to a sender
1067
+ **/
1068
+ this._attachEvent = function(sender, name, fnc, data, once){
1069
+ google.maps.event['addListener'+(once?'Once':'')](sender, name, function(event) {
1070
+ fnc.apply($this, [sender, event, data]);
1071
+ });
1072
+ }
1073
+
1074
+ /**
1075
+ * attach events from a container to a sender
1076
+ * todo[
1077
+ * events => { eventName => function, }
1078
+ * onces => { eventName => function, }
1079
+ * data => mixed data
1080
+ * ]
1081
+ **/
1082
+ this._attachEvents = function(sender, todo){
1083
+ var name;
1084
+ if (!todo) {
1085
+ return
1086
+ }
1087
+ if (todo.events){
1088
+ for(name in todo.events){
1089
+ if (typeof(todo.events[name]) === 'function'){
1090
+ this._attachEvent(sender, name, todo.events[name], todo.data, false);
1091
+ }
1092
+ }
1093
+ }
1094
+ if (todo.onces){
1095
+ for(name in todo.onces){
1096
+ if (typeof(todo.onces[name]) === 'function'){
1097
+ this._attachEvent(sender, name, todo.onces[name], todo.data, true);
1098
+ }
1099
+ }
1100
+ }
1101
+ }
1102
+
1103
+ /**
1104
+ * execute callback functions
1105
+ **/
1106
+ this._callback = function(result, todo){
1107
+ if (typeof(todo.callback) === 'function') {
1108
+ todo.callback.apply($this, [result]);
1109
+ } else if (typeof(todo.callback) === 'object') {
1110
+ for(var i=0; i<todo.callback.length; i++){
1111
+ if (typeof(todo.callback[i]) === 'function') {
1112
+ todo.callback[k].apply($this, [result]);
1113
+ }
1114
+ }
1115
+ }
1116
+ }
1117
+
1118
+ /**
1119
+ * execute ending functions
1120
+ **/
1121
+ this._manageEnd = function(result, todo, internal){
1122
+ var i, apply;
1123
+ if (result && (typeof(result) === 'object')){
1124
+ // attach events
1125
+ this._attachEvents(result, todo);
1126
+ // execute "apply"
1127
+ if (todo.apply && todo.apply.length){
1128
+ for(i=0; i<todo.apply.length; i++){
1129
+ apply = todo.apply[i];
1130
+ // need an existing "action" function in the result object
1131
+ if(!apply.action || (typeof(result[apply.action]) !== 'function') ) {
1132
+ continue;
1133
+ }
1134
+ if (apply.args) {
1135
+ result[apply.action].apply(result, apply.args);
1136
+ } else {
1137
+ result[apply.action]();
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ if (!internal) {
1143
+ this._callback(result, todo);
1144
+ this._end();
1145
+ }
1146
+ }
1147
+
1148
+ //-----------------------------------------------------------------------//
1149
+ // gmap3 functions
1150
+ //-----------------------------------------------------------------------//
1151
+
1152
+ /**
1153
+ * destroy an existing instance
1154
+ **/
1155
+ this.destroy = function(todo){
1156
+ var k;
1157
+ store.clear();
1158
+ $this.empty();
1159
+ for(k in styles){
1160
+ delete styles[ k ];
1161
+ }
1162
+ styles = {};
1163
+ if (map){
1164
+ delete map;
1165
+ }
1166
+ this._callback(null, todo);
1167
+ this._end();
1168
+ }
1169
+
1170
+ /**
1171
+ * Initialize google.maps.Map object
1172
+ **/
1173
+ this.init = function(todo, internal){
1174
+ var o, k, opts;
1175
+ if (map) { // already initialized
1176
+ return this._end();
1177
+ }
1178
+
1179
+ o = getObject('map', todo);
1180
+ if ( (typeof(o.options.center) === 'boolean') && o.options.center) {
1181
+ return false; // wait for an address resolution
1182
+ }
1183
+ opts = $.extend({}, _default.init, o.options);
1184
+ if (!opts.center) {
1185
+ opts.center = [_default.init.center.lat, _default.init.center.lng];
1186
+ }
1187
+ opts.center = toLatLng(opts.center);
1188
+ map = new _default.classes.Map($this.get(0), opts);
1189
+
1190
+ // add previous added styles
1191
+ for(k in styles) {
1192
+ map.mapTypes.set(k, styles[k]);
1193
+ }
1194
+
1195
+ this._manageEnd(map, o, internal);
1196
+ return true;
1197
+ }
1198
+
1199
+ /**
1200
+ * returns the geographical coordinates from an address
1201
+ **/
1202
+ this.getlatlng = function(todo){
1203
+ this._resolveLatLng(todo, '_getLatLng', true);
1204
+ },
1205
+
1206
+ this._getLatLng = function(todo, results){
1207
+ this._manageEnd(results, todo);
1208
+ },
1209
+
1210
+
1211
+ /**
1212
+ * returns address from latlng
1213
+ **/
1214
+ this.getaddress = function(todo, attempt){
1215
+ var latLng = toLatLng(todo, false, true),
1216
+ address = ival(todo, 'address'),
1217
+ params = latLng ? {latLng:latLng} : ( address ? (typeof(address) === 'string' ? {address:address} : address) : null),
1218
+ callback = ival(todo, 'callback'),
1219
+ that = this;
1220
+ if (!attempt){ // convert undefined to int
1221
+ attempt = 0;
1222
+ }
1223
+ if (params && typeof(callback) === 'function') {
1224
+ getGeocoder().geocode(
1225
+ params,
1226
+ function(results, status) {
1227
+ if ( (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < _default.queryLimit.attempt) ){
1228
+ setTimeout(function(){
1229
+ that.getaddress(todo, attempt+1);
1230
+ },
1231
+ _default.queryLimit.delay + Math.floor(Math.random() * _default.queryLimit.random)
1232
+ );
1233
+ } else {
1234
+ var out = status === google.maps.GeocoderStatus.OK ? results : false;
1235
+ callback.apply($this, [out, status]);
1236
+ if (!out && _default.verbose){
1237
+ alert('Geocode error : ' + status);
1238
+ }
1239
+ that._end();
1240
+ }
1241
+ }
1242
+ );
1243
+ } else {
1244
+ this._end();
1245
+ }
1246
+ }
1247
+
1248
+ /**
1249
+ * return a route
1250
+ **/
1251
+ this.getroute = function(todo){
1252
+ var callback = ival(todo, 'callback'),
1253
+ that = this;
1254
+ if ( (typeof(callback) === 'function') && todo.options ) {
1255
+ todo.options.origin = toLatLng(todo.options.origin, true);
1256
+ todo.options.destination = toLatLng(todo.options.destination, true);
1257
+ getDirectionsService().route(
1258
+ todo.options,
1259
+ function(results, status) {
1260
+ var out = status == google.maps.DirectionsStatus.OK ? results : false;
1261
+ callback.apply($this, [out, status]);
1262
+ that._end();
1263
+ }
1264
+ );
1265
+ } else {
1266
+ this._end();
1267
+ }
1268
+ }
1269
+
1270
+ /**
1271
+ * return the elevation of a location
1272
+ **/
1273
+ this.getelevation = function(todo){
1274
+ var fnc, path, samples, i,
1275
+ locations = [],
1276
+ callback = ival(todo, 'callback'),
1277
+ latLng = ival(todo, 'latlng'),
1278
+ that = this;
1279
+
1280
+ if (typeof(callback) === 'function'){
1281
+ fnc = function(results, status){
1282
+ var out = status === google.maps.ElevationStatus.OK ? results : false;
1283
+ callback.apply($this, [out, status]);
1284
+ that._end();
1285
+ };
1286
+ if (latLng){
1287
+ locations.push(toLatLng(latLng));
1288
+ } else {
1289
+ locations = ival(todo, 'locations') || [];
1290
+ if (locations){
1291
+ locations = array(locations);
1292
+ for(i=0; i<locations.length; i++){
1293
+ locations[i] = toLatLng(locations[i]);
1294
+ }
1295
+ }
1296
+ }
1297
+ if (locations.length){
1298
+ getElevationService().getElevationForLocations({locations:locations}, fnc);
1299
+ } else {
1300
+ path = ival(todo, 'path');
1301
+ samples = ival(todo, 'samples');
1302
+ if (path && samples){
1303
+ for(i=0; i<path.length; i++){
1304
+ locations.push(toLatLng(path[i]));
1305
+ }
1306
+ if (locations.length){
1307
+ getElevationService().getElevationAlongPath({path:locations, samples:samples}, fnc);
1308
+ }
1309
+ }
1310
+ }
1311
+ } else {
1312
+ this._end();
1313
+ }
1314
+ }
1315
+
1316
+ /**
1317
+ * return the distance between an origin and a destination
1318
+ *
1319
+ **/
1320
+ this.getdistance = function(todo){
1321
+ var i,
1322
+ callback = ival(todo, 'callback'),
1323
+ that = this;
1324
+ if ( (typeof(callback) === 'function') && todo.options && todo.options.origins && todo.options.destinations ) {
1325
+ // origins and destinations are array containing one or more address strings and/or google.maps.LatLng objects
1326
+ todo.options.origins = array(todo.options.origins);
1327
+ for(i=0; i<todo.options.origins.length; i++){
1328
+ todo.options.origins[i] = toLatLng(todo.options.origins[i], true);
1329
+ }
1330
+ todo.options.destinations = array(todo.options.destinations);
1331
+ for(i=0; i<todo.options.destinations.length; i++){
1332
+ todo.options.destinations[i] = toLatLng(todo.options.destinations[i], true);
1333
+ }
1334
+ getDistanceMatrixService().getDistanceMatrix(
1335
+ todo.options,
1336
+ function(results, status) {
1337
+ var out = status == google.maps.DistanceMatrixStatus.OK ? results : false;
1338
+ callback.apply($this, [out, status]);
1339
+ that._end();
1340
+ }
1341
+ );
1342
+ } else {
1343
+ this._end();
1344
+ }
1345
+ }
1346
+
1347
+ /**
1348
+ * Add a marker to a map after address resolution
1349
+ * if [infowindow] add an infowindow attached to the marker
1350
+ **/
1351
+ this.addmarker = function(todo){
1352
+ this._resolveLatLng(todo, '_addMarker');
1353
+ }
1354
+
1355
+ this._addMarker = function(todo, latLng, internal){
1356
+ var result, oi, to,
1357
+ o = getObject('marker', todo, 'to');
1358
+ if (!internal){
1359
+ if (!latLng) {
1360
+ this._manageEnd(false, o);
1361
+ return;
1362
+ }
1363
+ this._subcall(todo, latLng);
1364
+ } else if (!latLng){
1365
+ return;
1366
+ }
1367
+ if (o.to){
1368
+ to = store.refToObj(o.to);
1369
+ result = to && (typeof(to.add) === 'function');
1370
+ if (result){
1371
+ to.add(latLng, todo);
1372
+ if (typeof(to.redraw) === 'function'){
1373
+ to.redraw();
1374
+ }
1375
+ }
1376
+ if (!internal){
1377
+ this._manageEnd(result, o);
1378
+ }
1379
+ } else {
1380
+ o.options.position = latLng;
1381
+ o.options.map = map;
1382
+ result = new _default.classes.Marker(o.options);
1383
+ if (hasKey(todo, 'infowindow')){
1384
+ oi = getObject('infowindow', todo['infowindow'], 'open');
1385
+ // if "open" is not defined, add it in first position
1386
+ if ( (oi.open === undefined) || oi.open ){
1387
+ oi.apply = array(oi.apply);
1388
+ oi.apply.unshift({action:'open', args:[map, result]});
1389
+ }
1390
+ oi.action = 'addinfowindow';
1391
+ this._planNext(oi);
1392
+ }
1393
+ if (!internal){
1394
+ store.add('marker', result, o);
1395
+ this._manageEnd(result, o);
1396
+ }
1397
+ }
1398
+ return result;
1399
+ }
1400
+
1401
+ /**
1402
+ * add markers (without address resolution)
1403
+ **/
1404
+ this.addmarkers = function(todo){
1405
+ if (ival(todo, 'clusters')){
1406
+ this._resolveAllLatLng(todo, 'markers', '_addclusteredmarkers');
1407
+ } else {
1408
+ this._resolveAllLatLng(todo, 'markers', '_addmarkers');
1409
+ }
1410
+ }
1411
+
1412
+ this._addmarkers = function(todo){
1413
+ var result, o, i, latLng, marker, options = {}, tmp, to,
1414
+ markers = ival(todo, 'markers');
1415
+ this._subcall(todo);
1416
+ if (typeof(markers) !== 'object') {
1417
+ return this._end();
1418
+ }
1419
+ o = getObject('marker', todo, ['to', 'markers']);
1420
+
1421
+ if (o.to){
1422
+ to = store.refToObj(o.to);
1423
+ result = to && (typeof(to.add) === 'function');
1424
+ if (result){
1425
+ for(i=0; i<markers.length; i++){
1426
+ if (latLng = toLatLng(markers[i])) {
1427
+ to.add(latLng, markers[i]);
1428
+ }
1429
+ }
1430
+ if (typeof(to.redraw) === 'function'){
1431
+ to.redraw();
1432
+ }
1433
+ }
1434
+ this._manageEnd(result, o);
1435
+ } else {
1436
+ $.extend(true, options, o.options);
1437
+ options.map = map;
1438
+ result = [];
1439
+ for(i=0; i<markers.length; i++){
1440
+ if (latLng = toLatLng(markers[i])){
1441
+ if (markers[i].options){
1442
+ tmp = {};
1443
+ $.extend(true, tmp, options, markers[i].options);
1444
+ o.options = tmp;
1445
+ } else {
1446
+ o.options = options;
1447
+ }
1448
+ o.options.position = latLng;
1449
+ marker = new _default.classes.Marker(o.options);
1450
+ result.push(marker);
1451
+ o.data = markers[i].data;
1452
+ o.tag = markers[i].tag;
1453
+ store.add('marker', marker, o);
1454
+ this._manageEnd(marker, o, true);
1455
+ }
1456
+ }
1457
+ o.options = options; // restore previous for futur use
1458
+ this._callback(result, todo);
1459
+ this._end();
1460
+ }
1461
+ }
1462
+
1463
+ this._addclusteredmarkers = function(todo){
1464
+ var clusterer, i, latLng, storeId,
1465
+ that = this,
1466
+ radius = ival(todo, 'radius'),
1467
+ maxZoom = ival(todo, 'maxZoom'),
1468
+ markers = ival(todo, 'markers'),
1469
+ styles = ival(todo, 'clusters');
1470
+
1471
+ if (!map.getBounds()){ // map not initialised => bounds not available
1472
+ // wait for map
1473
+ google.maps.event.addListenerOnce(
1474
+ map,
1475
+ 'bounds_changed',
1476
+ function() {
1477
+ that._addclusteredmarkers(todo);
1478
+ }
1479
+ );
1480
+ return;
1481
+ }
1482
+
1483
+ if (typeof(radius) === 'number'){
1484
+ clusterer = new Clusterer();
1485
+ for(i=0 ; i<markers.length; i++){
1486
+ latLng = toLatLng(markers[i]);
1487
+ clusterer.add(latLng, markers[i]);
1488
+ }
1489
+ storeId = this._initClusters(todo, clusterer, radius, maxZoom, styles);
1490
+ }
1491
+
1492
+ this._callback(storeId, todo);
1493
+ this._end();
1494
+ }
1495
+
1496
+
1497
+ this._initClusters = function(todo, clusterer, radius, maxZoom, styles){
1498
+ var that = this;
1499
+
1500
+ clusterer.setRedraw(function(force){
1501
+ var same, clusters = clusterer.clusters(map, radius, maxZoom, force);
1502
+ if (clusters){
1503
+ same = clusterer.freeDiff(clusters);
1504
+ that._displayClusters(todo, clusterer, clusters, same, styles);
1505
+ }
1506
+ });
1507
+
1508
+ clusterer.events(
1509
+ google.maps.event.addListener(
1510
+ map,
1511
+ 'zoom_changed',
1512
+ function() {
1513
+ clusterer.redraw(true);
1514
+ }
1515
+ ),
1516
+ google.maps.event.addListener(
1517
+ map,
1518
+ 'bounds_changed',
1519
+ function() {
1520
+ clusterer.redraw();
1521
+ }
1522
+ )
1523
+ );
1524
+
1525
+ clusterer.redraw();
1526
+ return store.add('cluster', clusterer, todo);
1527
+ }
1528
+
1529
+ this._displayClusters = function(todo, clusterer, clusters, same, styles){
1530
+ var k, i, ii, m, done, obj, shadow, cluster, options, tmp, w, h,
1531
+ atodo,
1532
+ ctodo = hasKey(todo, 'cluster') ? getObject('', ival(todo, 'cluster')) : {},
1533
+ mtodo = hasKey(todo, 'marker') ? getObject('', ival(todo, 'marker')) : {};
1534
+ for(i=0; i<clusters.length; i++){
1535
+ if (i in same){
1536
+ continue;
1537
+ }
1538
+ cluster = clusters[i];
1539
+ done = false;
1540
+ if (cluster.idx.length > 1){
1541
+ // look for the cluster design to use
1542
+ m = 0;
1543
+ for(k in styles){
1544
+ if ( (k > m) && (k <= cluster.idx.length) ){
1545
+ m = k;
1546
+ }
1547
+ }
1548
+ if (styles[m]){ // cluster defined for the current markers count
1549
+ w = ival(styles[m], 'width');
1550
+ h = ival(styles[m], 'height');
1551
+
1552
+ // create a custom _addOverlay command
1553
+ atodo = {};
1554
+ $.extend(
1555
+ true,
1556
+ atodo,
1557
+ ctodo,
1558
+ { options:{
1559
+ pane: 'overlayLayer',
1560
+ content:styles[m].content.replace('CLUSTER_COUNT', cluster.idx.length),
1561
+ offset:{
1562
+ x: -w/2,
1563
+ y: -h/2
1564
+ }
1565
+ }
1566
+ }
1567
+ );
1568
+ obj = this._addOverlay(atodo, toLatLng(cluster), true);
1569
+ atodo.options.pane = 'floatShadow';
1570
+ atodo.options.content = $('<div></div>');
1571
+ atodo.options.content.width(w);
1572
+ atodo.options.content.height(h);
1573
+ shadow = this._addOverlay(atodo, toLatLng(cluster), true);
1574
+
1575
+ // store data to the clusterer
1576
+ ctodo.data = {
1577
+ latLng: toLatLng(cluster),
1578
+ markers:[]
1579
+ };
1580
+ for(ii=0; ii<cluster.idx.length; ii++){
1581
+ ctodo.data.markers.push(
1582
+ clusterer.get(cluster.idx[ii]).marker
1583
+ );
1584
+ }
1585
+ this._attachEvents(shadow, ctodo);
1586
+ clusterer.store(cluster, obj, shadow);
1587
+ done = true;
1588
+ }
1589
+ }
1590
+ if (!done){ // cluster not defined (< min count) or = 1 so display all markers of the current cluster
1591
+ // save the defaults options for the markers
1592
+ options = {};
1593
+ $.extend(true, options, mtodo.options);
1594
+ for(ii = 0; ii <cluster.idx.length; ii++){
1595
+ m = clusterer.get(cluster.idx[ii]);
1596
+ mtodo.latLng = m.latLng;
1597
+ mtodo.data = m.marker.data;
1598
+ mtodo.tag = m.marker.tag;
1599
+ if (m.marker.options){
1600
+ tmp = {};
1601
+ $.extend(true, tmp, options, m.marker.options);
1602
+ mtodo.options = tmp;
1603
+ } else {
1604
+ mtodo.options = options;
1605
+ }
1606
+ obj = this._addMarker(mtodo, mtodo.latLng, true);
1607
+ this._attachEvents(obj, mtodo);
1608
+ clusterer.store(cluster, obj);
1609
+ }
1610
+ mtodo.options = options; // restore previous for futur use
1611
+ }
1612
+ }
1613
+ }
1614
+
1615
+ /**
1616
+ * add an infowindow after address resolution
1617
+ **/
1618
+ this.addinfowindow = function(todo){
1619
+ this._resolveLatLng(todo, '_addInfoWindow');
1620
+ }
1621
+
1622
+ this._addInfoWindow = function(todo, latLng){
1623
+ var o, infowindow, args = [];
1624
+ this._subcall(todo, latLng);
1625
+ o = getObject('infowindow', todo, ['open', 'anchor']);
1626
+ if (latLng) {
1627
+ o.options.position = latLng;
1628
+ }
1629
+ infowindow = new _default.classes.InfoWindow(o.options);
1630
+ if ( (o.open === undefined) || o.open ){
1631
+ o.apply = array(o.apply);
1632
+ args.push(map);
1633
+ if (o.anchor){
1634
+ args.push(o.anchor);
1635
+ }
1636
+ o.apply.unshift({action:'open', args:args});
1637
+ }
1638
+ store.add('infowindow', infowindow, o);
1639
+ this._manageEnd(infowindow, o);
1640
+ }
1641
+
1642
+
1643
+ /**
1644
+ * add a polygone / polylin on a map
1645
+ **/
1646
+ this.addpolyline = function(todo){
1647
+ this._addPoly(todo, 'Polyline', 'path');
1648
+ }
1649
+
1650
+ this.addpolygon = function(todo){
1651
+ this._addPoly(todo, 'Polygon', 'paths');
1652
+ }
1653
+
1654
+ this._addPoly = function(todo, poly, path){
1655
+ var i,
1656
+ obj, latLng,
1657
+ o = getObject(poly.toLowerCase(), todo, path);
1658
+ if (o[path]){
1659
+ o.options[path] = [];
1660
+ for(i=0; i<o[path].length; i++){
1661
+ if (latLng = toLatLng(o[path][i])){
1662
+ o.options[path].push(latLng);
1663
+ }
1664
+ }
1665
+ }
1666
+ obj = new google.maps[poly](o.options);
1667
+ obj.setMap(map);
1668
+ store.add(poly.toLowerCase(), obj, o);
1669
+ this._manageEnd(obj, o);
1670
+ }
1671
+
1672
+ /**
1673
+ * add a circle
1674
+ **/
1675
+ this.addcircle = function(todo){
1676
+ this._resolveLatLng(todo, '_addCircle');
1677
+ }
1678
+
1679
+ this._addCircle = function(todo, latLng){
1680
+ var c, o = getObject('circle', todo);
1681
+ if (!latLng) {
1682
+ latLng = toLatLng(o.options.center);
1683
+ }
1684
+ if (!latLng) {
1685
+ return this._manageEnd(false, o);
1686
+ }
1687
+ this._subcall(todo, latLng);
1688
+ o.options.center = latLng;
1689
+ o.options.map = map;
1690
+ c = new _default.classes.Circle(o.options);
1691
+ store.add('circle', c, o);
1692
+ this._manageEnd(c, o);
1693
+ }
1694
+
1695
+ /**
1696
+ * add a rectangle
1697
+ **/
1698
+ this.addrectangle = function(todo){
1699
+ this._resolveLatLng(todo, '_addRectangle');
1700
+ }
1701
+
1702
+ this._addRectangle = function(todo, latLng ){
1703
+ var r, o = getObject('rectangle', todo);
1704
+ o.options.bounds = toLatLngBounds(o.options.bounds, true);
1705
+ if (!o.options.bounds) {
1706
+ return this._manageEnd(false, o);
1707
+ }
1708
+ this._subcall(todo, o.options.bounds.getCenter());
1709
+ o.options.map = map;
1710
+ r = new _default.classes.Rectangle(o.options);
1711
+ store.add('rectangle', r, o);
1712
+ this._manageEnd(r, o);
1713
+ }
1714
+
1715
+ /**
1716
+ * add an overlay to a map after address resolution
1717
+ **/
1718
+ this.addoverlay = function(todo){
1719
+ this._resolveLatLng(todo, '_addOverlay');
1720
+ }
1721
+
1722
+ this._addOverlay = function(todo, latLng, internal){
1723
+ var ov,
1724
+ o = getObject('overlay', todo),
1725
+ opts = $.extend({
1726
+ pane: 'floatPane',
1727
+ content: '',
1728
+ offset:{
1729
+ x:0,y:0
1730
+ }
1731
+ },
1732
+ o.options),
1733
+ $div = $('<div></div>'),
1734
+ listeners = [];
1735
+
1736
+ $div
1737
+ .css('border', 'none')
1738
+ .css('borderWidth', '0px')
1739
+ .css('position', 'absolute');
1740
+ $div.append(opts.content);
1741
+
1742
+ function f() {
1743
+ _default.classes.OverlayView.call(this);
1744
+ this.setMap(map);
1745
+ }
1746
+
1747
+ f.prototype = new _default.classes.OverlayView();
1748
+
1749
+ f.prototype.onAdd = function() {
1750
+ var panes = this.getPanes();
1751
+ if (opts.pane in panes) {
1752
+ $(panes[opts.pane]).append($div);
1753
+ }
1754
+ }
1755
+ f.prototype.draw = function() {
1756
+ var overlayProjection = this.getProjection(),
1757
+ ps = overlayProjection.fromLatLngToDivPixel(latLng),
1758
+ that = this;
1759
+
1760
+ $div
1761
+ .css('left', (ps.x+opts.offset.x) + 'px')
1762
+ .css('top' , (ps.y+opts.offset.y) + 'px');
1763
+
1764
+ $.each( ("dblclick click mouseover mousemove mouseout mouseup mousedown").split(" "), function( i, name ) {
1765
+ listeners.push(
1766
+ google.maps.event.addDomListener($div[0], name, function(e) {
1767
+ google.maps.event.trigger(that, name);
1768
+ })
1769
+ );
1770
+ });
1771
+ listeners.push(
1772
+ google.maps.event.addDomListener($div[0], "contextmenu", function(e) {
1773
+ google.maps.event.trigger(that, "rightclick");
1774
+ })
1775
+ );
1776
+ }
1777
+ f.prototype.onRemove = function() {
1778
+ for (var i = 0; i < listeners.length; i++) {
1779
+ google.maps.event.removeListener(listeners[i]);
1780
+ }
1781
+ $div.remove();
1782
+ }
1783
+ f.prototype.hide = function() {
1784
+ $div.hide();
1785
+ }
1786
+ f.prototype.show = function() {
1787
+ $div.show();
1788
+ }
1789
+ f.prototype.toggle = function() {
1790
+ if ($div) {
1791
+ if ($div.is(':visible')){
1792
+ this.show();
1793
+ } else {
1794
+ this.hide();
1795
+ }
1796
+ }
1797
+ }
1798
+ f.prototype.toggleDOM = function() {
1799
+ if (this.getMap()) {
1800
+ this.setMap(null);
1801
+ } else {
1802
+ this.setMap(map);
1803
+ }
1804
+ }
1805
+ f.prototype.getDOMElement = function() {
1806
+ return $div[0];
1807
+ }
1808
+ ov = new f();
1809
+ if (!internal){
1810
+ store.add('overlay', ov, o);
1811
+ this._manageEnd(ov, o);
1812
+ }
1813
+ return ov;
1814
+ }
1815
+
1816
+ /**
1817
+ * add a fix panel to a map
1818
+ **/
1819
+ this.addfixpanel = function(todo){
1820
+ var o = getObject('fixpanel', todo),
1821
+ x=y=0, $c, $div;
1822
+ if (o.options.content){
1823
+ $c = $(o.options.content);
1824
+
1825
+ if (o.options.left !== undefined){
1826
+ x = o.options.left;
1827
+ } else if (o.options.right !== undefined){
1828
+ x = $this.width() - $c.width() - o.options.right;
1829
+ } else if (o.options.center){
1830
+ x = ($this.width() - $c.width()) / 2;
1831
+ }
1832
+
1833
+ if (o.options.top !== undefined){
1834
+ y = o.options.top;
1835
+ } else if (o.options.bottom !== undefined){
1836
+ y = $this.height() - $c.height() - o.options.bottom;
1837
+ } else if (o.options.middle){
1838
+ y = ($this.height() - $c.height()) / 2
1839
+ }
1840
+
1841
+ $div = $('<div></div>')
1842
+ .css('position', 'absolute')
1843
+ .css('top', y+'px')
1844
+ .css('left', x+'px')
1845
+ .css('z-index', '1000')
1846
+ .append($c);
1847
+
1848
+ $this.first().prepend($div);
1849
+ this._attachEvents(map, o);
1850
+ store.add('fixpanel', $div, o);
1851
+ this._callback($div, o);
1852
+ }
1853
+ this._end();
1854
+ }
1855
+
1856
+ /**
1857
+ * add a direction renderer to a map
1858
+ **/
1859
+ this.adddirectionsrenderer = function(todo, internal){
1860
+ var dr, o = getObject('directionrenderer', todo, 'panelId');
1861
+ o.options.map = map;
1862
+ dr = new google.maps.DirectionsRenderer(o.options);
1863
+ if (o.panelId) {
1864
+ dr.setPanel(document.getElementById(o.panelId));
1865
+ }
1866
+ store.add('directionrenderer', dr, o);
1867
+ this._manageEnd(dr, o, internal);
1868
+ return dr;
1869
+ }
1870
+
1871
+ /**
1872
+ * set a direction panel to a dom element from its ID
1873
+ **/
1874
+ this.setdirectionspanel = function(todo){
1875
+ var dr = store.get('directionrenderer'),
1876
+ o = getObject('directionpanel', todo, 'id');
1877
+ if (dr && o.id) {
1878
+ dr.setPanel(document.getElementById(o.id));
1879
+ }
1880
+ this._manageEnd(dr, o);
1881
+ }
1882
+
1883
+ /**
1884
+ * set directions on a map (create Direction Renderer if needed)
1885
+ **/
1886
+ this.setdirections = function(todo){
1887
+ var dr = store.get('directionrenderer'),
1888
+ o = getObject('directions', todo);
1889
+ if (todo) {
1890
+ o.options.directions = todo.directions ? todo.directions : (todo.options && todo.options.directions ? todo.options.directions : null);
1891
+ }
1892
+ if (o.options.directions) {
1893
+ if (!dr) {
1894
+ dr = this.adddirectionsrenderer(o, true);
1895
+ } else {
1896
+ dr.setDirections(o.options.directions);
1897
+ }
1898
+ }
1899
+ this._manageEnd(dr, o);
1900
+ }
1901
+
1902
+ /**
1903
+ * set a streetview to a map
1904
+ **/
1905
+ this.setstreetview = function(todo){
1906
+ var panorama,
1907
+ o = getObject('streetview', todo, 'id');
1908
+ if (o.options.position){
1909
+ o.options.position = toLatLng(o.options.position);
1910
+ }
1911
+ panorama = new _default.classes.StreetViewPanorama(document.getElementById(o.id),o.options);
1912
+ if (panorama){
1913
+ map.setStreetView(panorama);
1914
+ }
1915
+ this._manageEnd(panorama, o);
1916
+ }
1917
+
1918
+ /**
1919
+ * add a kml layer to a map
1920
+ **/
1921
+ this.addkmllayer = function(todo){
1922
+ var kml,
1923
+ o = getObject('kmllayer', todo, 'url');
1924
+ o.options.map = map;
1925
+ if (typeof(o.url) === 'string'){
1926
+ kml = new _default.classes.KmlLayer(o.url, o.options);
1927
+ }
1928
+ store.add('kmllayer', kml, o);
1929
+ this._manageEnd(kml, o);
1930
+ }
1931
+
1932
+ /**
1933
+ * add a traffic layer to a map
1934
+ **/
1935
+ this.addtrafficlayer = function(todo){
1936
+ var o = getObject('trafficlayer', todo),
1937
+ tl = store.get('trafficlayer');
1938
+ if (!tl){
1939
+ tl = new _default.classes.TrafficLayer();
1940
+ tl.setMap(map);
1941
+ store.add('trafficlayer', tl, o);
1942
+ }
1943
+ this._manageEnd(tl, o);
1944
+ }
1945
+
1946
+ /**
1947
+ * add a bicycling layer to a map
1948
+ **/
1949
+ this.addbicyclinglayer = function(todo){
1950
+ var o = getObject('bicyclinglayer', todo),
1951
+ bl = store.get('bicyclinglayer');
1952
+ if (!bl){
1953
+ bl = new _default.classes.BicyclingLayer();
1954
+ bl.setMap(map);
1955
+ store.add('bicyclinglayer', bl, o);
1956
+ }
1957
+ this._manageEnd(bl, o);
1958
+ }
1959
+
1960
+ /**
1961
+ * add a ground overlay to a map
1962
+ **/
1963
+ this.addgroundoverlay = function(todo){
1964
+ var ov,
1965
+ o = getObject('groundoverlay', todo, ['bounds', 'url']);
1966
+ o.bounds = toLatLngBounds(o.bounds);
1967
+ if (o.bounds && (typeof(o.url) === 'string')){
1968
+ ov = new _default.classes.GroundOverlay(o.url, o.bounds);
1969
+ ov.setMap(map);
1970
+ store.add('groundoverlay', ov, o);
1971
+ }
1972
+ this._manageEnd(ov, o);
1973
+ }
1974
+
1975
+ /**
1976
+ * geolocalise the user and return a LatLng
1977
+ **/
1978
+ this.geolatlng = function(todo){
1979
+ var callback = ival(todo, 'callback');
1980
+ if (typeof(callback) === 'function') {
1981
+ if(navigator.geolocation) {
1982
+ navigator.geolocation.getCurrentPosition(
1983
+ function(position) {
1984
+ var out = new google.maps.LatLng(position.coords.latitude,position.coords.longitude);
1985
+ callback.apply($this, [out]);
1986
+ },
1987
+ function() {
1988
+ var out = false;
1989
+ callback.apply($this, [out]);
1990
+ }
1991
+ );
1992
+ } else if (google.gears) {
1993
+ google.gears.factory.create('beta.geolocation').getCurrentPosition(
1994
+ function(position) {
1995
+ var out = new google.maps.LatLng(position.latitude,position.longitude);
1996
+ callback.apply($this, [out]);
1997
+ },
1998
+ function() {
1999
+ out = false;
2000
+ callback.apply($this, [out]);
2001
+ }
2002
+ );
2003
+ } else {
2004
+ callback.apply($this, [false]);
2005
+ }
2006
+ }
2007
+ this._end();
2008
+ }
2009
+
2010
+ /**
2011
+ * add a style to a map
2012
+ **/
2013
+ this.addstyledmap = function(todo, internal){
2014
+ var o = getObject('styledmap', todo, ['id', 'style']);
2015
+ if (o.style && o.id && !styles[o.id]) {
2016
+ styles[o.id] = new _default.classes.StyledMapType(o.style, o.options);
2017
+ if (map) {
2018
+ map.mapTypes.set(o.id, styles[o.id]);
2019
+ }
2020
+ }
2021
+ this._manageEnd(styles[o.id], o, internal);
2022
+ }
2023
+
2024
+ /**
2025
+ * set a style to a map (add it if needed)
2026
+ **/
2027
+ this.setstyledmap = function(todo){
2028
+ var o = getObject('styledmap', todo, ['id', 'style']);
2029
+ if (o.id) {
2030
+ this.addstyledmap(o, true);
2031
+ if (styles[o.id]) {
2032
+ map.setMapTypeId(o.id);
2033
+ this._callback(styles[o.id], todo);
2034
+ }
2035
+ }
2036
+ this._manageEnd(styles[o.id], o);
2037
+ }
2038
+
2039
+ /**
2040
+ * remove objects from a map
2041
+ **/
2042
+ this.clear = function(todo){
2043
+ var list = array(ival(todo, 'list') || ival(todo, 'name')),
2044
+ last = ival(todo, 'last', false),
2045
+ first = ival(todo, 'first', false),
2046
+ tag = ival(todo, 'tag');
2047
+ if (tag !== undefined){
2048
+ tag = array(tag);
2049
+ }
2050
+ store.clear(list, last, first, tag);
2051
+ this._end();
2052
+ }
2053
+
2054
+ /**
2055
+ * return objects previously created
2056
+ **/
2057
+ this.get = function(todo){
2058
+ var name = ival(todo, 'name') || 'map',
2059
+ first= ival(todo, 'first'),
2060
+ all = ival(todo, 'all'),
2061
+ tag = ival(todo, 'tag');
2062
+ name = name.toLowerCase();
2063
+ if (name === 'map'){
2064
+ return map;
2065
+ }
2066
+ if (tag !== undefined){
2067
+ tag = array(tag);
2068
+ }
2069
+ if (first){
2070
+ return store.get(name, false, tag);
2071
+ } else if (all){
2072
+ return store.all(name, tag);
2073
+ } else {
2074
+ return store.get(name, true, tag);
2075
+ }
2076
+ }
2077
+
2078
+ /**
2079
+ * return the max zoom of a location
2080
+ **/
2081
+ this.getmaxzoom = function(todo){
2082
+ this._resolveLatLng(todo, '_getMaxZoom');
2083
+ }
2084
+
2085
+ this._getMaxZoom = function(todo, latLng){
2086
+ var callback = ival(todo, 'callback'),
2087
+ that = this;
2088
+ if (callback && typeof(callback) === 'function') {
2089
+ getMaxZoomService().getMaxZoomAtLatLng(
2090
+ latLng,
2091
+ function(result) {
2092
+ var zoom = result.status === google.maps.MaxZoomStatus.OK ? result.zoom : false;
2093
+ callback.apply($this, [zoom, result.status]);
2094
+ that._end();
2095
+ }
2096
+ );
2097
+ } else {
2098
+ this._end();
2099
+ }
2100
+ }
2101
+
2102
+ /**
2103
+ * modify default values
2104
+ **/
2105
+ this.setdefault = function(todo){
2106
+ setDefault(todo);
2107
+ this._end();
2108
+ }
2109
+
2110
+ /**
2111
+ * autofit a map using its overlays (markers, rectangles ...)
2112
+ **/
2113
+ this.autofit = function(todo, internal){
2114
+ var names, list, obj, i, j,
2115
+ empty = true,
2116
+ bounds = new google.maps.LatLngBounds(),
2117
+ maxZoom = ival(todo, 'maxZoom', null);
2118
+
2119
+ names = store.names();
2120
+ for(i=0; i<names.length; i++){
2121
+ list = store.all(names[i]);
2122
+ for(j=0; j<list.length; j++){
2123
+ obj = list[j];
2124
+ if (obj.getPosition){
2125
+ bounds.extend(obj.getPosition());
2126
+ empty = false;
2127
+ } else if (obj.getBounds){
2128
+ bounds.extend(obj.getBounds().getNorthEast());
2129
+ bounds.extend(obj.getBounds().getSouthWest());
2130
+ empty = false;
2131
+ } else if (obj.getPaths){
2132
+ obj.getPaths().forEach(function(path){
2133
+ path.forEach(function(latLng){
2134
+ bounds.extend(latLng);
2135
+ empty = false;
2136
+ });
2137
+ });
2138
+ } else if (obj.getPath){
2139
+ obj.getPath().forEach(function(latLng){
2140
+ bounds.extend(latLng);
2141
+ empty = false;
2142
+ });
2143
+ } else if (obj.getCenter){
2144
+ bounds.extend(obj.getCenter());
2145
+ empty = false;
2146
+ }
2147
+ }
2148
+ }
2149
+
2150
+ if (!empty && (!map.getBounds() || !map.getBounds().equals(bounds))){
2151
+ if (maxZoom !== null){
2152
+ // fitBouds Callback event => detect zoom level and check maxZoom
2153
+ google.maps.event.addListenerOnce(
2154
+ map,
2155
+ 'bounds_changed',
2156
+ function() {
2157
+ if (this.getZoom() > maxZoom){
2158
+ this.setZoom(maxZoom);
2159
+ }
2160
+ }
2161
+ );
2162
+ }
2163
+ map.fitBounds(bounds);
2164
+ }
2165
+ if (!internal){
2166
+ this._manageEnd(empty ? false : bounds, todo, internal);
2167
+ }
2168
+ }
2169
+
2170
+ };
2171
+
2172
+ //-----------------------------------------------------------------------//
2173
+ // jQuery plugin
2174
+ //-----------------------------------------------------------------------//
2175
+
2176
+ $.fn.gmap3 = function(){
2177
+ var i, args, list = [], empty = true, results = [];
2178
+ // store all arguments in a todo list
2179
+ for(i=0; i<arguments.length; i++){
2180
+ args = arguments[i] || {};
2181
+ // resolve string todo - action without parameters can be simplified as string
2182
+ if (typeof(args) === 'string'){
2183
+ args = {action:args};
2184
+ }
2185
+ list.push(args);
2186
+ }
2187
+ // resolve empty call - run init
2188
+ if (!list.length) {
2189
+ list.push({});
2190
+ }
2191
+ // loop on each jQuery object
2192
+ $.each(this, function() {
2193
+ var $this = $(this),
2194
+ gmap3 = $this.data('gmap3');
2195
+ empty = false;
2196
+ if (!gmap3){
2197
+ gmap3 = new Gmap3($this);
2198
+ $this.data('gmap3', gmap3);
2199
+ }
2200
+ // direct call : bypass jQuery method (not stackable, return mixed)
2201
+ if ( (list.length == 1) && (isDirect(list[0])) ){
2202
+ results.push(gmap3._direct(list[0]));
2203
+ } else {
2204
+ gmap3._plan(list);
2205
+ }
2206
+ });
2207
+ // return for direct call (only)
2208
+ if (results.length){
2209
+ if (results.length === 1){ // 1 css selector
2210
+ return results[0];
2211
+ } else {
2212
+ return results;
2213
+ }
2214
+ }
2215
+ // manage setDefault call
2216
+ if (empty && (arguments.length == 2) && (typeof(arguments[0]) === 'string') && (arguments[0].toLowerCase() === 'setdefault')){
2217
+ setDefault(arguments[1]);
2218
+ }
2219
+ return this;
2220
+ }
2221
+
2222
+ }(jQuery));