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