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