PageSourceSearch

https://gresna-gorica.hr/wp-content/themes/rosa/assets/js/vendor/gmap3.jquery.js

js gresna-gorica.hr collected 2026-10-04 11:06:43 UTC 28,361 bytes, 1,141 lines download raw bytes

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.