/**
 * Copyright 2020 Google LLC. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */ import { abortEvent , omit , stopPropagation } from "./util" ; import { Label } from "./label" ; import { MarkerSafe } from "./marker-safe" ; const CLICK = "click" ; const DBLCLICK = "dblclick" ; const DRAG = "drag" ; const DRAGEND = "dragend" ; const DRAGSTART = "dragstart" ; const MOUSEDOWN = "mousedown" ; const MOUSEMOVE = "mousemove" ; const MOUSEOVER = "mouseover" ; const MOUSEOUT = "mouseout" ; const MOUSEUP = "mouseup" ; export interface MarkerWithLabelOptions extends google . maps . MarkerOptions { labelContent : string | HTMLElement ; labelAnchor ? : google . maps . Point ; labelZIndexOffset ? : number ; labelClass ? : string ; } export class MarkerWithLabel extends MarkerSafe { private label : Label ; private propertyListeners : google . maps . MapsEventListener [ ] ; private interactiveListeners : google . maps . MapsEventListener [ ] ; private isTouchScreen = false ; private isDraggingLabel = false ; private isMouseDownOnLabel = false ; private shouldIgnoreClick = false ; private eventOffset : google . maps . Point | null ; private mouseOutTimeout : ReturnType < typeof setTimeout > ; constructor ( options ) { super ( omit ( options , [ "labelAnchor" , "labelZIndexOffset" , "labelClass" , "labelContent" , ] ) ) ; this . label = new Label ( { ... { } , ... options } ) ; this . propertyListeners = [ google . maps . event . addListener ( this , "clickable_changed" , this . handleClickableOrDraggableChange ) , google . maps . event . addListener ( this , "cursor_changed" , ( ) => { this . label . cursor = this . getCursor ( ) ; } ) , google . maps . event . addListener ( this , "draggable_changed" , this . handleClickableOrDraggableChange ) , google . maps . event . addListener ( this , "position_changed" , ( ) => { this . label . position = this . getPosition ( ) ; } ) , google . maps . event . addListener ( this , "opacity_changed" , ( ) => { this . label . opacity = this . getOpacity ( ) ; } ) , google . maps . event . addListener ( this , "title_changed" , ( ) => { this . label . title = this . getTitle ( ) ; } ) , google . maps . event . addListener ( this , "visible_changed" , ( ) => { this . label . visible = this . getVisible ( ) ; } ) , google . maps . event . addListener ( this , "zindex_changed" , ( ) => { this . label . zIndex = this . getZIndex ( ) ; this . label . draw ( ) ; } ) , ] ; } public get isInteractive ( ) : boolean { return this . getClickable ( ) || this . getDraggable ( ) ; } /**
     * Gets label element.
     */ public get labelElement ( ) : HTMLElement { return this . label . element ; } /**
     * Gets label `innerHTML`. See  Marker . labelElement for
     * accessing the HTMLElement instead.
      public get labelContent ( ) : string { return this . label . content ; } public set labelContent ( content ) { this . label . content = content ; } public get labelClass ( ) : string { return this . label . className ; } public set labelClass ( className ) { this . label . className = className ; } public setMap ( map ) { super . setMap ( map ) ; setTimeout ( ( ) => { this . label . setMap ( map ) ; this . removeInteractiveListeners ( ) ; if ( map ) { this . addInteractiveListeners ( ) ; } } ) ; } private handleClickableOrDraggableChange ( ) { this . label . clickable = this . getClickable ( ) ; this . label . draggable = this . getDraggable ( ) ; if ( this . isInteractive ) { this . addInteractiveListeners ( ) ; } else { this . removeInteractiveListeners ( ) ; } } private removeInteractiveListeners ( ) { if ( this . interactiveListeners ) { this . interactiveListeners . forEach ( ( l ) => google . maps . event . removeListener ( l ) ) ; this . interactiveListeners = null ; } } private addInteractiveListeners ( ) { if ( ! this . interactiveListeners ) { if ( ! this . getMap ( ) ) { return ; } this . interactiveListeners = [ this . label . addDomListener ( MOUSEOVER , ( e ) => { if ( ! this . isTouchScreen ) { google . maps . event . trigger ( this , MOUSEOVER , { latLng : this . getPosition ( ) , } ) ; abortEvent ( e ) ; } } ) , this . label . addDomListener ( MOUSEOUT , ( e ) => { if ( ! this . isTouchScreen ) { if ( this . mouseOutTimeout ) { clearTimeout ( this . mouseOutTimeout ) ; } if ( this . isMouseDownOnLabel ) { this . mouseOutTimeout = setTimeout ( ( ) => { if ( this . isMouseDownOnLabel ) { this . isMouseDownOnLabel = false ; google . maps . event . trigger ( this , MOUSEUP , { latLng : this . getPosition ( ) , } ) ; if ( this . isDraggingLabel ) { this . isDraggingLabel = false ; this . shouldIgnoreClick = true ; google . maps . event . trigger ( this , DRAGEND , { latLng : this . getPosition ( ) , } ) ; } } google . maps . event . trigger ( this , MOUSEOUT , { latLng : this . getPosition ( ) , } ) ; } , 200 ) ; } else { google . maps . event . trigger ( this , MOUSEOUT , { latLng : this . getPosition ( ) , } ) ; } abortEvent ( e ) ; } } ) , this . label . addDomListener ( MOUSEDOWN , ( e ) => { this . isDraggingLabel = false ; this . isMouseDownOnLabel = true ; google . maps . event . trigger ( this , MOUSEDOWN , { latLng : this . getPosition ( ) , } ) ; if ( ! this . isTouchScreen ) { abortEvent ( e ) ; } } ) , this . label . addDomListener ( MOUSEUP , ( e ) => { const markerEvent = { latLng : this . getPosition ( ) } ; if ( this . isMouseDownOnLabel ) { this . isMouseDownOnLabel = false ; google . maps . event . trigger ( this , MOUSEUP , markerEvent ) ; if ( this . isDraggingLabel ) { this . isDraggingLabel = false ; this . shouldIgnoreClick = true ; google . maps . event . trigger ( this , DRAGEND , markerEvent ) ; } if ( ! this . getDraggable ( ) ) { abortEvent ( e ) ; } } } ) , this . label . addDomListener ( CLICK , ( e ) => { if ( this . shouldIgnoreClick ) { this . shouldIgnoreClick = false ; } else { google . maps . event . trigger ( this , CLICK , { latLng : this . getPosition ( ) , } ) ; } abortEvent ( e ) ; } ) , this . label . addDomListener ( DBLCLICK , ( e ) => { google . maps . event . trigger ( this , DBLCLICK , { latLng : this . getPosition ( ) , } ) ; abortEvent ( e ) ; } ) , google . maps . event . addListener ( this . getMap ( ) , MOUSEMOVE , ( e ) => { if ( this . isMouseDownOnLabel && this . getDraggable ( ) ) { if ( this . isDraggingLabel ) { const position = new google . maps . LatLng ( e . latLng . lat ( ) - this . eventOffset . y , e . latLng . lng ( ) - this . eventOffset . x ) ; google . maps . event . trigger ( this , DRAG , { ... e , latLng : position , } ) ; } else { this . isDraggingLabel = true ; this . eventOffset = new google . maps . Point ( e . latLng . lng ( ) - this . getPosition ( ) . lng ( ) , e . latLng . lat ( ) - this . getPosition ( ) . lat ( ) ) ; google . maps . event . trigger ( this , DRAGSTART , { ... e , latLng : this . getPosition ( ) , } ) ; } } } ) , google . maps . event . addListener ( this , DRAGSTART , ( ) => { this . label . zIndex = 1000000 ; } ) , google . maps . event . addListener ( this , DRAG , ( { latLng } ) => { this . setPosition ( latLng ) ; } ) , google . maps . event . addListener ( this , DRAGEND , ( ) => { this . label . zIndex = this . getZIndex ( ) ; this . label . draw ( ) ; } ) , this . label . addDomListener ( "touchstart" , ( e ) => { this . isTouchScreen = true ; stopPropagation ( e ) ; } ) , this . label . addDomListener ( "touchmove" , ( e ) => { stopPropagation ( e ) ; } ) , this . label . addDomListener ( "touchend" , ( e ) => { stopPropagation ( e ) ; } ) , ] ; } } }