https://wonderfm.com/wp-content/themes/clubber-php85/js/gmap.js?ver=dd9142e185a17290317387c2454a2201
1/*! 2 * GMAP3 Plugin for JQuery 3 * Version : 5.0a 4 * Date : 2012-11-10 5 * Licence : GPL v3 : http://www.gnu.org/licenses/gpl.html 6 * Author : DEMONTE Jean-Baptiste 7 * Contact : [email protected] 8 * Web site : http://gmap3.net 9 * 10 * Copyright (c) 2010-2012 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;(function ($, undef) { 39 40 /***************************************************************************/ 41 /* GMAP3 DEFAULTS */ 42 /***************************************************************************/ 43 // defaults are defined later in the code to pass the rails asset pipeline and 44 //jasmine while google library is not loaded 45 var defaults, gId = 0; 46 47 function initDefaults() { 48 if (!defaults) { 49 defaults = { 50 verbose: false, 51 queryLimit: { 52 attempt: 5, 53 delay: 250, // setTimeout(..., delay + random); 54 random: 250 55 }, 56 classes: { 57 Map : google.maps.Map, 58 Marker : google.maps.Marker, 59 InfoWindow : google.maps.InfoWindow, 60 Circle : google.maps.Circle, 61 Rectangle : google.maps.Rectangle, 62 OverlayView : google.maps.OverlayView, 63 StreetViewPanorama: google.maps.StreetViewPanorama, 64 KmlLayer : google.maps.KmlLayer, 65 TrafficLayer : google.maps.TrafficLayer, 66 BicyclingLayer : google.maps.BicyclingLayer, 67 GroundOverlay : google.maps.GroundOverlay, 68 StyledMapType : google.maps.StyledMapType, 69 ImageMapType : google.maps.ImageMapType 70 }, 71 map: { 72 mapTypeId : google.maps.MapTypeId.ROADMAP, 73 center: [46.578498, 2.457275], 74 zoom: 2 75 }, 76 overlay: { 77 pane: "floatPane", 78 content: "", 79 offset: { 80 x: 0, 81 y: 0 82 } 83 }, 84 geoloc: { 85 getCurrentPosition: { 86 maximumAge: 60000, 87 timeout: 5000 88 } 89 } 90 } 91 } 92 } 93 94 function globalId(id, simulate){ 95 return id !== undef ? id : "gmap3_" + (simulate ? gId+1 : gId++); 96 } 97 98 /** 99 * attach events from a container to a sender 100 * todo[ 101 * events => { eventName => function, } 102 * onces => { eventName => function, } 103 * data => mixed data 104 * ] 105 **/ 106 function attachEvents($container, args, sender, id, senders){ 107 if (args.todo.events || args.todo.onces){ 108 var context = { 109 id: id, 110 data: args.todo.data, 111 tag: args.todo.tag 112 }; 113 } 114 if (args.todo.events){ 115 $.each(args.todo.events, function(name, f){ 116 google.maps.event.addListener(sender, name, function(event) { 117 f.apply($container, [senders ? senders : sender, event, context]); 118 }); 119 }); 120 } 121 if (args.todo.onces){ 122 $.each(args.todo.onces, function(name, f){ 123 google.maps.event.addListenerOnce(sender, name, function(event) { 124 f.apply($container, [senders ? senders : sender, event, context]); 125 }); 126 }); 127 } 128 } 129 130 /***************************************************************************/ 131 /* STACK */ 132 /***************************************************************************/ 133 134 function Stack (){ 135 var st = []; 136 this.empty = function (){ 137 return !st.length; 138 }; 139 this.add = function(v){ 140 st.push(v); 141 }; 142 this.get = function (){ 143 return st.length ? st[0] : false; 144 }; 145 this.ack = function (){ 146 st.shift(); 147 }; 148 } 149 150 /***************************************************************************/ 151 /* TASK */ 152 /***************************************************************************/ 153 154 function Task(ctx, onEnd, todo){ 155 var session = {}, 156 that = this, 157 current, 158 resolve = { 159 latLng:{ // function => bool (=> address = latLng) 160 map:false, 161 marker:false, 162 infowindow:false, 163 circle:false,
164 overlay: false, 165 getlatlng: false, 166 getmaxzoom: false, 167 getelevation: false, 168 streetviewpanorama: false, 169 getaddress: true 170 }, 171 geoloc:{ 172 getgeoloc: true 173 } 174 }; 175 176 if (typeof todo === "string"){ 177 todo = unify(todo); 178 } 179 180 function unify(todo){ 181 var result = {}; 182 result[todo] = {}; 183 return result; 184 } 185 186 function next(){ 187 var k; 188 for(k in todo){ 189 if (k in session){ // already run 190 continue; 191 } 192 return k; 193 } 194 } 195 196 this.run = function (){ 197 var k, opts; 198 while(k = next()){ 199 if (typeof ctx[k] === "function"){ 200 current = k; 201 opts = $.extend(true, {}, defaults[k] || {}, todo[k].options || {}); 202 if (k in resolve.latLng){ 203 if (todo[k].values){ 204 resolveAllLatLng(todo[k].values, ctx, ctx[k], {todo:todo[k], opts:opts, session:session}); 205 } else { 206 resolveLatLng(ctx, ctx[k], resolve.latLng[k], {todo:todo[k], opts:opts, session:session}); 207 } 208 } else if (k in resolve.geoloc){ 209 geoloc(ctx, ctx[k], {todo:todo[k], opts:opts, session:session}); 210 } else { 211 ctx[k].apply(ctx, [{todo:todo[k], opts:opts, session:session}]); 212 } 213 return; // wait until ack 214 } else { 215 session[k] = null; 216 } 217 } 218 onEnd.apply(ctx, [todo, session]); 219 }; 220 221 this.ack = function(result){ 222 session[current] = result; 223 that.run.apply(that, []); 224 }; 225 } 226 227 function getKeys(obj){
228 var k, keys = []; 229 for(k in obj){ 230 keys.push(k); 231 } 232 return keys; 233 } 234 235 function tuple(args, value){ 236 var todo = {}; 237 238 // "copy" the common data 239 if (args.todo){ 240 for(var k in args.todo){ 241 if ((k !== "options") && (k !== "values")){ 242 todo[k] = args.todo[k]; 243 } 244 } 245 } 246 // "copy" some specific keys from value first else args.todo 247 var i, keys = ["data", "tag", "id", "events", "onces"]; 248 for(i=0; i<keys.length; i++){ 249 copyKey(todo, keys[i], value, args.todo); 250 } 251 252 // create an extended options 253 todo.options = $.extend({}, args.todo.options || {}, value.options || {}); 254 255 return todo; 256 } 257 258 /** 259 * copy a key content 260 **/ 261 function copyKey(target, key){ 262 for(var i=2; i<arguments.length; i++){ 263 if (key in arguments[i]){ 264 target[key] = arguments[i][key]; 265 return; 266 } 267 } 268 } 269 270 /***************************************************************************/ 271 /* GEOCODERCACHE */ 272 /***************************************************************************/ 273 274 function GeocoderCache(){ 275 var cache = []; 276 277 this.get = function(request){ 278 if (cache.length){ 279 var i, j, k, item, eq, 280 keys = getKeys(request); 281 for(i=0; i<cache.length; i++){ 282 item = cache[i]; 283 eq = keys.length == item.keys.length; 284 for(j=0; (j<keys.length) && eq; j++){ 285 k = keys[j]; 286 eq = k in item.request; 287 if (eq){ 288 if ((typeof request[k] === "object") && ("equals" in request[k]) && (typeof request[k] === "function")){ 289 eq = request[k].equals(item.request[k]); 290 } else{ 291 eq = request[k] === item.request[k]; 292 } 293 } 294 } 295 if (eq){ 296 return item.results; 297 } 298 } 299 } 300 }; 301 302 this.store = function(request, results){ 303 cache.push({request:request, keys:getKeys(request), results:results}); 304 }; 305 } 306 307 /***************************************************************************/ 308 /* OVERLAYVIEW */ 309 /***************************************************************************/ 310 function OverlayView(map, opts, latLng, $div) { 311 var that = this, listeners = []; 312 313 defaults.classes.OverlayView.call(this); 314 this.setMap(map); 315 316 this.onAdd = function() { 317 var panes = this.getPanes(); 318 if (opts.pane in panes) { 319 $(panes[opts.pane]).append($div); 320 } 321 }; 322 this.getPosition = function(){ 323 return latLng; 324 }; 325 this.draw = function() { 326 var ps = this.getProjection().fromLatLngToDivPixel(latLng); 327 $div 328 .css("left", (ps.x+opts.offset.x) + "px") 329 .css("top" , (ps.y+opts.offset.y) + "px"); 330 $.each("dblclick click mouseover mousemove mouseout mouseup mousedown".split(" "), function(i, name){ 331 listeners.push( 332 google.maps.event.addDomListener($div[0], name, function(e) { 333 $.Event(e).stopPropagation(); 334 google.maps.event.trigger(that, name, [e]); 335 }) 336 ); 337 }); 338 listeners.push( 339 google.maps.event.addDomListener($div[0], "contextmenu", function(e) { 340 $.Event(e).stopPropagation(); 341 google.maps.event.trigger(that, "rightclick", [e]); 342 }) 343 ); 344 }; 345 this.onRemove = function() { 346 for (var i = 0; i < listeners.length; i++) { 347 google.maps.event.removeListener(listeners[i]); 348 } 349 $div.remove(); 350 }; 351 this.hide = function() { 352 $div.hide(); 353 }; 354 this.show = function() { 355 $div.show(); 356 }; 357 this.toggle = function() { 358 if ($div) { 359 if ($div.is(":visible")){ 360 this.show(); 361 } else { 362 this.hide(); 363 } 364 } 365 }; 366 this.toggleDOM = function() { 367 if (this.getMap()) { 368 this.setMap(null); 369 } else {
370 this.setMap(map); 371 } 372 }; 373 this.getDOMElement = function() { 374 return $div[0]; 375 }; 376 } 377 378 /***************************************************************************/ 379 /* CLUSTERING */ 380 /***************************************************************************/ 381 382 /** 383 * Usefull to get a projection 384 * => done in a function, to let dead-code analyser works without google library loaded 385 **/ 386 function newEmptyOverlay(map){ 387 function Overlay(){ 388 this.onAdd = function(){}; 389 this.onRemove = function(){}; 390 this.draw = function(){}; 391 return defaults.classes.OverlayView.apply(this, []); 392 } 393 Overlay.prototype = defaults.classes.OverlayView.prototype; 394 var obj = new Overlay(); 395 obj.setMap(map); 396 return obj; 397 } 398 399 /** 400 * Class InternalClusterer 401 * This class manage clusters thanks to "todo" objects 402 * 403 * Note: 404 * Individuals marker are created on the fly thanks to the todo objects, they are 405 * first set to null to keep the indexes synchronised with the todo list 406 * This is the "display" function, set by the gmap3 object, which uses theses data 407 * to create markers when clusters are not required 408 * To remove a marker, the objects are deleted and set not null in arrays 409 * markers[key] 410 * = null : marker exist but has not been displayed yet 411 * = false : marker has been removed 412 **/ 413 function InternalClusterer($container, map, radius, maxZoom){ 414 var updating = false, 415 updated = false, 416 redrawing = false, 417 ready = false, 418 enabled = true, 419 that = this, 420 events = [], 421 store = {}, // combin of index (id1-id2-...) => object 422 ids = {}, // unique id => index 423 markers = [], // index => marker 424 todos = [], // index => todo or null if removed 425 values = [], // index => value 426 overlay = newEmptyOverlay(map), 427 timer, projection, 428 ffilter, fdisplay, ferror; // callback function 429 430 main(); 431 432 /** 433 * return a marker by its id, null if not yet displayed and false if no exist or removed 434 **/ 435 this.getById = function(id){ 436 return id in ids ? markers[ids[id]] : false; 437 }; 438 439 /** 440 * remove a marker by its id 441 **/ 442 this.clearById = function(id){ 443 if (id in ids){ 444 var index = ids[id]; 445 446 if (markers[index]){ // can be null 447 markers[index].setMap(null); 448 } 449 delete markers[index]; 450 markers[index] = false; 451 452 delete todos[index]; 453 todos[index] = false; 454 455 delete values[index]; 456 values[index] = false; 457 458 delete ids[id]; 459 updated = true; 460 } 461 }; 462 463 // add a "marker todo" to the cluster 464 this.add = function(todo, value){ 465 todo.id = globalId(todo.id); 466 this.clearById(todo.id); 467 ids[todo.id] = markers.length; 468 markers.push(null); // null = marker not yet created / displayed 469 todos.push(todo); 470 values.push(value); 471 updated = true; 472 }; 473 474 // add a real marker to the cluster 475 this.addMarker = function(marker, todo){ 476 todo = todo || {}; 477 todo.id = globalId(todo.id); 478 this.clearById(todo.id); 479 if (!todo.options){ 480 todo.options = {}; 481 } 482 todo.options.position = marker.getPosition(); 483 attachEvents($container, {todo:todo}, marker, todo.id); 484 ids[todo.id] = markers.length; 485 markers.push(marker); 486 todos.push(todo); 487 values.push(todo.data || {}); 488 updated = true; 489 }; 490 491 // return a "marker todo" by its index 492 this.todo = function(index){ 493 return todos[index]; 494 }; 495 496 // return a "marker value" by its index 497 this.value = function(index){ 498 return values[index]; 499 }; 500 501 // return a marker by its index 502 this.marker = function(index){ 503 return markers[index]; 504 }; 505 506 // store a new marker instead if the default "false" 507 this.setMarker = function(index, marker){ 508 markers[index] = marker; 509 }; 510 511 // link the visible overlay to the logical data (to hide overlays later) 512 this.store = function(cluster, obj, shadow){ 513 store[cluster.ref] = {obj:obj, shadow:shadow}; 514 }; 515 516 // free all objects 517 this.free = function(){ 518 for(var i = 0; i < events.length; i++){ 519 google.maps.event.removeListener(events[i]); 520 } 521 events = []; 522 523 $.each(store, function(key){ 524 flush(key); 525 }); 526 store = {}; 527 528 $.each(todos, function(i){ 529 todos[i] = null; 530 }); 531 todos = []; 532 533 $.each(markers, function(i){ 534 if (markers[i]){ // false = removed 535 markers[i].setMap(null); 536 delete markers[i];
537 } 538 }); 539 markers = []; 540 541 $.each(values, function(i){ 542 delete values[i]; 543 }); 544 values = []; 545 546 ids = {}; 547 }; 548 549 // link the display function 550 this.filter = function(f){ 551 ffilter = f; 552 redraw(); 553 }; 554 555 // enable/disable the clustering feature 556 this.enable = function(value){ 557 if (enabled != value){ 558 enabled = value; 559 redraw(); 560 } 561 }; 562 563 // link the display function 564 this.display = function(f){ 565 fdisplay = f; 566 }; 567 568 // link the errorfunction 569 this.error = function(f){ 570 ferror = f; 571 }; 572 573 // lock the redraw 574 this.beginUpdate = function () { 575 updating = true; 576 }; 577 578 // unlock the redraw 579 this.endUpdate = function(){ 580 updating = false; 581 if (updated){ 582 redraw(); 583 } 584 }; 585 586 // bind events 587 function main(){ 588 projection = overlay.getProjection(); 589 if (!projection){ 590 setTimeout(function(){ 591 main.apply(that, []); 592 }, 593 25); 594 return; 595 } 596 ready = true; 597 events.push(google.maps.event.addListener(map, "zoom_changed", function(){delayRedraw();})); 598 events.push(google.maps.event.addListener(map, "bounds_changed", function(){delayRedraw();})); 599 redraw(); 600 } 601 602 // flush overlays 603 function flush(key){ 604 if (typeof store[key] === "object"){ // is overlay 605 if (typeof(store[key].obj.setMap) === "function") { 606 store[key].obj.setMap(null); 607 } 608 if (typeof(store[key].obj.remove) === "function") { 609 store[key].obj.remove(); 610 } 611 if (typeof(store[key].shadow.remove) === "function") { 612 store[key].obj.remove(); 613 } 614 if (typeof(store[key].shadow.setMap) === "function") { 615 store[key].shadow.setMap(null); 616 } 617 delete store[key].obj; 618 delete store[key].shadow; 619 } else if (markers[key]){ // marker not removed 620 markers[key].setMap(null); 621 // don't remove the marker object, it may be displayed later 622 } 623 delete store[key]; 624 } 625 626 /** 627 * return the distance between 2 latLng couple into meters 628 * Params : 629 * Lat1, Lng1, Lat2, Lng2 630 * LatLng1, Lat2, Lng2 631 * Lat1, Lng1, LatLng2 632 * LatLng1, LatLng2 633 **/ 634 function distanceInMeter(){ 635 var lat1, lat2, lng1, lng2, e, f, g, h; 636 if (arguments[0] instanceof google.maps.LatLng){ 637 lat1 = arguments[0].lat(); 638 lng1 = arguments[0].lng(); 639 if (arguments[1] instanceof google.maps.LatLng){ 640 lat2 = arguments[1].lat(); 641 lng2 = arguments[1].lng(); 642 } else { 643 lat2 = arguments[1]; 644 lng2 = arguments[2]; 645 } 646 } else { 647 lat1 = arguments[0]; 648 lng1 = arguments[1]; 649 if (arguments[2] instanceof google.maps.LatLng){ 650 lat2 = arguments[2].lat(); 651 lng2 = arguments[2].lng(); 652 } else { 653 lat2 = arguments[2]; 654 lng2 = arguments[3]; 655 } 656 } 657 e = Math.PI*lat1/180; 658 f = Math.PI*lng1/180; 659 g = Math.PI*lat2/180; 660 h = Math.PI*lng2/180; 661 return 1000*6371 * Math.acos(Math.min(Math.cos(e)*Math.cos(g)*Math.cos(f)*Math.cos(h)+Math.cos(e)*Math.sin(f)*Math.cos(g)*Math.sin(h)+Math.sin(e)*Math.sin(g),1)); 662 } 663 664 // extend the visible bounds 665 function extendsMapBounds(){ 666 var radius = distanceInMeter(map.getCenter(), map.getBounds().getNorthEast()), 667 circle = new google.maps.Circle({ 668 center: map.getCenter(), 669 radius: 1.25 * radius // + 25% 670 }); 671 return circle.getBounds(); 672 } 673 674 // return an object where keys are store keys 675 function getStoreKeys(){ 676 var keys = {}, k; 677 for(k in store){ 678 keys[k] = true; 679 } 680 return keys; 681 } 682 683 // async the delay function 684 function delayRedraw(){ 685 clearTimeout(timer); 686 timer = setTimeout(function(){ 687 redraw(); 688 }, 689 25); 690 } 691
692 // generate bounds extended by radius 693 function extendsBounds(latLng) { 694 var p = projection.fromLatLngToDivPixel(latLng), 695 ne = projection.fromDivPixelToLatLng(new google.maps.Point(p.x+radius, p.y-radius)), 696 sw = projection.fromDivPixelToLatLng(new google.maps.Point(p.x-radius, p.y+radius)); 697 return new google.maps.LatLngBounds(sw, ne); 698 } 699 700 // run the clustering process and call the display function 701 function redraw(){ 702 if (updating || redrawing || !ready){ 703 return; 704 } 705 706 var keys = [], used = {}, 707 zoom = map.getZoom(), 708 forceDisabled = (maxZoom !== undef) && (zoom > maxZoom), 709 previousKeys = getStoreKeys(), 710 i, j, k, lat, lng, indexes, previous, check = false, bounds, cluster; 711 712 // reset flag 713 updated = false; 714 715 if (zoom > 3){ 716 // extend the bounds of the visible map to manage clusters near the boundaries 717 bounds = extendsMapBounds(); 718 719 // check contain only if boundaries are valid
720 check = bounds.getSouthWest().lng() < bounds.getNorthEast().lng(); 721 } 722 723 // calculate positions of "visibles" markers (in extended bounds) 724 $.each(todos, function(index, todo){ 725 if (!todo){ // marker removed 726 return; 727 } 728 if (check && (!bounds.contains(todo.options.position))){ 729 return; 730 } 731 if (ffilter && !ffilter(values[index])){ 732 return; 733 } 734 keys.push(index); 735 }); 736 737 // for each "visible" marker, search its neighbors to create a cluster 738 // we can't do a classical "for" loop, because, analysis can bypass a marker while focusing on cluster 739 while(1){ 740 i=0; 741 while(used[i] && (i<keys.length)){ // look for the next marker not used 742 i++; 743 } 744 if (i == keys.length){ 745 break; 746 } 747 748 indexes = []; 749 750 if (enabled && !forceDisabled){ 751 do{ 752 previous = indexes; 753 indexes = []; 754 755 if (previous.length){ 756 // re-evaluates center 757 lat = lng = 0; 758 for(k=0; k<previous.length; k++){ 759 lat += todos[ keys[previous[k]] ].options.position.lat(); 760 lng += todos[ keys[previous[k]] ].options.position.lng(); 761 } 762 lat /= previous.length; 763 lng /= previous.length; 764 bounds = extendsBounds(new google.maps.LatLng(lat, lng)); 765 } else { 766 bounds = extendsBounds(todos[ keys[i] ].options.position); 767 } 768 769 for(j=i; j<keys.length; j++){ 770 if (used[j]){ 771 continue; 772 } 773 if (bounds.contains(todos[ keys[j] ].options.position)){ 774 indexes.push(j); 775 } 776 } 777 } while( (previous.length < indexes.length) && (indexes.length > 1) ); 778 } else { 779 for(j=i; j<keys.length; j++){ 780 if (used[j]){ 781 continue; 782 } 783 indexes.push(j); 784 break; 785 } 786 } 787 788 cluster = {latLng:bounds.getCenter(), indexes:[], ref:[]}; 789 for(k=0; k<indexes.length; k++){ 790 used[ indexes[k] ] = true; 791 cluster.indexes.push(keys[indexes[k]]); 792 cluster.ref.push(keys[indexes[k]]); 793 } 794 cluster.ref = cluster.ref.join("-"); 795 796 if (cluster.ref in previousKeys){ // cluster doesn't change 797 delete previousKeys[cluster.ref]; // remove this entry, these still in this array will be removed 798 } else { // cluster is new 799 if (indexes.length === 1){ // alone markers are not stored, so need to keep the key (else, will be displayed every time and marker will blink) 800 store[cluster.ref] = true; 801 } 802 // use a closure to async the display call make faster the process of cle clustering 803 (function(cl){ 804 setTimeout(function(){ 805 fdisplay(cl); // while displaying, will use store to link object 806 }, 1); 807 })(cluster); 808 } 809 } 810 811 // flush the previous overlays which are not still used 812 $.each(previousKeys, function(key){ 813 flush(key); 814 }); 815 redrawing = false; 816 } 817 } 818 819 /** 820 * Class Clusterer 821 * a facade with limited method for external use 822 **/ 823 function Clusterer(id, internalClusterer){ 824 this.id = function(){ 825 return id; 826 }; 827 this.filter = function(f){ 828 internalClusterer.filter(f); 829 }; 830 this.enable = function(){ 831 internalClusterer.enable(true); 832 }; 833 this.disable = function(){ 834 internalClusterer.enable(false); 835 }; 836 this.add = function(marker, todo, lock){ 837 if (!lock) { 838 internalClusterer.beginUpdate(); 839 } 840 internalClusterer.addMarker(marker, todo); 841 if (!lock) { 842 internalClusterer.endUpdate(); 843 } 844 }; 845 this.getById = function(id){ 846 return internalClusterer.getById(id); 847 }; 848 this.clearById = function(id, lock){ 849 if (!lock) { 850 internalClusterer.beginUpdate(); 851 } 852 internalClusterer.clearById(id); 853 if (!lock) { 854 internalClusterer.endUpdate(); 855 } 856 }; 857 } 858 /***************************************************************************/ 859 /* STORE */ 860 /***************************************************************************/ 861 862 function Store(){ 863 var store = {}, // name => [id, ...] 864 objects = {}; // id => object 865 866 function ftag(tag){ 867 if (tag){ 868 if (typeof tag === "function"){ 869 return tag; 870 } 871 tag = array(tag); 872 return function(val){ 873 if (val === undef){
874 return false; 875 } 876 if (typeof val === "object"){ 877 for(var i=0; i<val.length; i++){ 878 if($.inArray(val[i], tag) >= 0){ 879 return true; 880 } 881 } 882 return false; 883 } 884 return $.inArray(val, tag) >= 0; 885 } 886 } 887 } 888 889 function normalize(res) { 890 return { 891 id: res.id, 892 name: res.name, 893 object:res.obj, 894 tag:res.tag, 895 data:res.data 896 }; 897 } 898 899 /** 900 * add a mixed to the store 901 **/ 902 this.add = function(args, name, obj, sub){ 903 var todo = args.todo || {}, 904 id = globalId(todo.id); 905 if (!store[name]){ 906 store[name] = []; 907 } 908 if (id in objects){ // object already exists: remove it 909 this.clearById(id); 910 } 911 objects[id] = {obj:obj, sub:sub, name:name, id:id, tag:todo.tag, data:todo.data}; 912 store[name].push(id); 913 return id; 914 }; 915 916 /** 917 * return a stored object by its id 918 **/ 919 this.getById = function(id, sub, full){ 920 if (id in objects){ 921 if (sub) { 922 return objects[id].sub 923 } else if (full) { 924 return normalize(objects[id]); 925 } 926 return objects[id].obj; 927 928 } 929 return false; 930 }; 931 932 /** 933 * return a stored value 934 **/ 935 this.get = function(name, last, tag, full){ 936 var n, id, check = ftag(tag); 937 if (!store[name] || !store[name].length){ 938 return null; 939 } 940 n = store[name].length; 941 while(n){ 942 n--; 943 id = store[name][last ? n : store[name].length - n - 1]; 944 if (id && objects[id]){ 945 if (check && !check(objects[id].tag)){ 946 continue; 947 } 948 return full ? normalize(objects[id]) : objects[id].obj; 949 } 950 } 951 return null; 952 }; 953 954 /** 955 * return all stored values 956 **/ 957 this.all = function(name, tag, full){ 958 var result = [], 959 check = ftag(tag), 960 find = function(n){ 961 var i, id; 962 for(i=0; i<store[n].length; i++){ 963 id = store[n][i]; 964 if (id && objects[id]){ 965 if (check && !check(objects[id].tag)){ 966 continue; 967 } 968 result.push(full ? normalize(objects[id]) : objects[id].obj); 969 } 970 } 971 }; 972 if (name in store){ 973 find(name); 974 } else if (name === undef){ // internal use only 975 for(name in store){ 976 find(name); 977 } 978 } 979 return result; 980 }; 981 982 /** 983 * hide and remove an object 984 **/ 985 function rm(obj){ 986 // Google maps element 987 if (typeof(obj.setMap) === "function") { 988 obj.setMap(null); 989 } 990 // jQuery 991 if (typeof(obj.remove) === "function") { 992 obj.remove(); 993 } 994 // internal (cluster) 995 if (typeof(obj.free) === "function") { 996 obj.free(); 997 } 998 obj = null; 999 } 1000 1001 /** 1002 * remove one object from the store 1003 **/ 1004 this.rm = function(name, check, pop){ 1005 var idx, id; 1006 if (!store[name]) { 1007 return false; 1008 } 1009 if (check){ 1010 if (pop){ 1011 for(idx = store[name].length - 1; idx >= 0; idx--){ 1012 id = store[name][idx]; 1013 if ( check(objects[id].tag) ){ 1014 break; 1015 } 1016 } 1017 } else { 1018 for(idx = 0; idx < store[name].length; idx++){ 1019 id = store[name][idx]; 1020 if (check(objects[id].tag)){ 1021 break; 1022 } 1023 } 1024 } 1025 } else { 1026 idx = pop ? store[name].length - 1 : 0; 1027 } 1028 if ( !(idx in store[name]) ) { 1029 return false; 1030 } 1031 return this.clearById(store[name][idx], idx); 1032 }; 1033 1034 /** 1035 * remove object from the store by its id 1036 **/ 1037 this.clearById = function(id, idx){ 1038 if (id in objects){ 1039 var i, name = objects[id].name; 1040 for(i=0; idx === undef && i<store[name].length; i++){ 1041 if (id === store[name][i]){ 1042 idx = i; 1043 } 1044 } 1045 rm(objects[id].obj); 1046 if(objects[id].sub){ 1047 rm(objects[id].sub); 1048 } 1049 delete objects[id]; 1050 store[name].splice(idx, 1); 1051 return true; 1052 } 1053 return false; 1054 }; 1055 1056 /** 1057 * return an object from a container object in the store by its id 1058 * ! for now, only cluster manage this feature 1059 **/ 1060 this.objGetById = function(id){ 1061 var result; 1062 if (store["clusterer"]) { 1063 for(var idx in store["clusterer"]){ 1064 if ((result = objects[store["clusterer"][idx]].obj.getById(id)) !== false){ 1065 return result; 1066 } 1067 } 1068 } 1069 return false; 1070 }; 1071 1072 /** 1073 * remove object from a container object in the store by its id 1074 * ! for now, only cluster manage this feature 1075 **/ 1076 this.objClearById = function(id){ 1077 if (store["clusterer"]) { 1078 for(var idx in store["clusterer"]){ 1079 if (objects[store["clusterer"][idx]].obj.clearById(id)){ 1080 return true; 1081 } 1082 } 1083 } 1084 return null; 1085 }; 1086 1087 /** 1088 * remove objects from the store 1089 **/ 1090 this.clear = function(list, last, first, tag){ 1091 var k, i, name, check = ftag(tag); 1092 if (!list || !list.length){ 1093 list = []; 1094 for(k in store){ 1095 list.push(k); 1096 } 1097 } else { 1098 list = array(list); 1099 } 1100 for(i=0; i<list.length; i++){ 1101 if (list[i]){ 1102 name = list[i]; 1103 if (!store[name]){ 1104 continue; 1105 } 1106 if (last){ 1107 this.rm(name, check, true); 1108 } else if (first){ 1109 this.rm(name, check, false); 1110 } else { // all 1111 while(this.rm(name, check, false)); 1112 } 1113 } 1114 } 1115 }; 1116 } 1117 1118 /***************************************************************************/ 1119 /* GMAP3 GLOBALS */ 1120 /***************************************************************************/ 1121 1122 var services = {}, 1123 geocoderCache = new GeocoderCache(); 1124 1125 //-----------------------------------------------------------------------// 1126 // Service tools 1127 //-----------------------------------------------------------------------// 1128 1129 function geocoder(){ 1130 if (!services.geocoder) { 1131 services.geocoder = new google.maps.Geocoder(); 1132 } 1133 return services.geocoder; 1134 } 1135 1136 function directionsService(){ 1137 if (!services.directionsService) { 1138 services.directionsService = new google.maps.DirectionsService(); 1139 } 1140 return services.directionsService; 1141 } 1142 1143 function elevationService(){ 1144 if (!services.elevationService) { 1145 services.elevationService = new google.maps.ElevationService(); 1146 } 1147 return services.elevationService; 1148 } 1149
1150 function maxZoomService(){ 1151 if (!services.maxZoomService) { 1152 services.maxZoomService = new google.maps.MaxZoomService(); 1153 } 1154 return services.maxZoomService; 1155 } 1156 1157 function distanceMatrixService(){ 1158 if (!services.distanceMatrixService) { 1159 services.distanceMatrixService = new google.maps.DistanceMatrixService(); 1160 } 1161 return services.distanceMatrixService; 1162 } 1163 1164 //-----------------------------------------------------------------------// 1165 // Unit tools 1166 //-----------------------------------------------------------------------// 1167 1168 function error(){ 1169 if (defaults.verbose){ 1170 var i, err = []; 1171 if (window.console && (typeof console.log === "function") ){ 1172 for(i=0; i<arguments.length; i++){ 1173 err.push(arguments[i]); 1174 } 1175 console.log.apply(console, err); 1176 } else { 1177 err = ""; 1178 for(i=0; i<arguments.length; i++){ 1179 err += arguments[i].toString() + " " ; 1180 } 1181 alert(err); 1182 } 1183 } 1184 } 1185 1186 /** 1187 * return true if mixed is usable as number 1188 **/ 1189 function numeric(mixed){
1190 return (typeof(mixed) === "number" || typeof(mixed) === "string") && mixed !== "" && !isNaN(mixed); 1191 } 1192 1193 /** 1194 * convert data to array 1195 **/ 1196 function array(mixed){ 1197 var k, a = []; 1198 if (mixed !== undef){ 1199 if (typeof(mixed) === "object"){ 1200 if (typeof(mixed.length) === "number") { 1201 a = mixed; 1202 } else { 1203 for(k in mixed) { 1204 a.push(mixed[k]); 1205 } 1206 } 1207 } else{ 1208 a.push(mixed); 1209 } 1210 } 1211 return a; 1212 } 1213 1214 /** 1215 * convert mixed [ lat, lng ] objet to google.maps.LatLng 1216 **/ 1217 function toLatLng (mixed, emptyReturnMixed, noFlat){ 1218 var empty = emptyReturnMixed ? mixed : null; 1219 if (!mixed || (typeof mixed === "string")){ 1220 return empty; 1221 } 1222 // defined latLng 1223 if (mixed.latLng) { 1224 return toLatLng(mixed.latLng); 1225 } 1226 // google.maps.LatLng object 1227 if (mixed instanceof google.maps.LatLng) { 1228 return mixed; 1229 } 1230 // {lat:X, lng:Y} object 1231 else if ( numeric(mixed.lat) ) { 1232 return new google.maps.LatLng(mixed.lat, mixed.lng); 1233 } 1234 // [X, Y] object 1235 else if ( !noFlat && $.isArray(mixed)){ 1236 if ( !numeric(mixed[0]) || !numeric(mixed[1]) ) { 1237 return empty; 1238 } 1239 return new google.maps.LatLng(mixed[0], mixed[1]); 1240 } 1241 return empty; 1242 } 1243 1244 /** 1245 * convert mixed [ sw, ne ] object by google.maps.LatLngBounds 1246 **/ 1247 function toLatLngBounds(mixed){ 1248 var ne, sw; 1249 if (!mixed || mixed instanceof google.maps.LatLngBounds) { 1250 return mixed || null; 1251 } 1252 if ($.isArray(mixed)){ 1253 if (mixed.length == 2){ 1254 ne = toLatLng(mixed[0]); 1255 sw = toLatLng(mixed[1]); 1256 } else if (mixed.length == 4){ 1257 ne = toLatLng([mixed[0], mixed[1]]); 1258 sw = toLatLng([mixed[2], mixed[3]]); 1259 } 1260 } else { 1261 if ( ("ne" in mixed) && ("sw" in mixed) ){ 1262 ne = toLatLng(mixed.ne); 1263 sw = toLatLng(mixed.sw); 1264 } else if ( ("n" in mixed) && ("e" in mixed) && ("s" in mixed) && ("w" in mixed) ){ 1265 ne = toLatLng([mixed.n, mixed.e]); 1266 sw = toLatLng([mixed.s, mixed.w]); 1267 } 1268 } 1269 if (ne && sw){ 1270 return new google.maps.LatLngBounds(sw, ne); 1271 } 1272 return null; 1273 } 1274 1275 /** 1276 * resolveLatLng 1277 **/ 1278 function resolveLatLng(ctx, method, runLatLng, args, attempt){ 1279 var latLng = runLatLng ? toLatLng(args.todo, false, true) : false, 1280 conf = latLng ? {latLng:latLng} : (args.todo.address ? (typeof(args.todo.address) === "string" ? {address:args.todo.address} : args.todo.address) : false), 1281 cache = conf ? geocoderCache.get(conf) : false, 1282 that = this; 1283 if (conf){ 1284 attempt = attempt || 0; // convert undefined to int 1285 if (cache){ 1286 args.latLng = cache.results[0].geometry.location; 1287 args.results = cache.results; 1288 args.status = cache.status; 1289 method.apply(ctx, [args]); 1290 } else { 1291 if (conf.location){ 1292 conf.location = toLatLng(conf.location); 1293 } 1294 if (conf.bounds){ 1295 conf.bounds = toLatLngBounds(conf.bounds); 1296 } 1297 geocoder().geocode( 1298 conf, 1299 function(results, status) { 1300 if (status === google.maps.GeocoderStatus.OK){ 1301 geocoderCache.store(conf, {results:results, status:status}); 1302 args.latLng = results[0].geometry.location; 1303 args.results = results; 1304 args.status = status; 1305 method.apply(ctx, [args]); 1306 } else if ( (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) && (attempt < defaults.queryLimit.attempt) ){ 1307 setTimeout( 1308 function(){ 1309 resolveLatLng.apply(that, [ctx, method, runLatLng, args, attempt+1]); 1310 }, 1311 defaults.queryLimit.delay + Math.floor(Math.random() * defaults.queryLimit.random) 1312 ); 1313 } else { 1314 error("geocode failed", status, conf); 1315 args.latLng = args.results = false; 1316 args.status = status; 1317 method.apply(ctx, [args]); 1318 } 1319 } 1320 ); 1321 } 1322 } else {
1323 args.latLng = toLatLng(args.todo, false, true); 1324 method.apply(ctx, [args]); 1325 } 1326 } 1327 1328 function resolveAllLatLng(list, ctx, method, args){ 1329 var that = this, i = -1; 1330 1331 function resolve(){ 1332 // look for next address to resolve 1333 do{ 1334 i++; 1335 }while( (i < list.length) && !("address" in list[i]) ); 1336 1337 // no address found, so run method 1338 if (i >= list.length){ 1339 method.apply(ctx, [args]); 1340 return; 1341 } 1342 1343 resolveLatLng( 1344 that, 1345 function(args){ 1346 delete args.todo; 1347 $.extend(list[i], args); 1348 resolve.apply(that, []); // resolve next (using apply avoid too much recursion) 1349 }, 1350 true, 1351 {todo:list[i]} 1352 ); 1353 } 1354 resolve(); 1355 } 1356 1357 /** 1358 * geolocalise the user and return a LatLng 1359 **/ 1360 function geoloc(ctx, method, args){ 1361 var is_echo = false; // sometime, a kind of echo appear, this trick will notice once the first call is run to ignore the next one 1362 if (navigator && navigator.geolocation){ 1363 navigator.geolocation.getCurrentPosition( 1364 function(pos) { 1365 if (is_echo){ 1366 return; 1367 } 1368 is_echo = true; 1369 args.latLng = new google.maps.LatLng(pos.coords.latitude,pos.coords.longitude); 1370 method.apply(ctx, [args]); 1371 }, 1372 function() { 1373 if (is_echo){ 1374 return; 1375 } 1376 is_echo = true; 1377 args.latLng = false; 1378 method.apply(ctx, [args]); 1379 }, 1380 args.opts.getCurrentPosition 1381 ); 1382 } else { 1383 args.latLng = false; 1384 method.apply(ctx, [args]); 1385 } 1386 } 1387 1388 /***************************************************************************/ 1389 /* GMAP3 */ 1390 /***************************************************************************/ 1391 1392 function Gmap3($this){ 1393 var that = this, 1394 stack = new Stack(), 1395 store = new Store(), 1396 map = null, 1397 task; 1398 1399 //-----------------------------------------------------------------------// 1400 // Stack tools 1401 //-----------------------------------------------------------------------// 1402 1403 /** 1404 * store actions to execute in a stack manager 1405 **/ 1406 this._plan = function(list){ 1407 for(var k = 0; k < list.length; k++) { 1408 stack.add(new Task(that, end, list[k])); 1409 } 1410 run(); 1411 }; 1412 1413 /** 1414 * if not running, start next action in stack 1415 **/ 1416 function run(){ 1417 if (!task && (task = stack.get())){ 1418 task.run(); 1419 } 1420 } 1421 1422 /** 1423 * called when action in finished, to acknoledge the current in stack and start next one 1424 **/ 1425 function end(){ 1426 task = null; 1427 stack.ack(); 1428 run.call(that); // restart to high level scope 1429 } 1430 1431 //-----------------------------------------------------------------------// 1432 // Tools 1433 //-----------------------------------------------------------------------// 1434 1435 /** 1436 * execute callback functions 1437 **/ 1438 function callback(args){ 1439 var i, params = []; 1440 for(i=1; i<arguments.length; i++){ 1441 params.push(arguments[i]); 1442 } 1443 if (typeof args.todo.callback === "function") { 1444 args.todo.callback.apply($this, params); 1445 } else if (typeof args.todo.callback === "object") { 1446 $.each(args.todo.callback, function(i, cb){ 1447 if (typeof cb === "function") { 1448 cb.apply($this, params); 1449 } 1450 }); 1451 } 1452 } 1453 1454 /** 1455 * execute ending functions 1456 **/ 1457 function manageEnd(args, obj, id){ 1458 if (id){ 1459 attachEvents($this, args, obj, id); 1460 } 1461 callback(args, obj); 1462 task.ack(obj); 1463 } 1464 1465 /** 1466 * initialize the map if not yet initialized 1467 **/ 1468 function newMap(latLng, args){ 1469 args = args || {}; 1470 if (map) { 1471 if (args.todo && args.todo.options){ 1472 $.each("heading mapTypeId tilt zoom".split(" "), function(i, name){ 1473 if (name in args.todo.options){ 1474 map["set" + name.charAt(0).toUpperCase() + name.substr(1)](args.todo.options[name]); 1475 } 1476 }); 1477 if (args.todo.options.center){
1478 map.setCenter(toLatLng(args.todo.options.center)); 1479 } 1480 } 1481 } else { 1482 var opts = args.opts || $.extend(true, {}, defaults.map, args.todo && args.todo.options ? args.todo.options : {}); 1483 opts.center = latLng || toLatLng(opts.center); 1484 map = new defaults.classes.Map($this.get(0), opts); 1485 } 1486 } 1487 1488 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 1489 => function with latLng resolution 1490 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ 1491 1492 /** 1493 * Initialize google.maps.Map object 1494 **/ 1495 this.map = function(args){ 1496 newMap(args.latLng, args); 1497 attachEvents($this, args, map); 1498 manageEnd(args, map); 1499 }; 1500 1501 /** 1502 * destroy an existing instance 1503 **/ 1504 this.destroy = function(args){ 1505 store.clear(); 1506 $this.empty(); 1507 if (map){ 1508 map = null; 1509 } 1510 manageEnd(args, true); 1511 }; 1512 1513 /** 1514 * add an infowindow 1515 **/ 1516 this.infowindow = function(args){ 1517 var objs = [], multiple = "values" in args.todo; 1518 if (!multiple){ 1519 if (args.latLng) { 1520 args.opts.position = args.latLng; 1521 } else if (args.opts.position){ 1522 args.opts.position = toLatLng(args.opts.position); 1523 } 1524 args.todo.values = [{options:args.opts}]; 1525 } 1526 $.each(args.todo.values, function(i, value){ 1527 var id, obj, todo = tuple(args, value); 1528 if (!map){ 1529 newMap(todo.options.position); 1530 } 1531 obj = new defaults.classes.InfoWindow(todo.options); 1532 if (obj && ((todo.open === undef) || todo.open)){ 1533 if (multiple){ 1534 obj.open(map, todo.anchor ? todo.anchor : undef); 1535 } else { 1536 obj.open(map, todo.anchor ? todo.anchor : (args.latLng ? undef : (args.session.marker ? args.session.marker : undef))); 1537 } 1538 } 1539 objs.push(obj); 1540 id = store.add({todo:todo}, "infowindow", obj); 1541 attachEvents($this, {todo:todo}, obj, id); 1542 }); 1543 manageEnd(args, multiple ? objs : objs[0]); 1544 }; 1545 1546 /** 1547 * add a circle 1548 **/ 1549 this.circle = function(args){ 1550 var objs = [], multiple = "values" in args.todo; 1551 if (!multiple){ 1552 args.opts.center = args.latLng || toLatLng(args.opts.center); 1553 args.todo.values = [{options:args.opts}]; 1554 } 1555 if (!args.todo.values.length){ 1556 manageEnd(args, false); 1557 return; 1558 } 1559 $.each(args.todo.values, function(i, value){ 1560 var id, obj, todo = tuple(args, value); 1561 todo.options.center = todo.options.center ? toLatLng(todo.options.center) : toLatLng(value); 1562 if (!map){ 1563 newMap(todo.options.center); 1564 } 1565 todo.options.map = map; 1566 obj = new defaults.classes.Circle(todo.options); 1567 objs.push(obj); 1568 id = store.add({todo:todo}, "circle", obj); 1569 attachEvents($this, {todo:todo}, obj, id); 1570 }); 1571 manageEnd(args, multiple ? objs : objs[0]); 1572 }; 1573 1574 /** 1575 * add an overlay 1576 **/ 1577 this.overlay = function(args, internal){ 1578 var id, obj, 1579 $div = $(document.createElement("div")) 1580 .css("border", "none") 1581 .css("borderWidth", "0px") 1582 .css("position", "absolute"); 1583 $div.append(args.opts.content); 1584 OverlayView.prototype = new defaults.classes.OverlayView(); 1585 obj = new OverlayView(map, args.opts, args.latLng, $div); 1586 $div = null; // memory leak 1587 if (internal){ 1588 return obj; 1589 } 1590 id = store.add(args, "overlay", obj); 1591 manageEnd(args, obj, id); 1592 }; 1593 1594 /** 1595 * returns address structure from latlng 1596 **/ 1597 this.getaddress = function(args){ 1598 callback(args, args.results, args.status); 1599 task.ack(); 1600 }; 1601 1602 /** 1603 * returns latlng from an address 1604 **/ 1605 this.getlatlng = function(args){ 1606 callback(args, args.results, args.status); 1607 task.ack(); 1608 }; 1609 1610 /** 1611 * return the max zoom of a location 1612 **/ 1613 this.getmaxzoom = function(args){ 1614 maxZoomService().getMaxZoomAtLatLng( 1615 args.latLng, 1616 function(result) { 1617 callback(args, result.status === google.maps.MaxZoomStatus.OK ? result.zoom : false, status); 1618 task.ack(); 1619 } 1620 ); 1621 }; 1622 1623 /** 1624 * return the elevation of a location 1625 **/ 1626 this.getelevation = function(args){ 1627 var i, locations = [], 1628 f = function(results, status){ 1629 callback(args, status === google.maps.ElevationStatus.OK ? results : false, status); 1630 task.ack(); 1631 }; 1632 1633 if (args.latLng){ 1634 locations.push(args.latLng); 1635 } else { 1636 locations = array(args.todo.locations || []); 1637 for(i=0; i<locations.length; i++){ 1638 locations[i] = toLatLng(locations[i]); 1639 } 1640 } 1641 if (locations.length){ 1642 elevationService().getElevationForLocations({locations:locations}, f); 1643 } else { 1644 if (args.todo.path && args.todo.path.length){ 1645 for(i=0; i<args.todo.path.length; i++){
1646 locations.push(toLatLng(args.todo.path[i])); 1647 } 1648 } 1649 if (locations.length){ 1650 elevationService().getElevationAlongPath({path:locations, samples:args.todo.samples}, f); 1651 } else { 1652 task.ack(); 1653 } 1654 } 1655 }; 1656 1657 /* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 1658 => function without latLng resolution 1659 = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = */ 1660 1661 /** 1662 * define defaults values 1663 **/ 1664 this.defaults = function(args){ 1665 $.each(args.todo, function(name, value){ 1666 if (typeof defaults[name] === "object"){ 1667 defaults[name] = $.extend({}, defaults[name], value); 1668 } else { 1669 defaults[name] = value; 1670 } 1671 }); 1672 task.ack(true); 1673 }; 1674 1675 /** 1676 * add a rectangle 1677 **/ 1678 this.rectangle = function(args){ 1679 var objs = [], multiple = "values" in args.todo; 1680 if (!multiple){ 1681 args.todo.values = [{options:args.opts}]; 1682 } 1683 if (!args.todo.values.length){ 1684 manageEnd(args, false); 1685 return; 1686 } 1687 $.each(args.todo.values, function(i, value){ 1688 var id, obj, todo = tuple(args, value); 1689 todo.options.bounds = todo.options.bounds ? toLatLngBounds(todo.options.bounds) : toLatLngBounds(value); 1690 if (!map){ 1691 newMap(todo.options.bounds.getCenter()); 1692 } 1693 todo.options.map = map; 1694 1695 obj = new defaults.classes.Rectangle(todo.options); 1696 objs.push(obj); 1697 id = store.add({todo:todo}, "rectangle", obj); 1698 attachEvents($this, {todo:todo}, obj, id); 1699 }); 1700 manageEnd(args, multiple ? objs : objs[0]); 1701 }; 1702 1703 /** 1704 * add a polygone / polyline 1705 **/ 1706 function poly(args, poly, path){ 1707 var objs = [], multiple = "values" in args.todo; 1708 if (!multiple){ 1709 args.todo.values = [{options:args.opts}]; 1710 } 1711 if (!args.todo.values.length){ 1712 manageEnd(args, false); 1713 return; 1714 } 1715 newMap(); 1716 $.each(args.todo.values, function(_, value){ 1717 var id, i, j, obj, todo = tuple(args, value); 1718 if (todo.options[path]){ 1719 if (todo.options[path][0][0] && $.isArray(todo.options[path][0][0])){ 1720 for(i=0; i<todo.options[path].length; i++){ 1721 for(j=0; j<todo.options[path][i].length; j++){ 1722 todo.options[path][i][j] = toLatLng(todo.options[path][i][j]); 1723 } 1724 } 1725 } else { 1726 for(i=0; i<todo.options[path].length; i++){ 1727 todo.options[path][i] = toLatLng(todo.options[path][i]); 1728 } 1729 } 1730 } 1731 todo.options.map = map; 1732 obj = new google.maps[poly](todo.options); 1733 objs.push(obj); 1734 id = store.add({todo:todo}, poly.toLowerCase(), obj); 1735 attachEvents($this, {todo:todo}, obj, id); 1736 }); 1737 manageEnd(args, multiple ? objs : objs[0]); 1738 } 1739 1740 this.polyline = function(args){ 1741 poly(args, "Polyline", "path"); 1742 }; 1743 1744 this.polygon = function(args){ 1745 poly(args, "Polygon", "paths"); 1746 }; 1747 1748 /** 1749 * add a traffic layer 1750 **/ 1751 this.trafficlayer = function(args){ 1752 newMap(); 1753 var obj = store.get("trafficlayer"); 1754 if (!obj){ 1755 obj = new defaults.classes.TrafficLayer(); 1756 obj.setMap(map); 1757 store.add(args, "trafficlayer", obj); 1758 } 1759 manageEnd(args, obj); 1760 }; 1761 1762 /** 1763 * add a bicycling layer 1764 **/ 1765 this.bicyclinglayer = function(args){ 1766 newMap(); 1767 var obj = store.get("bicyclinglayer"); 1768 if (!obj){ 1769 obj = new defaults.classes.BicyclingLayer(); 1770 obj.setMap(map); 1771 store.add(args, "bicyclinglayer", obj); 1772 } 1773 manageEnd(args, obj); 1774 }; 1775 1776 /** 1777 * add a ground overlay 1778 **/ 1779 this.groundoverlay = function(args){ 1780 args.opts.bounds = toLatLngBounds(args.opts.bounds); 1781 if (args.opts.bounds){ 1782 newMap(args.opts.bounds.getCenter()); 1783 } 1784 var id, obj = new defaults.classes.GroundOverlay(args.opts.url, args.opts.bounds, args.opts.opts);
1785 obj.setMap(map); 1786 id = store.add(args, "groundoverlay", obj); 1787 manageEnd(args, obj, id); 1788 }; 1789 1790 /** 1791 * set a streetview 1792 **/ 1793 this.streetviewpanorama = function(args){ 1794 if (!args.opts.opts){ 1795 args.opts.opts = {}; 1796 } 1797 if (args.latLng){ 1798 args.opts.opts.position = args.latLng; 1799 } else if (args.opts.opts.position){ 1800 args.opts.opts.position = toLatLng(args.opts.opts.position); 1801 } 1802 if (args.todo.divId){ 1803 args.opts.container = document.getElementById(args.todo.divId) 1804 } else if (args.opts.container){ 1805 args.opts.container = $(args.opts.container).get(0); 1806 } 1807 var id, obj = new defaults.classes.StreetViewPanorama(args.opts.container, args.opts.opts); 1808 if (obj){ 1809 map.setStreetView(obj); 1810 } 1811 id = store.add(args, "streetviewpanorama", obj); 1812 manageEnd(args, obj, id); 1813 }; 1814 1815 this.kmllayer = function(args){ 1816 var objs = [], multiple = "values" in args.todo; 1817 if (!multiple){ 1818 args.todo.values = [{options:args.opts}]; 1819 } 1820 if (!args.todo.values.length){ 1821 manageEnd(args, false); 1822 return; 1823 } 1824 $.each(args.todo.values, function(i, value){ 1825 var id, obj, todo = tuple(args, value); 1826 if (!map){ 1827 newMap(); 1828 } 1829 todo.options.opts.map = map; 1830 obj = new defaults.classes.KmlLayer(todo.options.url, todo.options.opts); 1831 objs.push(obj); 1832 id = store.add({todo:todo}, "kmllayer", obj); 1833 attachEvents($this, {todo:todo}, obj, id); 1834 }); 1835 manageEnd(args, multiple ? objs : objs[0]); 1836 }; 1837 1838 /** 1839 * add a fix panel 1840 **/ 1841 this.panel = function(args){ 1842 newMap(); 1843 var id, x= 0, y=0, $content, 1844 $div = $(document.createElement("div")); 1845 1846 $div 1847 .css("position", "absolute") 1848 .css("z-index", "1000"); 1849 1850 if (args.opts.content){ 1851 $content = $(args.opts.content); 1852 1853 if (args.opts.left !== undef){ 1854 x = args.opts.left; 1855 } else if (args.opts.right !== undef){ 1856 x = $this.width() - $content.width() - args.opts.right; 1857 } else if (args.opts.center){ 1858 x = ($this.width() - $content.width()) / 2; 1859 } 1860 1861 if (args.opts.top !== undef){ 1862 y = args.opts.top; 1863 } else if (args.opts.bottom !== undef){ 1864 y = $this.height() - $content.height() - args.opts.bottom; 1865 } else if (args.opts.middle){ 1866 y = ($this.height() - $content.height()) / 2 1867 } 1868 1869 $div 1870 .css("top", y+"px") 1871 .css("left", x+"px") 1872 .append($content); 1873 } 1874 1875 $this.first().prepend($div); 1876 id = store.add(args, "panel", $div); 1877 manageEnd(args, $div, id); 1878 $div = null; // memory leak 1879 }; 1880 1881 /** 1882 * Create an InternalClusterer object 1883 **/ 1884 function createClusterer(raw){ 1885 var internalClusterer = new InternalClusterer($this, map, raw.radius, raw.maxZoom), 1886 todo = {}, 1887 styles = {}, 1888 isInt = /^[0-9]+$/, 1889 calculator, 1890 k; 1891 1892 for(k in raw){ 1893 if (isInt.test(k)){ 1894 styles[k] = raw[k]; 1895 styles[k].width = styles[k].width || 0; 1896 styles[k].height = styles[k].height || 0; 1897 } else { 1898 todo[k] = raw[k]; 1899 } 1900 } 1901 1902 // external calculator 1903 if (todo.calculator){ 1904 calculator = function(indexes){ 1905 var data = []; 1906 $.each(indexes, function(i, index){ 1907 data.push(internalClusterer.value(index)); 1908 }); 1909 return todo.calculator.apply($this, [data]); 1910 }; 1911 } else { 1912 calculator = function(indexes){ 1913 return indexes.length; 1914 }; 1915 } 1916 1917 // set error function 1918 internalClusterer.error(function(){ 1919 error.apply(that, arguments); 1920 }); 1921 1922 // set display function 1923 internalClusterer.display(function(cluster){ 1924 var k, style, n = 0, atodo, obj, offset, 1925 cnt = calculator(cluster.indexes); 1926 1927 // look for the style to use 1928 if (cnt > 1){ 1929 for(k in styles){ 1930 if (k > n && k <= cnt){ 1931 n = k; 1932 } 1933 } 1934 style = styles[n]; 1935 } 1936 1937 if (style){ 1938 offset = style.offset || [-style.width/2, -style.height/2]; 1939 // create a custom overlay command 1940 // nb: 2 extends are faster that a deeper extend 1941 atodo = $.extend({}, todo); 1942 atodo.options = $.extend({ 1943 pane: "overlayLayer", 1944 content:style.content ? style.content.replace("CLUSTER_COUNT", cnt) : "", 1945 offset:{ 1946 x: ("x" in offset ? offset.x : offset[0]) || 0, 1947 y: ("y" in offset ? offset.y : offset[1]) || 0 1948 } 1949 }, 1950 todo.options || {}); 1951 1952 obj = that.overlay({todo:atodo, opts:atodo.options, latLng:toLatLng(cluster)}, true); 1953 1954 atodo.options.pane = "floatShadow"; 1955 atodo.options.content = $(document.createElement("div")).width(style.width+"px").height(style.height+"px"); 1956 shadow = that.overlay({todo:atodo, opts:atodo.options, latLng:toLatLng(cluster)}, true); 1957 1958 // store data to the clusterer 1959 todo.data = { 1960 latLng: toLatLng(cluster), 1961 markers:[] 1962 }; 1963 $.each(cluster.indexes, function(i, index){ 1964 todo.data.markers.push(internalClusterer.value(index)); 1965 if (internalClusterer.marker(index)){ 1966 internalClusterer.marker(index).setMap(null); 1967 } 1968 }); 1969 attachEvents($this, {todo:todo}, shadow, undef, {main:obj, shadow:shadow}); 1970 internalClusterer.store(cluster, obj, shadow); 1971 } else { 1972 $.each(cluster.indexes, function(i, index){ 1973 if (internalClusterer.marker(index)){
1974 internalClusterer.marker(index).setMap(map); 1975 } else { 1976 var todo = internalClusterer.todo(index), 1977 marker = new defaults.classes.Marker(todo.options); 1978 internalClusterer.setMarker(index, marker); 1979 attachEvents($this, {todo:todo}, marker, todo.id); 1980 } 1981 }); 1982 } 1983 }); 1984 1985 return internalClusterer; 1986 } 1987 /** 1988 * add a marker 1989 **/ 1990 this.marker = function(args){ 1991 var multiple = "values" in args.todo, 1992 init = !map; 1993 if (!multiple){ 1994 args.opts.position = args.latLng || toLatLng(args.opts.position); 1995 args.todo.values = [{options:args.opts}]; 1996 } 1997 if (!args.todo.values.length){ 1998 manageEnd(args, false); 1999 return; 2000 } 2001 if (init){ 2002 newMap(); 2003 } 2004 2005 if (args.todo.cluster && !map.getBounds()){ // map not initialised => bounds not available : wait for map if clustering feature is required 2006 google.maps.event.addListenerOnce(map, "bounds_changed", function() { that.marker.apply(that, [args]); }); 2007 return; 2008 } 2009 if (args.todo.cluster){ 2010 var clusterer, internalClusterer; 2011 if (args.todo.cluster instanceof Clusterer){ 2012 clusterer = args.todo.cluster; 2013 internalClusterer = store.getById(clusterer.id(), true); 2014 } else { 2015 internalClusterer = createClusterer(args.todo.cluster); 2016 clusterer = new Clusterer(globalId(args.todo.id, true), internalClusterer); 2017 store.add(args, "clusterer", clusterer, internalClusterer); 2018 } 2019 internalClusterer.beginUpdate(); 2020 2021 $.each(args.todo.values, function(i, value){ 2022 var todo = tuple(args, value); 2023 todo.options.position = todo.options.position ? toLatLng(todo.options.position) : toLatLng(value); 2024 todo.options.map = map; 2025 if (init){ 2026 map.setCenter(todo.options.position); 2027 init = false; 2028 } 2029 internalClusterer.add(todo, value); 2030 }); 2031 2032 internalClusterer.endUpdate(); 2033 manageEnd(args, clusterer); 2034 2035 } else { 2036 var objs = []; 2037 $.each(args.todo.values, function(i, value){ 2038 var id, obj, todo = tuple(args, value); 2039 todo.options.position = todo.options.position ? toLatLng(todo.options.position) : toLatLng(value); 2040 todo.options.map = map; 2041 if (init){ 2042 map.setCenter(todo.options.position); 2043 init = false; 2044 } 2045 obj = new defaults.classes.Marker(todo.options); 2046 objs.push(obj); 2047 id = store.add({todo:todo}, "marker", obj); 2048 attachEvents($this, {todo:todo}, obj, id); 2049 }); 2050 manageEnd(args, multiple ? objs : objs[0]); 2051 } 2052 }; 2053 2054 /** 2055 * return a route 2056 **/ 2057 this.getroute = function(args){ 2058 args.opts.origin = toLatLng(args.opts.origin, true); 2059 args.opts.destination = toLatLng(args.opts.destination, true); 2060 directionsService().route( 2061 args.opts, 2062 function(results, status) { 2063 callback(args, status == google.maps.DirectionsStatus.OK ? results : false, status); 2064 task.ack(); 2065 } 2066 ); 2067 }; 2068 2069 /** 2070 * add a direction renderer 2071 **/ 2072 this.directionsrenderer = function(args){ 2073 args.opts.map = map; 2074 var id, obj = new google.maps.DirectionsRenderer(args.opts); 2075 if (args.todo.divId){ 2076 obj.setPanel(document.getElementById(args.todo.divId)); 2077 } else if (args.todo.container){ 2078 obj.setPanel($(args.todo.container).get(0)); 2079 } 2080 id = store.add(args, "directionrenderer", obj); 2081 manageEnd(args, obj, id); 2082 }; 2083 2084 /** 2085 * returns latLng of the user 2086 **/ 2087 this.getgeoloc = function(args){ 2088 manageEnd(args, args.latLng); 2089 }; 2090 2091 /** 2092 * add a style 2093 **/ 2094 this.styledmaptype = function(args){ 2095 newMap(); 2096 var obj = new defaults.classes.StyledMapType(args.todo.styles, args.opts); 2097 map.mapTypes.set(args.todo.id, obj); 2098 manageEnd(args, obj); 2099 }; 2100 2101 /** 2102 * add an imageMapType 2103 **/ 2104 this.imagemaptype = function(args){ 2105 newMap(); 2106 var obj = new defaults.classes.ImageMapType(args.opts); 2107 map.mapTypes.set(args.todo.id, obj); 2108 manageEnd(args, obj); 2109 }; 2110 2111 /** 2112 * autofit a map using its overlays (markers, rectangles ...) 2113 **/ 2114 this.autofit = function(args){ 2115 var bounds = new google.maps.LatLngBounds(); 2116 2117 $.each(store.all(), function(i, obj){ 2118 if (obj.getPosition){ 2119 bounds.extend(obj.getPosition()); 2120 } else if (obj.getBounds){ 2121 bounds.extend(obj.getBounds().getNorthEast());
2122 bounds.extend(obj.getBounds().getSouthWest()); 2123 } else if (obj.getPaths){ 2124 obj.getPaths().forEach(function(path){ 2125 path.forEach(function(latLng){ 2126 bounds.extend(latLng); 2127 }); 2128 }); 2129 } else if (obj.getPath){ 2130 obj.getPath().forEach(function(latLng){ 2131 bounds.extend(latLng);"" 2132 }); 2133 } else if (obj.getCenter){ 2134 bounds.extend(obj.getCenter()); 2135 } 2136 }); 2137 2138 if (!bounds.isEmpty() && (!map.getBounds() || !map.getBounds().equals(bounds))){ 2139 if ("maxZoom" in args.todo){ 2140 // fitBouds Callback event => detect zoom level and check maxZoom 2141 google.maps.event.addListenerOnce( 2142 map, 2143 "bounds_changed", 2144 function() { 2145 if (this.getZoom() > args.todo.maxZoom){ 2146 this.setZoom(args.todo.maxZoom); 2147 } 2148 } 2149 ); 2150 } 2151 map.fitBounds(bounds); 2152 } 2153 manageEnd(args, true); 2154 }; 2155 2156 /** 2157 * remove objects from a map 2158 **/ 2159 this.clear = function(args){ 2160 if (typeof args.todo === "string"){ 2161 if (store.clearById(args.todo) || store.objClearById(args.todo)){ 2162 manageEnd(args, true); 2163 return; 2164 } 2165 args.todo = {name:args.todo}; 2166 } 2167 if (args.todo.id){ 2168 $.each(array(args.todo.id), function(i, id){ 2169 store.clearById(id); 2170 }); 2171 } else { 2172 store.clear(array(args.todo.name), args.todo.last, args.todo.first, args.todo.tag); 2173 } 2174 manageEnd(args, true); 2175 }; 2176 2177 /** 2178 * run a function on each items selected 2179 **/ 2180 this.exec = function(args){ 2181 var that = this; 2182 $.each(array(args.todo.func), function(i, func){ 2183 $.each(that.get(args.todo, true, args.todo.hasOwnProperty("full") ? args.todo.full : true), function(j, res){ 2184 func.call($this, res); 2185 }); 2186 }); 2187 manageEnd(args, true); 2188 }; 2189 2190 /** 2191 * return objects previously created 2192 **/ 2193 this.get = function(args, direct, full){ 2194 var name, res, 2195 todo = direct ? args : args.todo; 2196 if (!direct) { 2197 full = todo.full; 2198 } 2199 if (typeof todo === "string"){ 2200 res = store.getById(todo, false, full) || store.objGetById(todo); 2201 if (res === false){ 2202 name = todo; 2203 todo = {}; 2204 } 2205 } else { 2206 name = todo.name; 2207 } 2208 if (name === "map"){ 2209 res = map; 2210 } 2211 if (!res){ 2212 res = []; 2213 if (todo.id){ 2214 $.each(array(todo.id), function(i, id) { 2215 res.push(store.getById(id, false, full) || store.objGetById(id)); 2216 }); 2217 if (!$.isArray(todo.id)) { 2218 res = res[0]; 2219 } 2220 } else { 2221 $.each(name ? array(name) : [undef], function(i, aName) { 2222 var result; 2223 if (todo.first){ 2224 result = store.get(aName, false, todo.tag, full); 2225 if (result) res.push(result); 2226 } else if (todo.all){ 2227 $.each(store.all(aName, todo.tag, full), function(i, result){ 2228 res.push(result); 2229 }); 2230 } else { 2231 result = store.get(aName, true, todo.tag, full); 2232 if (result) res.push(result); 2233 } 2234 }); 2235 if (!todo.all && !$.isArray(name)) { 2236 res = res[0]; 2237 } 2238 } 2239 } 2240 res = $.isArray(res) || !todo.all ? res : [res]; 2241 if (direct){ 2242 return res; 2243 } else { 2244 manageEnd(args, res); 2245 } 2246 }; 2247 2248 /** 2249 * return the distance between an origin and a destination 2250 * 2251 **/ 2252 this.getdistance = function(args){ 2253 var i; 2254 args.opts.origins = array(args.opts.origins);
2255 for(i=0; i<args.opts.origins.length; i++){ 2256 args.opts.origins[i] = toLatLng(args.opts.origins[i], true); 2257 } 2258 args.opts.destinations = array(args.opts.destinations); 2259 for(i=0; i<args.opts.destinations.length; i++){ 2260 args.opts.destinations[i] = toLatLng(args.opts.destinations[i], true); 2261 } 2262 distanceMatrixService().getDistanceMatrix( 2263 args.opts, 2264 function(results, status) { 2265 callback(args, status === google.maps.DistanceMatrixStatus.OK ? results : false, status); 2266 task.ack(); 2267 } 2268 ); 2269 }; 2270 2271 /** 2272 * trigger events on the map 2273 **/ 2274 this.trigger = function(args){ 2275 if (typeof args.todo === "string"){ 2276 google.maps.event.trigger(map, args.todo); 2277 } else { 2278 var options = [map, args.todo.eventName]; 2279 if (args.todo.var_args) { 2280 $.each(args.todo.var_args, function(i, v){ 2281 options.push(v); 2282 }); 2283 } 2284 google.maps.event.trigger.apply(google.maps.event, options); 2285 } 2286 callback(args); 2287 task.ack(); 2288 }; 2289 } 2290 2291 /** 2292 * Return true if get is a direct call 2293 * it means : 2294 * - get is the only key 2295 * - get has no callback 2296 * @param obj {Object} The request to check 2297 * @return {Boolean} 2298 */ 2299 function isDirectGet(obj) { 2300 var k; 2301 if (!typeof obj === "object" || !obj.hasOwnProperty("get")){ 2302 return false; 2303 } 2304 for(k in obj) { 2305 if (k !== "get") { 2306 return false; 2307 } 2308 } 2309 return !obj.get.hasOwnProperty("callback"); 2310 } 2311 2312 //-----------------------------------------------------------------------// 2313 // jQuery plugin 2314 //-----------------------------------------------------------------------// 2315 2316 $.fn.gmap3 = function(){ 2317 var i, list = [], empty = true, results = []; 2318 2319 // init library 2320 initDefaults(); 2321 2322 // store all arguments in a todo list 2323 for(i=0; i<arguments.length; i++){ 2324 if (arguments[i]){ 2325 list.push(arguments[i]); 2326 } 2327 } 2328 2329 // resolve empty call - run init 2330 if (!list.length) { 2331 list.push("map"); 2332 } 2333 2334 // loop on each jQuery object 2335 $.each(this, function() { 2336 var $this = $(this), gmap3 = $this.data("gmap3"); 2337 empty = false; 2338 if (!gmap3){ 2339 gmap3 = new Gmap3($this); 2340 $this.data("gmap3", gmap3); 2341 } 2342 if (list.length === 1 && (list[0] === "get" || isDirectGet(list[0]))){ 2343 results.push(gmap3.get(list[0] === "get" ? "map" : list[0].get, true)); 2344 } else { 2345 gmap3._plan(list); 2346 } 2347 }); 2348 2349 // return for direct call only 2350 if (results.length){ 2351 if (results.length === 1){ // 1 css selector 2352 return results[0]; 2353 } else { 2354 return results; 2355 } 2356 } 2357 2358 return this; 2359 } 2360 2361})(jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.