vendor: 28,361 bytes, lines 1-1141
1/*! 2 * GMAP3 Plugin for jQuery 3 * Version : 7.2 4 * Date : 2016/12/03 5 * Author : DEMONTE Jean-Baptiste 6 * Contact : [email protected] 7 * Web site : http://gmap3.net 8 * Licence : GPL-3.0+ 9 */ 10(function ($, window, document) { 11 "use strict"; 12 13 var gm, services = {}, loadOptions, 14 15 // Proxify functions to get shorter minimized code 16 when = $.when, 17 extend = $.extend, 18 isArray = $.isArray, 19 isFunction = $.isFunction, 20 deferred = $.Deferred; 21 22 /** 23 * Duplicate option to never modify original object 24 * @param {Object} options 25 * @returns {Object} 26 */ 27 function dupOpts(options) { 28 return extend(true, {}, options || {}); 29 } 30 31 /** 32 * Slice an array like 33 * @params {Array|Object} 34 * @params {Number} [start] 35 * @params {Number} [end] 36 * @returns {Array} 37 */ 38 function slice() { 39 var fn = Array.prototype.slice, 40 args = fn.call(arguments, 1); 41 return fn.apply(arguments[0], args); 42 } 43 44 /** 45 * Return true if value is undefined 46 * @param {*} value 47 * @returns {Boolean} 48 */ 49 function isUndefined(value) { 50 return typeof value === 'undefined'; 51 } 52 53 /** 54 * Equivalent to Promise.all 55 * @param {Deferred[]} deferreds 56 * @returns {Deferred} 57 */ 58 function all(deferreds) { 59 return when.apply($, deferreds); 60 } 61 62 /** 63 * Equivalent to Promise.resolve 64 * @param {*} value 65 * @returns {Deferred} 66 */ 67 function resolved(value) { 68 return when().then(function () { 69 return value; 70 }); 71 } 72 73 /** 74 * return the distance between 2 latLng in meters 75 * @param {LatLng} origin 76 * @param {LatLng} destination 77 * @returns {Number} 78 **/ 79 function distanceInMeter(origin, destination) { 80 var m = Math, 81 pi = m.PI, 82 e = pi * origin.lat() / 180, 83 f = pi * origin.lng() / 180, 84 g = pi * destination.lat() / 180, 85 h = pi * destination.lng() / 180, 86 cos = m.cos, 87 sin = m.sin; 88 return 1000 * 6371 * m.acos(m.min(cos(e) * cos(g) * cos(f) * cos(h) + cos(e) * sin(f) * cos(g) * sin(h) + sin(e) * sin(g), 1)); 89 } 90 91 function ready(fn) { 92 if (document.readyState != 'loading') { 93 fn(); 94 } else { 95 document.addEventListener('DOMContentLoaded', fn); 96 } 97 } 98 99 function serialize(obj) { 100 return objectKeys(obj).map(function (key) { 101 return encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]); 102 }).join("&"); 103 } 104 105 // Auto-load google maps library if needed 106 when(function () { 107 var dfd = deferred(), 108 cbName = '__gmap3', 109 script; 110 111 $.holdReady(true); 112 113 ready(function () { 114 if (window.google && window.google.maps || loadOptions === false) { 115 dfd.resolve(); 116 } else { 117 // callback function - resolving promise after maps successfully loaded 118 window[cbName] = function () { 119 delete window[cbName]; 120 dfd.resolve(); 121 }; 122 script = document.createElement('script'); 123 script.type = 'text/javascript'; 124 script.src = 'https://maps.googleapis.com/maps/api/js?callback=' + cbName + (loadOptions ? '&' + (typeof loadOptions === 'string' ? loadOptions : serialize(loadOptions)) : ''); 125 $("head").append(script); 126 } 127 }); 128 129 return dfd; 130 }()).then(function () { 131 $.holdReady(false); 132 }); 133 134 /** 135 * Instantiate only once a google service 136 * @param {String} name 137 * @returns {Object} 138 */ 139 function service(name) { 140 if (!services[name]) { 141 services[name] = gmElement(name); 142 } 143 return services[name]; 144 } 145 146 /** 147 * Return GoogleMap Class (or overwritten by user) instance 148 * @param {String} name 149 * @returns {Object} 150 */ 151 function gmElement(name) { 152 var cls = gm[name]; 153 154 function F(args) { 155 return cls.apply(this, args); 156 } 157 F.prototype = cls.prototype; 158 159 return new F(slice(arguments, 1)); 160 } 161 162 /** 163 * Resolve a GeocodeRequest 164 * https://developers.google.com/maps/documentation/javascript/geocoding 165 * @param {String|Object} request 166 * @returns {Deferred} 167 */ 168 function geocode(request) { 169 var dfd = deferred(); 170 if (typeof request === 'string') { 171 request = { 172 address: request 173 }; 174 } 175 service('Geocoder').geocode(request, function (results, status) { 176 if (status === gm.GeocoderStatus.OK) { 177 dfd.resolve(results[0].geometry.location); 178 } else { 179 dfd.reject(status); 180 } 181 }); 182 return dfd; 183 } 184 185 /** 186 * Callable function taking a parameter as string 187 * @callback StringCallback 188 * @param {String} 189 */ 190 191 /** 192 * Split a string and execute a function on each item 193 * @param {String} str - Space separated list of items 194 * @param {StringCallback} fn - Callback function 195 */ 196 function foreachStr(str, fn) { 197 foreach(str.split(' '), fn); 198 } 199 200 /** 201 * Execute a function on each items if items is an array and on items as a single element if it is not an array 202 * @param {Array|*} items - Items to execute foreach callback on 203 * @param {Function} fn - Callback function 204 */ 205 function foreach(items, fn) { 206 (isArray(items) ? items : [items]).forEach(fn); 207 } 208 209 /** 210 * Return Object keys 211 * @param {Object} obj 212 * @returns {String[]} 213 */ 214 function objectKeys(obj) { 215 return Object.keys(obj); 216 } 217 218 /** 219 * Return Object values 220 * @param {Object} obj 221 * @returns {*[]} 222 */ 223 function objectValues(obj) { 224 return objectKeys(obj).map(function (key) { 225 return obj[key]; 226 }); 227 } 228 229 /** 230 * Resolution function 231 * @callback OptionCallback 232 * @param {Object} options 233 * @returns {Deferred|*} 234 */ 235 236 /** 237 * Convert bounds from array [ n, e, s, w ] to google.maps.LatLngBounds 238 * @param {Object} options - Container of options.bounds 239 * @param {OptionCallback} fn 240 * @returns {Deferred} 241 */ 242 function resolveLatLngBounds(options, fn) { 243 options = dupOpts(options); 244 if (options.bounds) { 245 options.bounds = toLatLngBound(options.bounds); 246 } 247 return resolved(fn(options)); 248 } 249 250 /** 251 * Resolve an address location / convert a LatLng array to google.maps.LatLng object 252 * @param {Object} options 253 * @param {String} key - LatLng key name in options object 254 * @param {OptionCallback} fn 255 * @returns {Deferred} 256 */ 257 function resolveLatLng(options, key, fn) { 258 var dfd = deferred(); 259 options = dupOpts(options); 260 when() 261 .then(function () { 262 var address = options.address; 263 if (address) { 264 delete options.address; 265 return geocode(address).then(function (latLng) { 266 options[key] = latLng; 267 }); 268 } 269 options[key] = toLatLng(options[key]); 270 }) 271 .then(function () { 272 dfd.resolve(fn(options)); 273 }) 274 .fail(function (reason) { 275 dfd.reject(reason); 276 }); 277 return dfd; 278 } 279 280 /** 281 * Convert an array of mixed LatLng to google.maps.LatLng object 282 * No address resolution here 283 * @param {Object} options 284 * @param {String} key - Array key name in options object 285 * @param {OptionCallback} fn 286 * @returns {Deferred} 287 */ 288 function resolveArrayOfLatLng(options, key, fn) { 289 options = dupOpts(options); 290 options[key] = (options[key] || []).map(toLatLng); 291 return resolved(fn(options)); 292 } 293 294 /** 295 * Convert a LatLng array to google.maps.LatLng 296 * @param {Array|*} mixed 297 * @param {Boolean} [convertLiteral] 298 * @returns {LatLng} 299 */ 300 function toLatLng(mixed, convertLiteral) { 301 return isArray(mixed) ? new gm.LatLng(mixed[0], mixed[1]) : (convertLiteral && mixed && !(mixed instanceof gm.LatLng) ? new gm.LatLng(mixed.lat, mixed.lng) : mixed); 302 } 303 304 /** 305 * Convert a LatLngBound array to google.maps.LatLngBound 306 * @param {Array|*} mixed 307 * @param {Boolean} [convertLiteral] 308 * @returns {LatLngBounds} 309 */ 310 function toLatLngBound(mixed, convertLiteral) { 311 if (isArray(mixed)) { 312 return new gm.LatLngBounds({lat: mixed[2], lng: mixed[3]}, {lat: mixed[0], lng: mixed[1]}); 313 } else if (convertLiteral && !mixed.getCenter){ 314 return new gm.LatLngBounds({lat: mixed.south, lng: mixed.west}, {lat: mixed.north, lng: mixed.east}); 315 } 316 return mixed; 317 } 318 319 /** 320 * Create a custom overlay view 321 * @param {Map} map 322 * @param {Object} options 323 * @returns {OverlayView} 324 */ 325 function createOverlayView(map, options) { 326 327 var GMOverlayView = gm.OverlayView; 328 329 var $div = $(document.createElement("div")) 330 .css({ 331 border: "none", 332 borderWidth: 0, 333 position: "absolute" 334 }) 335 .append(options.content); 336 337 options = extend({x: 0, y: 0}, options); 338 339 if (options.position) { 340 options.position = toLatLng(options.position, true); 341 } else if (options.bounds) { 342 options.bounds = toLatLngBound(options.bounds, true); 343 } 344 345 /** 346 * Class OverlayView 347 * @constructor 348 */ 349 function OverlayView() { 350 var self = this, 351 listeners = []; 352 353 GMOverlayView.call(self); 354 self.setMap(map); 355 356 function fromLatLngToDivPixel(latlng) { 357 return self.getProjection().fromLatLngToDivPixel(latlng); 358 } 359 360 self.onAdd = function () { 361 var panes = self.getPanes(); 362 panes.overlayMouseTarget.appendChild($div[0]); 363 }; 364 365 if (options.position) { 366 self.getPosition = function () { 367 return options.position; 368 }; 369 370 self.setPosition = function (latlng) { 371 options.position = latlng; 372 self.draw(); 373 }; 374 375 self.draw = function () { 376 var ps = fromLatLngToDivPixel(options.position); 377 $div.css({ 378 left: (ps.x + options.x) + 'px', 379 top: (ps.y + options.y) + 'px' 380 }); 381 }; 382 } else { 383 self.getBounds = function () { 384 return options.bounds; 385 }; 386 387 self.setBounds = function (bounds) { 388 options.bounds = bounds; 389 self.draw(); 390 }; 391 392 self.draw = function () { 393 var sw = fromLatLngToDivPixel(options.bounds.getSouthWest()); 394 var ne = fromLatLngToDivPixel(options.bounds.getNorthEast()); 395 396 $div.css({ 397 left: (sw.x + options.x) + 'px', 398 top: (ne.y + options.y) + 'px', 399 width: (ne.x - sw.x + options.x) + 'px', 400 height: (sw.y - ne.y + options.y) + 'px' 401 }); 402 }; 403 } 404 405 self.onRemove = function () { 406 listeners.map(function (handler) { 407 gm.event.removeListener(handler); 408 }); 409 $div.remove(); 410 self.$ = $div = null; // mem leaks 411 }; 412 413 self.$ = $div; 414 } 415 416 OverlayView.prototype = new GMOverlayView(); 417 418 return new OverlayView(); 419 } 420 421 /** 422 * Return a map projection 423 * @param {Map} map 424 * @returns {*} 425 */ 426 function getProjection(map) { 427 function Overlay() { 428 var self = this; 429 self.onAdd = self.onRemove = self.draw = function () {}; 430 return gm.OverlayView.call(self); 431 } 432 Overlay.prototype = new gm.OverlayView(); 433 var overlay = new Overlay(); 434 overlay.setMap(map); 435 return overlay.getProjection(); 436 } 437 438 /** 439 * Class used as event first parameter on clustering overlays 440 * @param {Cluster} cluster 441 * @param {Marker[]} markers 442 * @param {OverlayView} overlay 443 * @param {LatLngBounds} bounds 444 * @constructor 445 */ 446 function ClusterOverlay(cluster, markers, overlay, bounds) { 447 var self = this; 448 self.cluster = cluster; 449 self.markers = markers; 450 self.$ = overlay.$; 451 self.overlay = overlay; 452 453 overlay.getBounds = function () { 454 return gmElement('LatLngBounds', bounds.getSouthWest(), bounds.getNorthEast()); 455 }; 456 } 457 458 /** 459 * Cluster Group definition. 460 * @typedef {Object} ClusterGroupDef 461 * @property {String|jQuery} content 462 * @property {Number} [x] Offset 463 * @property {Number} [y] Offset 464 */ 465 466 /** 467 * Cluster evaluation function 468 * @callback clusterCallback 469 * @param {Marker[]} markers 470 * @return {ClusterGroupDef|undefined} 471 */ 472 473 /** 474 * Class used to handle clustering 475 * @param {Map} map 476 * @param {Object} options 477 * @param {Integer} [options.size] 478 * @param {Object[]} [options.markers] markers definition 479 * @param {clusterCallback} [options.cb] callback used to evaluate clustering elements 480 * @constructor 481 */ 482 function Cluster(map, options) { 483 var timer, igniter, previousViewHash, projection, filter, 484 self = this, 485 markers = [], 486 radius = (options.size || 200) >> 1, 487 enabled = true, 488 overlays = {}, 489 handlers = []; 490 491 options = options || {}; 492 options.markers = options.markers || []; 493 494 /** 495 * Cluster evaluation function 496 * @callback bindCallback 497 * @param {ClusterOverlay[]} instances 498 */ 499 500 /** 501 * Bind a function to each current or future overlays 502 * @param {bindCallback} fn 503 */ 504 self._b = function (fn) { 505 fn(objectValues(overlays)); 506 handlers.push(fn); 507 }; 508 509 /** 510 * Get the marker list 511 * @returns {Marker[]} 512 */ 513 self.markers = function () { 514 return slice(markers); 515 }; 516 517 /** 518 * Get the current groups 519 * @returns {ClusterOverlay[]} 520 */ 521 self.groups = function () { 522 return objectValues(overlays); 523 }; 524 525 /** 526 * Enable the clustering feature 527 */ 528 self.enable = function () { 529 if (!enabled) { 530 enabled = true; 531 previousViewHash = ''; 532 delayRedraw(); 533 } 534 }; 535 536 537 /** 538 * Disable the clustering feature 539 */ 540 self.disable = function () { 541 if (enabled) { 542 enabled = false; 543 previousViewHash = ''; 544 delayRedraw(); 545 } 546 }; 547 548 /** 549 * Add a marker 550 * @param {Marker} marker 551 */ 552 self.add = function (marker) { 553 markers.push(marker); 554 previousViewHash = ''; 555 delayRedraw(); 556 }; 557 558 /** 559 * Remove a marker 560 * @param {Marker} marker 561 */ 562 self.remove = function (marker) { 563 markers = markers.filter(function (item) { 564 return item !== marker; 565 }); 566 previousViewHash = ''; 567 delayRedraw(); 568 }; 569 570 /** 571 * Filtering function, Cluster only handle those who return true 572 * @callback filterCallback 573 * @param {Marker} marker 574 * @returns {Boolean} 575 */ 576 577 /** 578 * Set a filter function 579 * @param {filterCallback} fn 580 */ 581 self.filter = function (fn) { 582 if (filter !== fn) { 583 filter = fn; 584 previousViewHash = ''; 585 delayRedraw(); 586 } 587 }; 588 589 /** 590 * Generate extended visible bounds 591 * @returns {LatLngBounds} 592 */ 593 function extendsMapBounds() { 594 var circle = gmElement('Circle', { 595 center: map.getCenter(), 596 radius: 1.15 * distanceInMeter(map.getCenter(), map.getBounds().getNorthEast()) // + 15% 597 }); 598 return circle.getBounds(); 599 } 600 601 /** 602 * Generate bounds extended by radius 603 * @param {LatLng} latLng 604 * @returns {LatLngBounds} 605 */ 606 function extendsBounds(latLng) { 607 var p = projection.fromLatLngToDivPixel(latLng); 608 return gmElement('LatLngBounds', 609 projection.fromDivPixelToLatLng(gmElement('Point', p.x - radius, p.y + radius)), 610 projection.fromDivPixelToLatLng(gmElement('Point', p.x + radius, p.y - radius)) 611 ); 612 } 613 614 options.markers.map(function (opts) { 615 opts.position = toLatLng(opts.position); 616 markers.push(gmElement('Marker', opts)); 617 }); 618 619 /** 620 * Redraw clusters 621 */ 622 function redraw() { 623 var keys, bounds, overlayOptions, hash, currentMarkers, viewHash, 624 zoom = map.getZoom(), 625 currentHashes = {}, 626 newOverlays = [], 627 ignore = {}; 628 629 viewHash = '' + zoom; 630 631 if (zoom > 3) { 632 bounds = extendsMapBounds(); 633 foreach(markers, function (marker, index) { 634 if (!bounds.contains(marker.getPosition())) { 635 viewHash += '-' + index; 636 ignore[index] = true; 637 if (marker.getMap()) { 638 marker.setMap(null); 639 } 640 } 641 }); 642 } 643 if (filter) { 644 foreach(markers, function (marker, index) { 645 if (!ignore[index] && !filter(marker)) { 646 viewHash += '-' + index; 647 ignore[index] = true; 648 if (marker.getMap()) { 649 marker.setMap(null); 650 } 651 } 652 }); 653 } 654 655 if (viewHash === previousViewHash) { 656 return; 657 } 658 previousViewHash = viewHash; 659 660 foreach(markers, function (marker, index) { 661 if (ignore[index]) { 662 return; 663 } 664 665 keys = [index]; 666 bounds = extendsBounds(marker.getPosition()); 667 668 if (enabled) { 669 foreach(slice(markers, index + 1), function (marker, idx) { 670 idx += index + 1; 671 if (!ignore[idx] && bounds.contains(marker.getPosition())) { 672 keys.push(idx); 673 ignore[idx] = true; 674 } 675 }); 676 } 677 678 hash = keys.join('-'); 679 currentHashes[hash] = true; 680 681 if (overlays[hash]) { // hash is already set 682 return; 683 } 684 685 currentMarkers = keys.map(function (key) { 686 return markers[key]; 687 }); 688 689 // ask the user callback on this subset (may be composed by only one marker) 690 overlayOptions = options.cb(slice(currentMarkers)); 691 692 // create an overlay if cb returns its properties 693 if (overlayOptions) { 694 bounds = gmElement('LatLngBounds'); 695 foreach(currentMarkers, function (marker) { 696 bounds.extend(marker.getPosition()); 697 if (marker.getMap()) { 698 marker.setMap(null); 699 } 700 }); 701 702 overlayOptions = dupOpts(overlayOptions); 703 overlayOptions.position = bounds.getCenter(); 704 overlays[hash] = new ClusterOverlay(self, slice(currentMarkers), createOverlayView(map, overlayOptions), bounds); 705 newOverlays.push(overlays[hash]); 706 707 } else { 708 foreach(currentMarkers, function (marker) { 709 if (!marker.getMap()) { // to avoid marker blinking 710 marker.setMap(map); 711 } 712 }); 713 } 714 715 }); 716 717 // remove previous overlays 718 foreach(objectKeys(overlays), function (key) { 719 if (!currentHashes[key]) { 720 overlays[key].overlay.setMap(null); 721 delete overlays[key]; 722 } 723 }); 724 725 if (newOverlays.length) { 726 foreach(handlers, function (fn) { 727 fn(newOverlays); 728 }); 729 } 730 } 731 732 /** 733 * Restart redraw timer 734 */ 735 function delayRedraw() { 736 clearTimeout(timer); 737 timer = setTimeout(redraw, 100); 738 } 739 740 /** 741 * Init clustering 742 */ 743 function init() { 744 gm.event.addListener(map, "zoom_changed", delayRedraw); 745 gm.event.addListener(map, "bounds_changed", delayRedraw); 746 redraw(); 747 } 748 749 igniter = setInterval(function () { 750 projection = getProjection(map); 751 if (projection) { 752 clearInterval(igniter); 753 init(); 754 } 755 }, 10); 756 } 757 758 /** 759 * Configure google maps loading library 760 * @param {string|object} options 761 */ 762 $.gmap3 = function (options) { 763 loadOptions = options; 764 }; 765 766 /** 767 * jQuery Plugin 768 */ 769 $.fn.gmap3 = function (options) { 770 var items = []; 771 gm = window.google.maps; // once gmap3 is loaded, google.maps library should be loaded 772 this.each(function () { 773 var $this = $(this), gmap3 = $this.data("gmap3"); 774 if (!gmap3) { 775 gmap3 = new Gmap3($this, options); 776 $this.data("gmap3", gmap3); 777 } 778 items.push(gmap3); 779 }); 780 781 return new Handler(this, items); 782 }; 783 784 /** 785 * Class Handler 786 * Chainable objet which handle all Gmap3 items associated to all jQuery elements in the current command set 787 * @param {jQuery} chain - "this" to return to maintain the jQuery chain 788 * @param {Gmap3[]} items 789 * @constructor 790 */ 791 function Handler(chain, items) { 792 var self = this; 793 794 // Map all functions from Gmap3 class 795 objectKeys(items[0]).forEach(function (name) { 796 self[name] = function () { 797 var results = [], 798 args = slice(arguments); 799 items.forEach(function (item) { 800 results.push(item[name].apply(item, args)); 801 }); 802 return name === 'get' ? (results.length > 1 ? results : results[0]) : self; 803 }; 804 }); 805 806 self.$ = chain; 807 } 808 809 /** 810 * Class Gmap3 811 * Handle a Google.maps.Map instance 812 * @param {jQuery} $container - Element to display the map in 813 * @param {Object} options - MapOptions 814 * @constructor 815 */ 816 function Gmap3($container, options) { 817 var map, 818 previousResults = [], 819 promise = when(), 820 self = this; 821 822 function context() { 823 return { 824 $: $container, 825 get: self.get 826 }; 827 } 828 829 /** 830 * Attach events to instances 831 * @param {Object } events 832 * @param {Array|Object} instances 833 * @param {array} [args] arguments to add 834 * @param {Boolean} once 835 */ 836 function attachEvents(events, instances, args, once) { 837 var hasArgs = arguments.length > 3; 838 if (!hasArgs) { 839 once = args; 840 } 841 $.each(events, function (eventName, handlers) { 842 foreach(instances, function (instance) { 843 var isClusterOverlay = instance instanceof ClusterOverlay; 844 var isDom = isClusterOverlay || (instance instanceof gm.OverlayView); 845 var eventListener = isDom ? instance.$.get(0) : instance; 846 gm.event['add' + (isDom ? 'Dom' : '') + 'Listener' + (once ? 'Once' : '')](eventListener, eventName, function (event) { 847 foreach(handlers, function (handler) { 848 if (isFunction(handler)) { 849 if (isClusterOverlay) { 850 handler.call(context(), undefined /* marker */, instance, instance.cluster, event); 851 } else if (hasArgs) { 852 var buffer = slice(args); 853 buffer.unshift(instance); 854 buffer.push(event); 855 handler.apply(context(), buffer); 856 } else { 857 handler.call(context(), instance, event); 858 } 859 } 860 }); 861 }); 862 }); 863 }); 864 } 865 866 /** 867 * Decorator to handle multiple call based on array of options 868 * @param {Function} fn 869 * @returns {Deferred} 870 */ 871 function multiple(fn) { 872 return function (options) { 873 if (isArray(options)) { 874 var instances = []; 875 var promises = options.map(function (opts) { 876 return fn.call(self, opts).then(function (instance) { 877 instances.push(instance); 878 }); 879 }); 880 return all(promises).then(function () { 881 previousResults.push(instances); 882 return instances; 883 }); 884 } else { 885 return fn.apply(self, arguments).then(function (instance) { 886 previousResults.push(instance); 887 return instance; 888 }); 889 } 890 }; 891 } 892 893 /** 894 * Decorator to chain promise result onto the main promise chain 895 * @param {Function} fn 896 * @returns {Function} 897 */ 898 function chainToPromise(fn) { 899 return function () { 900 var args = slice(arguments); 901 promise = promise.then(function (instance) { 902 if (isFunction(args[0])) { 903 // handle return as a deferred / promise to support both sync & async result 904 return when(args[0].call(context(), instance)).then(function (value) { 905 args[0] = value; 906 return fn.apply(self, args); 907 }); 908 } 909 910 return when(fn.apply(self, args)); 911 }); 912 return promise; 913 }; 914 } 915 916 self.map = chainToPromise(function (options) { 917 return map || resolveLatLng(options, 'center', function (opts) { 918 map = gmElement('Map', $container.get(0), opts); 919 previousResults.push(map); 920 return map; 921 }); 922 }); 923 924 // Space separated string of : separated element 925 // (google.maps class name) : (latLng property name) : (add map - 0|1 - default = 1) 926 foreachStr('Marker:position Circle:center InfoWindow:position:0 Polyline:path Polygon:paths', function (item) { 927 item = item.split(':'); 928 var property = item[1] || ''; 929 self[item[0].toLowerCase()] = chainToPromise(multiple(function (options) { 930 return (property.match(/^path/) ? resolveArrayOfLatLng : resolveLatLng)(options, property, function (opts) { 931 if (item[2] !== '0') { 932 opts.map = map; 933 } 934 return gmElement(item[0], opts); 935 }); 936 })); 937 }); 938 939 foreachStr('TrafficLayer TransitLayer BicyclingLayer', function (item) { 940 self[item.toLowerCase()] = chainToPromise(function () { 941 var instance = gmElement(item); 942 previousResults.push(instance); 943 instance.setMap(map); 944 return instance; 945 }); 946 }); 947 948 self.kmllayer = chainToPromise(multiple(function (options) { 949 options = dupOpts(options); 950 options.map = map; 951 return when(gmElement('KmlLayer', options)); 952 })); 953 954 self.rectangle = chainToPromise(multiple(function (options) { 955 return resolveLatLngBounds(options, function (opts) { 956 opts.map = map; 957 return gmElement('Rectangle', opts); 958 }); 959 })); 960 961 self.overlay = chainToPromise(multiple(function (options) { 962 function fn(opts) { 963 return createOverlayView(map, opts); 964 } 965 966 options = dupOpts(options); 967 return options.bounds ? resolveLatLngBounds(options, fn) : resolveLatLng(options, 'position', fn); 968 })); 969 970 self.groundoverlay = chainToPromise(function (url, bounds, options) { 971 return resolveLatLngBounds({bounds: bounds}, function (opts) { 972 options = dupOpts(options); 973 options.map = map; 974 var instance = gmElement('GroundOverlay', url, opts.bounds, options); 975 previousResults.push(instance); 976 return instance; 977 }); 978 }); 979 980 self.styledmaptype = chainToPromise(function (styleId, styles, options) { 981 var instance = gmElement('StyledMapType', styles, options); 982 previousResults.push(instance); 983 map.mapTypes.set(styleId, instance); 984 return instance; 985 }); 986 987 self.streetviewpanorama = chainToPromise(function (container, options) { 988 return resolveLatLng(options, 'position', function (opts) { 989 var instance = gmElement('StreetViewPanorama', $(container).get(0), opts); 990 map.setStreetView(instance); 991 previousResults.push(instance); 992 return instance; 993 }); 994 }); 995 996 self.route = chainToPromise(function (options) { 997 var dfd = deferred(); 998 options = dupOpts(options); 999 options.origin = toLatLng(options.origin); 1000 options.destination = toLatLng(options.destination); 1001 service('DirectionsService').route(options, function (results, status) { 1002 previousResults.push(results); 1003 dfd.resolve(status === gm.DirectionsStatus.OK ? results : false); 1004 }); 1005 return dfd; 1006 }); 1007 1008 self.cluster = chainToPromise(function (options) { 1009 var cluster = new Cluster(map, dupOpts(options)); 1010 previousResults.push(cluster); 1011 return resolved(cluster); 1012 }); 1013 1014 self.directionsrenderer = chainToPromise(function (options) { 1015 var instance; 1016 if (options) { 1017 options = dupOpts(options); 1018 options.map = map; 1019 if (options.panel) { 1020 options.panel = $(options.panel).get(0); 1021 } 1022 instance = gmElement('DirectionsRenderer', options); 1023 } 1024 previousResults.push(instance); 1025 return instance; 1026 }); 1027 1028 self.latlng = chainToPromise(multiple(function (options) { 1029 return resolveLatLng(options, 'latlng', function (opts) { 1030 previousResults.push(opts.latlng); 1031 return opts.latlng; 1032 }); 1033 })); 1034 1035 self.fit = chainToPromise(function () { 1036 var bounds = gmElement('LatLngBounds'); 1037 foreach(previousResults, function (instances) { 1038 if (instances !== map) { 1039 foreach(instances, function (instance) { 1040 if (instance) { 1041 if (instance.getPosition && instance.getPosition()) { 1042 bounds.extend(instance.getPosition()); 1043 } else if (instance.getBounds && instance.getBounds()) { 1044 bounds.extend(instance.getBounds().getNorthEast()); 1045 bounds.extend(instance.getBounds().getSouthWest()); 1046 } else if (instance.getPaths && instance.getPaths()) { 1047 foreach(instance.getPaths().getArray(), function (path) { 1048 foreach(path.getArray(), function (latLng) { 1049 bounds.extend(latLng); 1050 }); 1051 }); 1052 } else if (instance.getPath && instance.getPath()) { 1053 foreach(instance.getPath().getArray(), function (latLng) { 1054 bounds.extend(latLng); 1055 }); 1056 } else if (instance.getCenter && instance.getCenter()) { 1057 bounds.extend(instance.getCenter()); 1058 } 1059 } 1060 }); 1061 } 1062 }); 1063 if (!bounds.isEmpty()) { 1064 map.fitBounds(bounds); 1065 } 1066 return true; 1067 }); 1068 1069 self.wait = function (duration) { 1070 promise = promise.then(function (instance) { 1071 var dfd = deferred(); 1072 setTimeout(function () { 1073 dfd.resolve(instance); 1074 }, duration); 1075 return dfd; 1076 }); 1077 }; 1078 1079 self.then = function (fn) { 1080 if (isFunction(fn)) { 1081 promise = promise.then(function (instance) { 1082 return when(fn.call(context(), instance)).then(function (newInstance) { 1083 return isUndefined(newInstance) ? instance : newInstance; 1084 }); 1085 }); 1086 } 1087 }; 1088 1089 self.catch = function (fn) { 1090 if (isFunction(fn)) { 1091 promise = promise 1092 .then(null, function (reason) { 1093 return when(fn.call(context(), reason)); 1094 }); 1095 } 1096 }; 1097 1098 foreachStr('on once', function (name, once) { 1099 self[name] = function () { 1100 var events = arguments[0]; 1101 if (events) { 1102 if (typeof events === 'string') { // cast call on('click', handler) to on({click: handler}) 1103 events = {}; 1104 events[arguments[0]] = slice(arguments, 1); 1105 } 1106 promise.then(function (instances) { 1107 if (instances) { 1108 if (instances instanceof Cluster) { 1109 instances._b(function (items) { 1110 if (items && items.length) { 1111 attachEvents(events, items, once); 1112 } 1113 }); 1114 return attachEvents(events, instances.markers(), [undefined, instances], once); 1115 } 1116 attachEvents(events, instances, once); 1117 } 1118 }); 1119 } 1120 }; 1121 }); 1122 1123 self.get = function (index) { 1124 if (isUndefined(index)) { 1125 return previousResults.map(function (instance) { 1126 return isArray(instance) ? instance.slice() : instance; 1127 }); 1128 } else { 1129 if (index < 0) { 1130 index = previousResults.length + index; 1131 } 1132 return isArray(previousResults[index]) ? previousResults[index].slice() : previousResults[index]; 1133 } 1134 }; 1135 1136 if (options) { 1137 self.map(options); 1138 } 1139 } 1140 1141})(jQuery, window, document);
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.