PageSourceSearch

https://www.duxiana.co.uk/assets/js/site/stores.js?v=VUYrhiYPT5zoRmH2dlg1jhbKKuZdtiYbiNYTNqdTdoI

js duxiana.co.uk collected 2026-09-26 07:51:47 UTC 12,686 bytes, 386 lines download raw bytes

1
2Vue.config.devtools = true;
3
4Vue.filter("distanceFormat",function(value) {
5	return String.format(window.mapOptions.map.distanceFormat,((value || 0) / 1000).toFixed(1));
6});
7
8var app = new Vue({
9	el: '#retailer-map',
10	data: function() {
11		return {
12			expanded: false,
13			showFeatured: true,
14			currentRetailer: null,
15			currentInfoWindow: null,
16			userPosition: false,
17			userMarker: null,
18			minMaxBounds: null,
19			options: {
20				lat: window.mapOptions.map.lat,
21				lng: window.mapOptions.map.lng,
22				zoom: window.mapOptions.map.zoom,
23				endPoint: window.mapOptions.map.endPoint
24			},
25			map: null,
26			markers: [],
27			featured: []
28		};
29	},
30	mounted: function() {
31		var that = this;
32		$(window).on("gmapsInit",function() {
33			that.$nextTick(function() {
34				that.loadMap();
35			})
36		});
37	},
38	methods: {
39		tracking: function(category,action,retailer) {
40			track(category,action,{'location-type': retailer.locationType,'location-subtype': retailer.locationSubType,'location-name': retailer.title,'location-url': retailer.slug});
41		},
42		mapsTracking: function(retailer) {
43			track('contact','click-map',{'location-type': retailer.locationType,'location-subtype': retailer.locationSubType,'location-name': retailer.title});
44		},
45		loadMap: function() {
46			var that = this;
47			var mobile = $(window).outerWidth() <= 768; // 'ontouchstart' in window || window.DocumentTouch && document instanceof window.DocumentTouch || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0;
48			this.map = new google.maps.Map(this.$refs.map,{
49				center: {lat: that.options.lat,lng: that.options.lng},
50				zoom: that.options.zoom,
51				disableDefaultUI: mobile,
52				zoomControl: !mobile,
53				mapTypeId: "roadmap", //hybrid roadmap satellite terrain
54				mapTypeControl: false,
55				scrollwheel: false,
56				draggable: true,
57				streetViewControl: false,
58				styles: googleMapsThemes.grey.json,
59			});
60			this.fetchRetailers(that.options.endPoint);
61			this.hookupAutocomplete();
62			this.setSearchOrigin(that.options.lat,that.options.lng);
63			google.maps.event.addListener(this.map,"tilesloaded",function() {
64				[].slice.apply(document.querySelectorAll('#map a, #map [tabindex="0"], #map button')).forEach(function(item) {
65					item.setAttribute('tabindex','-1');
66				})
67			});
68			//this.checkGeoLocation();
69		},
70		mapKey($event) {
71			//	if ($event.key == "Escape") {
72			//		document.querySelectorAll('.find-retailer-map')[0].focus();
73			//	}
74		},
75		checkRetailerFeaturedKeys(marker,index,url,$event) {
76			if ($event.key == "Enter" || $event.key == " ") {
77				window.location = url;
78			}
79			else {
80				switch ($event.key) {
81					case "ArrowUp":
82					case "ArrowLeft":
83						if (index > 0) {
84							this.$refs.featured[index - 1].focus();
85						}
86						break;
87					case "ArrowDown":
88					case "ArrowRight":
89						if (index < this.featured.length - 1) {
90							this.$refs.featured[index + 1].focus();
91						}
92						else if (this.markers.length > 0) {
93							this.$refs.markers[0].focus();
94						}
95						break;
96					case "Escape":
97						if (this.$refs.featured.length > 0)
98							this.$refs.featured[0].focus();
99						break;
100				}
101			}
102		},
103		checkRetailerListKeys(marker,index,$event) {
104			if ($event.key == "Enter" || $event.key == " ") {
105				this.selectRetailer(marker,index,true,$event)
106			}
107			else {
108				switch ($event.key) {
109					case "ArrowUp":
110					case "ArrowLeft":
111						if (index > 0) {
112							this.$refs.markers[index - 1].focus();
113						}
114						else {
115							if (this.$refs.featured.length > 0) {
116								this.$refs.featured[this.$refs.featured.length - 1].focus();
117							}
118						}
119						break;
120					case "ArrowDown":
121					case "ArrowRight":
122						if (index < this.markers.length - 1) {
123							this.$refs.markers[index + 1].focus();
124						}
125						break;
126					case "Escape":
127						if (this.$refs.featured.length > 0)
128							this.$refs.featured[0].focus();
129						else if (this.$refs.markers.length > 0)
130							this.$refs.markers[0].focus();
131						break;
132				}
133			}
134		},
135		selectRetailer: function(marker,index,skipSort) {
136			var that = this;
137			that.currentRetailer = marker.retailer;
138			if (marker && marker.position) {
139				that.setMapCenter(marker.position,skipSort);
140				//track('search-store', 'click-map', {'location-type': marker.retailer.locationType, 'location-subtype' : marker.retailer.locationSubType,'title' : marker.retailer.title, 'city' : marker.retailer.city});
141
142				setTimeout(function() {
143					// TODO: find a "vue" way of doing this
144					// scroll to the selected retailer in the list
145					var $listElement = $(that.$refs.markerList)
146					var $liElement = $listElement.find("li[data-key=" + marker.retailer.key + "]").first();
147					var elementY = $liElement[0].offsetTop;
148					var scrollY = $listElement.parent().scrollTop();
149					var listHeight = $listElement.parent().outerHeight();
150
151					if (elementY < scrollY || elementY > (scrollY + listHeight)) {
152						$listElement.parent().animate(
153							{
154								scrollTop: elementY
155							},300);
156					}
157				},400);
158			}
159		},
160		fetchRetailers: function(url) {
161			var that = this;
162			$.get(url)
163				.done(function(data) {
164					that.featured = data.featured || [];
165					if (data.retailers && data.retailers.length)
166						that.addMarkers(data.retailers);
167					that.expanded = that.expanded || (that.showFeatured && that.featured.length > 0);
168				});
169		},
170		setMapCenter: function(position,skipSort) {
171			this.map.setCenter(position);
172			if (!skipSort)
173				this.sortMarkersByDistance();
174		},
175		setSearchOrigin: function(latitude,longitude) {
176			var point = new google.maps.LatLng({lat: latitude,lng: longitude});
177			if (!this.userMarker) {
178				this.userMarker = new google.maps.Marker({
179					map: this.map,
180					position: point,
181					draggable: false,
182					clickable: true,
183					zIndex: 999999,
184					infoWindow: new google.maps.InfoWindow({
185						content: window.mapOptions.map.startPositionTitle
186					})
187				});
188
189				this.userMarker.setIcon({
190					url: "/assets/js/site/location-search/icons/relevant-position.png",
191					size: new google.maps.Size(32,32),
192					anchor: new google.maps.Point(16,16)
193				});
194
195				var that = this;
196				that.userMarker.addListener('click',function() {
197					if (that.currentInfoWindow) {
198						that.currentInfoWindow.close();
199					}
200					that.userMarker.infoWindow.open(that.map,that.userMarker);
201					that.currentInfoWindow = that.userMarker.infoWindow;
202				});
203			}
204			this.userMarker.setPosition(point);
205			this.sortMarkersByDistance();
206		},
207		addMarkers: function(retailers) {
208			var that = this;
209			if (retailers) {
210				retailers.forEach(function(retailer) {
211					var marker = new google.maps.Marker({
212						map: that.map,
213						position: {lat: retailer.lat,lng: retailer.lng},
214						retailer: retailer
215					});
216
217					marker.infoWindow = new google.maps.InfoWindow({
218						content: String.format("<a href='{5}{0}'>{1}<br />{2}<br />{3},{4}</a>",
219							retailer.slug,retailer.title,retailer.street,retailer.zipAndCity,retailer.country,window.url)
220					});
221
222					marker.setIcon({
223						url: marker.retailer.isDuxiana ? "/assets/js/site/location-search/icons/map-marker.svg" : "/assets/js/site/location-search/icons/map-marker-black.svg",
224						anchor: new google.maps.Point(18,45),
225						scaledSize: new google.maps.Size(37,45)
226					});
227
228					marker.addListener('click',function() {
229						if (that.currentInfoWindow) {
230							that.currentInfoWindow.close();
231						}
232
233						marker.infoWindow.open(that.map,marker);
234						that.currentInfoWindow = marker.infoWindow;
235						marker.setZIndex(99999);
236						that.selectRetailer(marker,true);
237					});
238
239					that.markers.push(marker);
240				});
241			}
242			that.minMaxBounds = Object.assign({},new google.maps.LatLngBounds(),{
243				lat: Math.min.apply(Math,that.markers.map(function(marker) {return marker.retailer.lat;})),
244				lng: Math.min.apply(Math,that.markers.map(function(marker) {return marker.retailer.lng;}))
245			});
246			that.sortMarkersByDistance();
247		},
248		sortMarkersByDistance: function(origin) {
249			var that = this;
250
251			if (!that.map || !that.markers || !that.markers.length) {
252				return;
253			}
254			origin = origin || (that.userMarker || {}).position || that.map.getCenter();
255			that.markers.forEach(function(marker) {
256				// Set distance
257				marker.retailer.distanceFromUserPosition = google.maps.geometry.spherical.computeDistanceBetween(marker.getPosition(),origin);
258				that.featured.filter(function(item) {
259					if (item.key == marker.retailer.key)
260						item.distanceFromUserPosition = marker.retailer.distanceFromUserPosition;
261					return true;
262				});
263				// Set bearing
264				//marker.retailer.headingFromUserPosition = google.maps.geometry.spherical.computeHeading(marker.getPosition(), origin);
265			});
266
267			that.markers.sort(function(a,b) {
268				return a.retailer.distanceFromUserPosition - b.retailer.distanceFromUserPosition;
269			});
270		},
271
272		// PLACES
273		hookupAutocomplete: function() {
274			var that = this;
275			that.autocomplete = new google.maps.places.Autocomplete(that.$refs.placesSearch);
276
277			// Bind the map's bounds (viewport) property to the autocomplete object,
278			// so that the autocomplete requests use the current map bounds for the
279			// bounds option in the request.
280			that.autocomplete.bindTo('bounds',that.map);
281
282			that.autocomplete.addListener('place_changed',function() {
283				var place = that.autocomplete.getPlace();
284
285				if (!place.geometry) {
286					that.fallbackToFirstPlaceResult(place,that.handleResolvedPlace)
287				} else {
288					that.handleResolvedPlace(place);
289				}
290				that.showFeatured = false;
291			});
292		},
293		fallbackToFirstPlaceResult: function(place,callback) {
294			// User entered the name of a Place that was not suggested and pressed the Enter key, or the Place Details request failed.
295			// We try to get the same result again and populate with the first result
296			var that = this;
297			var autocompleteService = new google.maps.places.AutocompleteService();
298
299			if (!that.minMaxBounds.length) {
300				// Calculate a LatLngBounds object that includes all the markers
301				that.minMaxBounds = new google.maps.LatLngBounds();
302				that.minMaxBounds.extend({
303					lat: Math.min.apply(Math,that.markers.map(function(marker) {return marker.retailer.lat;})),
304					lng: Math.min.apply(Math,that.markers.map(function(marker) {return marker.retailer.lng;}))
305				});
306			}
307			autocompleteService.getPlacePredictions({
308				input: place.name,
309				offset: place.name.length,
310				bounds: that.minMaxBounds
311			},function(list,status) {
312				if (list && list.length) {
313					var placesService = new google.maps.places.PlacesService(that.$refs.placesSearch);
314
315					placesService.getDetails({'reference': list[0].reference},function detailsresult(detailsResult,placesServiceStatus) {
316						callback(detailsResult);
317					});
318				}
319			});
320		},
321		handleResolvedPlace: function(place) {
322			var maxAutoZoom = 0;
323			var that = this;
324			// Unable to find a place
325			if (!place.geometry) {
326				return;
327			}
328
329			// If the place has a geometry, then present it on a map.
330			if (place.geometry.viewport) {
331				that.map.fitBounds(place.geometry.viewport);
332				if (maxAutoZoom > 0) {
333					// Handle the zoom-level if set
334					google.maps.event.addListenerOnce(that.map,"idle",function() {
335						if (that.map.getZoom() > maxAutoZoom) {
336							that.map.setZoom(maxAutoZoom)
337						};
338					});
339				}
340			} else {
341				if (maxAutoZoom > 0) {
342					that.map.setZoom(maxAutoZoom);
343				}
344			}
345			that.setMapCenter(place.geometry.location);
346			that.userPosition = false;
347			that.setSearchOrigin(place.geometry.location.lat(),place.geometry.location.lng());
348
349			track('search-store','search',{'search-text': that.$refs.placesSearch.value});
350			$(that.$refs.markerList).parent().animate(
351				{
352					scrollTop: 0
353				},300);
354			that.$refs.placesSearch.blur();
355		},
356		// GEO LOCATION
357		checkGeoLocation: function() {
358			var that = this;
359			if (navigator.geolocation) {
360				navigator.geolocation.getCurrentPosition(function(position) {
361					track('search-store','location-enabled',{'enabled': 'true'});
362					that.setMapCenter({lat: position.coords.latitude,lng: position.coords.longitude});
363					// Set user's known location
364					that.setSearchOrigin(position.coords.latitude,position.coords.longitude);
365					that.userPosition = true;
366				},function() {
367					track('search-store','location-enabled',{'enabled': 'false'});
368					that.setMapCenter({lat: that.options.lat,lng: that.options.lng});
369				});
370			}
371			else {
372				that.setMapCenter({lat: that.options.lat,lng: that.options.lng});
373				track('search-store','location-enabled',{'enabled': 'false'});
374			}
375			that.$refs.placesSearch.value = '';
376			that.showFeatured = false;
377
378		},
379		markerHref: function(phone) {
380			return 'tel:' + phone;
381		},
382		markerMailTo(email) {
383			return 'mailto:' + email;
384		}
385	}
386});

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.