mirror of https://github.com/Squidex/squidex.git
32 changed files with 978 additions and 330 deletions
@ -0,0 +1,57 @@ |
|||
// ==========================================================================
|
|||
// GoogleMapsGeocoder.cs
|
|||
// CivicPlus implementation of Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
using System.Linq; |
|||
using System.Net.Http; |
|||
using System.Threading.Tasks; |
|||
using Newtonsoft.Json; |
|||
using Newtonsoft.Json.Linq; |
|||
|
|||
namespace Squidex.Infrastructure.Geocoding |
|||
{ |
|||
public class GoogleMapsGeocoder : IGeocoder |
|||
{ |
|||
private readonly string geoCodeUrl = "https://maps.googleapis.com/maps/api/geocode/json"; |
|||
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2); |
|||
|
|||
public GoogleMapsGeocoder(string key) |
|||
{ |
|||
Key = key; |
|||
} |
|||
|
|||
public string Key { get; } |
|||
|
|||
public object GeocodeAddress(string address) |
|||
{ |
|||
var requestUrl = $"{geoCodeUrl}?key={Key}&address={address}"; |
|||
var request = new HttpRequestMessage(HttpMethod.Get, requestUrl); |
|||
|
|||
return GetLatLong(request).Result; |
|||
} |
|||
|
|||
private async Task<object> GetLatLong(HttpRequestMessage request) |
|||
{ |
|||
try |
|||
{ |
|||
HttpResponseMessage response; |
|||
using (var client = new HttpClient { Timeout = Timeout }) |
|||
{ |
|||
response = await client.SendAsync(request); |
|||
} |
|||
|
|||
var result = JsonConvert.DeserializeObject<JObject>(response.Content.ReadAsStringAsync().Result); |
|||
var innerResults = ((JObject)result["results"].FirstOrDefault()); |
|||
var geometry = ((JObject)innerResults["geometry"]); |
|||
var location = ((JObject)geometry["location"]); |
|||
return new { Latitude = location["lat"], Longitude = location["lng"] }; |
|||
} |
|||
catch |
|||
{ |
|||
throw new InvalidCastException("Latitude and Longitude could not be calculated. Please enter a valid address."); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,14 @@ |
|||
// ==========================================================================
|
|||
// IGeocoder.cs
|
|||
// CivicPlus implementation of Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
|
|||
namespace Squidex.Infrastructure.Geocoding |
|||
{ |
|||
public interface IGeocoder |
|||
{ |
|||
string Key { get; } |
|||
|
|||
object GeocodeAddress(string address); |
|||
} |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
// ==========================================================================
|
|||
// OSMGeocoder.cs
|
|||
// CivicPlus implementation of Squidex Headless CMS
|
|||
// ==========================================================================
|
|||
|
|||
using System; |
|||
|
|||
namespace Squidex.Infrastructure.Geocoding |
|||
{ |
|||
public class OSMGeocoder : IGeocoder |
|||
{ |
|||
public string Key { get; } |
|||
|
|||
public object GeocodeAddress(string address) |
|||
{ |
|||
throw new InvalidCastException("Latitude and Longitude must be provided."); |
|||
} |
|||
} |
|||
} |
|||
@ -1,19 +0,0 @@ |
|||
<div> |
|||
<div class="editor" #editor></div> |
|||
|
|||
<div> |
|||
<form class="form-inline" [formGroup]="geolocationForm" (submit)="updateValueByInput()"> |
|||
<div class="form-group latitude-group"> |
|||
<input type="number" class="form-control" formControlName="latitude" step="any" #dateInput /> |
|||
</div> |
|||
<div class="form-group longitude-group"> |
|||
<input type="number" class="form-control" formControlName="longitude" step="any" /> |
|||
</div> |
|||
<div class="form-group" [class.hidden]="!hasValue"> |
|||
<button type="reset" class="btn btn-link clear" [disabled]="isDisabled" (click)="reset()">Clear</button> |
|||
</div> |
|||
|
|||
<button type="submit" class="hidden"></button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
@ -1,14 +0,0 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
.editor { |
|||
height: 30rem; |
|||
} |
|||
|
|||
.form-inline { |
|||
margin-top: .5rem; |
|||
} |
|||
|
|||
.latitude-group { |
|||
margin-right: .25rem; |
|||
} |
|||
@ -1,217 +0,0 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; |
|||
import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
|
|||
import { Types } from './../utils/types'; |
|||
|
|||
import { ResourceLoaderService } from './../services/resource-loader.service'; |
|||
import { ValidatorsEx } from './validators'; |
|||
|
|||
declare var L: any; |
|||
|
|||
export const SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => GeolocationEditorComponent), multi: true |
|||
}; |
|||
|
|||
interface Geolocation { |
|||
latitude: number; |
|||
longitude: number; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'sqx-geolocation-editor', |
|||
styleUrls: ['./geolocation-editor.component.scss'], |
|||
templateUrl: './geolocation-editor.component.html', |
|||
providers: [SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR] |
|||
}) |
|||
export class GeolocationEditorComponent implements ControlValueAccessor, AfterViewInit { |
|||
private callChange = (v: any) => { /* NOOP */ }; |
|||
private callTouched = () => { /* NOOP */ }; |
|||
private marker: any; |
|||
private map: any; |
|||
private value: Geolocation | null = null; |
|||
|
|||
public get hasValue() { |
|||
return !!this.value; |
|||
} |
|||
|
|||
public geolocationForm = |
|||
this.formBuilder.group({ |
|||
latitude: ['', |
|||
[ |
|||
ValidatorsEx.between(-90, 90) |
|||
]], |
|||
longitude: ['', |
|||
[ |
|||
ValidatorsEx.between(-180, 180) |
|||
]] |
|||
}); |
|||
|
|||
@ViewChild('editor') |
|||
public editor: ElementRef; |
|||
|
|||
public isDisabled = false; |
|||
|
|||
constructor( |
|||
private readonly resourceLoader: ResourceLoaderService, |
|||
private readonly formBuilder: FormBuilder |
|||
) { |
|||
} |
|||
|
|||
public writeValue(value: Geolocation) { |
|||
if (Types.isObject(value) && Types.isNumber(value.latitude) && Types.isNumber(value.longitude)) { |
|||
this.value = value; |
|||
} else { |
|||
this.value = null; |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.updateMarker(true, false); |
|||
} |
|||
} |
|||
|
|||
public setDisabledState(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
|
|||
if (isDisabled) { |
|||
if (this.map) { |
|||
this.map.zoomControl.disable(); |
|||
|
|||
this.map._handlers.forEach((handler: any) => { |
|||
handler.disable(); |
|||
}); |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.marker.dragging.disable(); |
|||
} |
|||
|
|||
this.geolocationForm.disable(); |
|||
} else { |
|||
if (this.map) { |
|||
this.map.zoomControl.enable(); |
|||
|
|||
this.map._handlers.forEach((handler: any) => { |
|||
handler.enable(); |
|||
}); |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.marker.dragging.enable(); |
|||
} |
|||
|
|||
this.geolocationForm.enable(); |
|||
} |
|||
} |
|||
|
|||
public registerOnChange(fn: any) { |
|||
this.callChange = fn; |
|||
} |
|||
|
|||
public registerOnTouched(fn: any) { |
|||
this.callTouched = fn; |
|||
} |
|||
|
|||
public updateValueByInput() { |
|||
if (this.geolocationForm.controls['latitude'].value !== null && |
|||
this.geolocationForm.controls['longitude'].value !== null && |
|||
this.geolocationForm.valid) { |
|||
this.value = this.geolocationForm.value; |
|||
} else { |
|||
this.value = null; |
|||
} |
|||
|
|||
this.updateMarker(true, true); |
|||
} |
|||
|
|||
public ngAfterViewInit() { |
|||
this.resourceLoader.loadStyle('https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css'); |
|||
this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js').then(() => { |
|||
this.map = L.map(this.editor.nativeElement).fitWorld(); |
|||
|
|||
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { |
|||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' |
|||
}).addTo(this.map); |
|||
|
|||
this.map.on('click', (event: any) => { |
|||
if (!this.marker && !this.isDisabled) { |
|||
const latlng = event.latlng.wrap(); |
|||
|
|||
this.value = { latitude: latlng.lat, longitude: latlng.lng }; |
|||
|
|||
this.updateMarker(false, true); |
|||
} |
|||
}); |
|||
|
|||
this.updateMarker(true, false); |
|||
|
|||
if (this.isDisabled) { |
|||
this.map.zoomControl.disable(); |
|||
|
|||
this.map._handlers.forEach((handler: any) => { |
|||
handler.disable(); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public reset() { |
|||
this.value = null; |
|||
|
|||
this.updateMarker(true, true); |
|||
} |
|||
|
|||
private updateMarker(zoom: boolean, fireEvent: boolean) { |
|||
if (this.value) { |
|||
if (!this.marker) { |
|||
this.marker = L.marker([0, 90], { draggable: true }).addTo(this.map); |
|||
|
|||
this.marker.on('drag', (event: any) => { |
|||
const latlng = event.latlng.wrap(); |
|||
|
|||
this.value = { latitude: latlng.lat, longitude: latlng.lng }; |
|||
}); |
|||
|
|||
this.marker.on('dragend', () => { |
|||
this.updateMarker(false, true); |
|||
}); |
|||
|
|||
if (this.isDisabled) { |
|||
this.marker.dragging.disable(); |
|||
} |
|||
} |
|||
|
|||
const latLng = L.latLng(this.value.latitude, this.value.longitude); |
|||
|
|||
if (zoom) { |
|||
this.map.setView(latLng, 8); |
|||
} else { |
|||
this.map.panTo(latLng); |
|||
} |
|||
|
|||
this.marker.setLatLng(latLng); |
|||
|
|||
this.geolocationForm.setValue(this.value, { emitEvent: false, onlySelf: false }); |
|||
} else { |
|||
if (this.marker) { |
|||
this.marker.removeFrom(this.map); |
|||
this.marker = null; |
|||
} |
|||
|
|||
this.map.fitWorld(); |
|||
|
|||
this.geolocationForm.reset(undefined, { emitEvent: false, onlySelf: false }); |
|||
} |
|||
|
|||
if (fireEvent) { |
|||
this.callChange(this.value); |
|||
this.callTouched(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
<div> |
|||
<form> |
|||
<div class="editor" #editor></div> |
|||
<input id="pac-input" class="form-control controls" type="text" placeholder="Search Google Maps" #searchBox /> |
|||
</form> |
|||
<div> |
|||
<form [formGroup]="geolocationForm" (change)="updateValueByInput()" [class.form-inline]="!useGoogleMaps"> |
|||
<sqx-control-errors for="state"></sqx-control-errors> |
|||
<sqx-control-errors for="zip"></sqx-control-errors> |
|||
<div class="form-group latitude-group" [hidden]="useGoogleMaps"> |
|||
<sqx-control-errors for="latitude" style="z-index: 10000;"></sqx-control-errors> |
|||
<label for="latitude">Lat. </label> |
|||
<input type="number" class="form-control" formControlName="latitude" step="any"/> |
|||
</div> |
|||
<div class="form-group longitude-group" [hidden]="useGoogleMaps"> |
|||
<sqx-control-errors for="longitude" style="z-index: 10000;"></sqx-control-errors> |
|||
<label for="longitude">Long. </label> |
|||
<input type="number" class="form-control" formControlName="longitude" step="any"/> |
|||
</div> |
|||
<div class="form-group address1-group" [hidden]="!useGoogleMaps"> |
|||
<label for="latitude">Address 1 </label> |
|||
<input type="text" class="form-control" formControlName="address1"/> |
|||
</div> |
|||
<div class="form-group address2-group" [hidden]="!useGoogleMaps"> |
|||
<label for="longitude">Address 2 </label> |
|||
<input type="text" class="form-control" formControlName="address2"/> |
|||
</div> |
|||
<div class="form-group city-group" [hidden]="!useGoogleMaps" [class.hasClear]="hasValue"> |
|||
<label for="latitude">City </label> |
|||
<input type="text" class="form-control" formControlName="city"/> |
|||
</div> |
|||
<div class="form-group state-group" [hidden]="!useGoogleMaps"> |
|||
<label for="longitude">State </label> |
|||
<input type="text" class="form-control" formControlName="state" sqxUpperCaseInput/> |
|||
</div> |
|||
<div class="form-group zip-group" [hidden]="!useGoogleMaps"> |
|||
<label for="longitude">ZIP Code </label> |
|||
<input type="text" class="form-control" formControlName="zip"/> |
|||
</div> |
|||
<div class="form-group clear-group" [class.hidden]="!hasValue"> |
|||
<button type="reset" class="btn btn-link clear" [disabled]="isDisabled" (click)="reset()">Clear</button> |
|||
</div> |
|||
|
|||
<button type="submit" class="hidden"></button> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
@ -0,0 +1,68 @@ |
|||
@import '_mixins'; |
|||
@import '_vars'; |
|||
|
|||
.editor { |
|||
height: 30rem; |
|||
} |
|||
|
|||
.form-group { |
|||
margin-top: .5rem; |
|||
margin-bottom: .5rem; |
|||
|
|||
|
|||
&.city-group { |
|||
display: inline-block; |
|||
width: 70%; |
|||
|
|||
&.hasClear{ |
|||
width: 55%; |
|||
} |
|||
} |
|||
|
|||
&.state-group { |
|||
display: inline-block; |
|||
width: 10%; |
|||
} |
|||
|
|||
&.zip-group { |
|||
display: inline-block; |
|||
width: 18.5%; |
|||
} |
|||
|
|||
&.clear-group { |
|||
display: inline-block; |
|||
&.hidden{ |
|||
display: none; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.latitude-group { |
|||
margin-right: .25rem; |
|||
} |
|||
|
|||
#pac-input { |
|||
background-color: #fff; |
|||
font-size: 15px; |
|||
font-weight: 300; |
|||
margin-left: 12px; |
|||
padding: 0 11px 0 13px; |
|||
text-overflow: ellipsis; |
|||
width: 400px; |
|||
height: 40px; |
|||
|
|||
&:focus { |
|||
border-color: #4d90fe; |
|||
} |
|||
} |
|||
|
|||
#type-selector { |
|||
color: #fff; |
|||
background-color: #4d90fe; |
|||
padding: 5px 11px 0px 11px; |
|||
} |
|||
|
|||
#type-selector label { |
|||
font-size: 13px; |
|||
font-weight: 300; |
|||
} |
|||
@ -0,0 +1,496 @@ |
|||
/* |
|||
* Squidex Headless CMS |
|||
* |
|||
* @license |
|||
* Copyright (c) Sebastian Stehle. All rights reserved |
|||
*/ |
|||
|
|||
import { AfterViewInit, Component, ElementRef, forwardRef, ViewChild } from '@angular/core'; |
|||
import { ControlValueAccessor, FormBuilder, NG_VALUE_ACCESSOR } from '@angular/forms'; |
|||
|
|||
import { Types } from './../../framework/utils/types'; |
|||
|
|||
import { ResourceLoaderService } from './../../framework/services/resource-loader.service'; |
|||
import { ValidatorsEx } from './../../framework/angular/validators'; |
|||
|
|||
import { AppContext } from './app-context'; |
|||
|
|||
declare var L: any; |
|||
declare var google: any; |
|||
|
|||
export const SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR: any = { |
|||
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => GeolocationEditorComponent), multi: true |
|||
}; |
|||
|
|||
interface Geolocation { |
|||
latitude: number; |
|||
longitude: number; |
|||
address1: string; |
|||
address2: string; |
|||
city: string; |
|||
state: string; |
|||
zip: string; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: 'sqx-geolocation-editor', |
|||
styleUrls: ['./geolocation-editor.component.scss'], |
|||
templateUrl: './geolocation-editor.component.html', |
|||
providers: [SQX_GEOLOCATION_EDITOR_CONTROL_VALUE_ACCESSOR] |
|||
}) |
|||
export class GeolocationEditorComponent implements ControlValueAccessor, AfterViewInit { |
|||
private callChange = (v: any) => { /* NOOP */ }; |
|||
private callTouched = () => { /* NOOP */ }; |
|||
private marker: any; |
|||
private map: any; |
|||
private searchBox: any; |
|||
private value: Geolocation | null = null; |
|||
public useGoogleMaps = false; |
|||
|
|||
public get hasValue() { |
|||
return !!this.value; |
|||
} |
|||
|
|||
public geolocationForm = |
|||
this.formBuilder.group({ |
|||
address1: ['', []], |
|||
address2: ['', []], |
|||
city: ['', []], |
|||
state: [ |
|||
'', [ |
|||
ValidatorsEx.pattern('[A-Z]{2}', 'This field must be a valid state abbreviation.') |
|||
] |
|||
], |
|||
zip: [ |
|||
'', [ |
|||
ValidatorsEx.pattern('[0-9]{5}(\-[0-9]{4})?', 'This field must be a valid ZIP Code.') |
|||
] |
|||
], |
|||
latitude: [ |
|||
'', |
|||
[ |
|||
ValidatorsEx.between(-90, 90) |
|||
] |
|||
], |
|||
longitude: [ |
|||
'', |
|||
[ |
|||
ValidatorsEx.between(-180, 180) |
|||
] |
|||
] |
|||
}); |
|||
|
|||
@ViewChild('editor') |
|||
public editor: ElementRef; |
|||
|
|||
@ViewChild('searchBox') |
|||
public searchBoxInput: ElementRef; |
|||
|
|||
public isDisabled = false; |
|||
|
|||
constructor( |
|||
private readonly resourceLoader: ResourceLoaderService, |
|||
private readonly formBuilder: FormBuilder, |
|||
private readonly ctx: AppContext |
|||
) { |
|||
this.useGoogleMaps = this.ctx.app.geocoderKey !== ''; |
|||
} |
|||
|
|||
public writeValue(value: Geolocation) { |
|||
if (Types.isObject(value) && Types.isNumber(value.latitude) && Types.isNumber(value.longitude)) { |
|||
this.value = value; |
|||
} else { |
|||
this.value = null; |
|||
} |
|||
|
|||
if (this.marker || (!this.marker && this.map && this.value)) { |
|||
this.updateMarker(true, false); |
|||
} |
|||
} |
|||
|
|||
public setDisabledState(isDisabled: boolean): void { |
|||
if (this.ctx.app.geocoderKey === '') { |
|||
this.setDisabledStateOSM(isDisabled); |
|||
} else { |
|||
this.setDisabledStateGoogle(isDisabled); |
|||
} |
|||
} |
|||
|
|||
private setDisabledStateOSM(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
|
|||
if (isDisabled) { |
|||
if (this.map) { |
|||
this.map.zoomControl.disable(); |
|||
|
|||
this.map._handlers.forEach((handler: any) => { |
|||
handler.disable(); |
|||
}); |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.marker.dragging.disable(); |
|||
} |
|||
|
|||
this.geolocationForm.disable(); |
|||
} else { |
|||
if (this.map) { |
|||
this.map.zoomControl.enable(); |
|||
|
|||
this.map._handlers.forEach((handler: any) => { |
|||
handler.enable(); |
|||
}); |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.marker.dragging.enable(); |
|||
} |
|||
|
|||
this.geolocationForm.enable(); |
|||
} |
|||
} |
|||
|
|||
private setDisabledStateGoogle(isDisabled: boolean): void { |
|||
this.isDisabled = isDisabled; |
|||
|
|||
if (isDisabled) { |
|||
if (this.map) { |
|||
this.map.setOptions({ |
|||
draggable: false, |
|||
zoomControl: false |
|||
}); |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.marker.setDraggable(false); |
|||
} |
|||
|
|||
this.geolocationForm.disable(); |
|||
this.searchBoxInput.nativeElement.disabled = true; |
|||
} else { |
|||
if (this.map) { |
|||
this.map.setOptions({ |
|||
draggable: true, |
|||
zoomControl: true |
|||
}); |
|||
} |
|||
|
|||
if (this.marker) { |
|||
this.marker.setDraggable(true); |
|||
} |
|||
|
|||
this.geolocationForm.enable(); |
|||
this.searchBoxInput.nativeElement.disabled = false; |
|||
} |
|||
} |
|||
|
|||
public registerOnChange(fn: any) { |
|||
this.callChange = fn; |
|||
} |
|||
|
|||
public registerOnTouched(fn: any) { |
|||
this.callTouched = fn; |
|||
} |
|||
|
|||
public updateValueByInput() { |
|||
let updateMap = this.geolocationForm.controls['latitude'].value !== null && |
|||
this.geolocationForm.controls['longitude'].value !== null; |
|||
|
|||
this.value = this.geolocationForm.value; |
|||
|
|||
if (updateMap) { |
|||
this.updateMarker(true, true); |
|||
} else { |
|||
this.callChange(this.value); |
|||
this.callTouched(); |
|||
} |
|||
} |
|||
|
|||
public ngAfterViewInit() { |
|||
if (!this.useGoogleMaps) { |
|||
this.ngAfterViewInitOSM(); |
|||
} else { |
|||
this.ngAfterViewInitGoogle(); |
|||
} |
|||
} |
|||
|
|||
private ngAfterViewInitOSM() { |
|||
this.searchBoxInput.nativeElement.remove(); |
|||
|
|||
this.resourceLoader.loadStyle('https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.css'); |
|||
this.resourceLoader.loadScript('https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.3/leaflet.js').then( |
|||
() => { |
|||
this.map = L.map(this.editor.nativeElement).fitWorld(); |
|||
|
|||
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', |
|||
{ |
|||
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' |
|||
}).addTo(this.map); |
|||
|
|||
this.map.on('click', |
|||
(event: any) => { |
|||
if (!this.marker && !this.isDisabled) { |
|||
const latlng = event.latlng.wrap(); |
|||
|
|||
this.value = { |
|||
latitude: latlng.lat, |
|||
longitude: latlng.lng, |
|||
address1: '', |
|||
address2: '', |
|||
city: '', |
|||
state: '', |
|||
zip: '' |
|||
}; |
|||
|
|||
this.updateMarker(false, true); |
|||
} |
|||
}); |
|||
|
|||
this.updateMarker(true, false); |
|||
|
|||
if (this.isDisabled) { |
|||
this.map.zoomControl.disable(); |
|||
|
|||
this.map._handlers.forEach((handler: any) => { |
|||
handler.disable(); |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
private ngAfterViewInitGoogle() { |
|||
this.resourceLoader.loadScript(`https://maps.googleapis.com/maps/api/js?key=${this.ctx.app.geocoderKey}&libraries=places`).then( |
|||
() => { |
|||
this.map = new google.maps.Map(this.editor.nativeElement, |
|||
{ |
|||
zoom: 1, |
|||
mapTypeControl: false, |
|||
streetViewControl: false, |
|||
center: { lat: 0, lng: 0 } |
|||
}); |
|||
|
|||
this.searchBox = new google.maps.places.SearchBox(this.searchBoxInput.nativeElement); |
|||
this.map.controls[google.maps.ControlPosition.LEFT_TOP].push(this.searchBoxInput.nativeElement); |
|||
|
|||
this.map.addListener('click', |
|||
(event: any) => { |
|||
if (!this.isDisabled) { |
|||
this.value = { |
|||
latitude: event.latLng.lat(), |
|||
longitude: event.latLng.lng(), |
|||
address1: this.value == null ? '' : this.value.address1, |
|||
address2: this.value == null ? '' : this.value.address2, |
|||
city: this.value == null ? '' : this.value.city, |
|||
state: this.value == null ? '' : this.value.state, |
|||
zip: this.value == null ? '' : this.value.zip |
|||
}; |
|||
|
|||
this.updateMarker(false, true); |
|||
} |
|||
}); |
|||
|
|||
this.map.addListener('bounds_changed', (event: any) => { |
|||
this.searchBox.setBounds(this.map.getBounds()); |
|||
}); |
|||
|
|||
this.searchBox.addListener('places_changed', (event: any) => { |
|||
let places = this.searchBox.getPlaces(); |
|||
|
|||
if (places.length === 1) { |
|||
let place = places[0]; |
|||
|
|||
if (!place.geometry) { |
|||
console.log('Returned place contains no geometry'); |
|||
return; |
|||
} |
|||
|
|||
if (!this.isDisabled) { |
|||
this.value = this.parseAddress(place); |
|||
|
|||
this.updateMarker(false, true); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
this.updateMarker(true, false); |
|||
|
|||
if (this.isDisabled) { |
|||
this.map.setOptions({ |
|||
draggable: false, |
|||
zoomControl: false |
|||
}); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
public reset() { |
|||
this.value = null; |
|||
this.searchBoxInput.nativeElement.value = null; |
|||
|
|||
this.updateMarker(true, true); |
|||
} |
|||
|
|||
private parseAddress(value: any): Geolocation { |
|||
let latitude = value.geometry.location.lat(); |
|||
let longitude = value.geometry.location.lng(); |
|||
let address1 = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('street_number') > -1)) |
|||
+ ' ' + this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('route') > -1)); |
|||
let address2 = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('subpremise') > -1)); |
|||
let city = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('locality') > -1)); |
|||
let state = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('administrative_area_level_1') > -1)).toUpperCase(); |
|||
|
|||
let zipCode = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('postal_code') > -1)); |
|||
let zipCodeSuffix = this.getAddressValue(value.address_components.find((a: any) => a.types.indexOf('postal_code_suffix') > -1)); |
|||
let zip = zipCodeSuffix === '' ? zipCode : zipCode + '-' + zipCodeSuffix; |
|||
|
|||
return { latitude: latitude, longitude: longitude, address1: address1, address2: address2, city: city, state: state, zip: zip }; |
|||
} |
|||
|
|||
private getAddressValue(value: any) { |
|||
return value == null ? '' : value.short_name; |
|||
} |
|||
|
|||
private fillMissingParameters() { |
|||
return { |
|||
latitude: this.value.latitude, |
|||
longitude: this.value.longitude, |
|||
address1: this.value.address1 ? this.value.address1 : '', |
|||
address2: this.value.address2 ? this.value.address2 : '', |
|||
city: this.value.city ? this.value.city : '', |
|||
state: this.value.state ? this.value.state : '', |
|||
zip: this.value.zip ? this.value.zip : '' |
|||
}; |
|||
} |
|||
|
|||
private updateMarker(zoom: boolean, fireEvent: boolean) { |
|||
if (this.ctx.app.geocoderKey === '') { |
|||
this.updateMarkerOSM(zoom, fireEvent); |
|||
} else { |
|||
this.updateMarkerGoogle(zoom, fireEvent); |
|||
} |
|||
} |
|||
|
|||
private updateMarkerOSM(zoom: boolean, fireEvent: boolean) { |
|||
if (this.value) { |
|||
if (!this.marker) { |
|||
this.marker = L.marker([0, 90], { draggable: true }).addTo(this.map); |
|||
|
|||
this.marker.on('drag', (event: any) => { |
|||
const latlng = event.latlng.wrap(); |
|||
|
|||
this.value = { |
|||
latitude: latlng.lat, |
|||
longitude: latlng.lng, |
|||
address1: '', |
|||
address2: '', |
|||
city: '', |
|||
state: '', |
|||
zip: '' }; |
|||
}); |
|||
|
|||
this.marker.on('dragend', () => { |
|||
this.updateMarker(false, true); |
|||
}); |
|||
|
|||
if (this.isDisabled) { |
|||
this.marker.dragging.disable(); |
|||
} |
|||
} |
|||
|
|||
const latLng = L.latLng(this.value.latitude, this.value.longitude); |
|||
|
|||
if (zoom) { |
|||
this.map.setView(latLng, 8); |
|||
} else { |
|||
this.map.panTo(latLng); |
|||
} |
|||
|
|||
this.marker.setLatLng(latLng); |
|||
|
|||
this.geolocationForm.setValue(this.fillMissingParameters(), { emitEvent: false, onlySelf: false }); |
|||
} else { |
|||
if (this.marker) { |
|||
this.marker.removeFrom(this.map); |
|||
this.marker = null; |
|||
} |
|||
|
|||
this.map.fitWorld(); |
|||
|
|||
this.geolocationForm.reset(undefined, { emitEvent: false, onlySelf: false }); |
|||
} |
|||
|
|||
if (fireEvent) { |
|||
this.callChange(this.value); |
|||
this.callTouched(); |
|||
} |
|||
} |
|||
|
|||
private updateMarkerGoogle(zoom: boolean, fireEvent: boolean) { |
|||
if (this.value) { |
|||
if (!this.marker) { |
|||
this.marker = new google.maps.Marker({ |
|||
position: { lat: 0, lng: 0 }, |
|||
map: this.map, |
|||
draggable: true |
|||
}); |
|||
|
|||
this.marker.addListener('drag', (event: any) => { |
|||
if (!this.isDisabled) { |
|||
this.value = { |
|||
latitude: event.latLng.lat(), |
|||
longitude: event.latLng.lng(), |
|||
address1: this.value == null ? '' : this.value.address1, |
|||
address2: this.value == null ? '' : this.value.address2, |
|||
city: this.value == null ? '' : this.value.city, |
|||
state: this.value == null ? '' : this.value.state, |
|||
zip: this.value == null ? '' : this.value.zip |
|||
}; |
|||
} |
|||
}); |
|||
this.marker.addListener('dragend', (event: any) => { |
|||
if (!this.isDisabled) { |
|||
this.value = { |
|||
latitude: event.latLng.lat(), |
|||
longitude: event.latLng.lng(), |
|||
address1: this.value == null ? '' : this.value.address1, |
|||
address2: this.value == null ? '' : this.value.address2, |
|||
city: this.value == null ? '' : this.value.city, |
|||
state: this.value == null ? '' : this.value.state, |
|||
zip: this.value == null ? '' : this.value.zip |
|||
}; |
|||
|
|||
this.updateMarker(false, true); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
const latLng = { lat: this.value.latitude, lng: this.value.longitude }; |
|||
|
|||
if (zoom) { |
|||
this.map.setCenter(latLng); |
|||
} else { |
|||
this.map.panTo(latLng); |
|||
} |
|||
|
|||
this.marker.setPosition(latLng); |
|||
this.map.setZoom(16); |
|||
|
|||
this.geolocationForm.setValue(this.fillMissingParameters(), { emitEvent: false, onlySelf: false }); |
|||
} else { |
|||
if (this.marker) { |
|||
this.marker.setMap(null); |
|||
this.marker = null; |
|||
} |
|||
|
|||
this.map.setCenter({ lat: 0, lng: 0 }); |
|||
this.map.setZoom(1); |
|||
|
|||
this.geolocationForm.reset(undefined, { emitEvent: false, onlySelf: false }); |
|||
} |
|||
|
|||
if (fireEvent) { |
|||
this.callChange(this.value); |
|||
this.callTouched(); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue