ignore import L = require ( "leaflet" ) ; interface TimelineSliderControlOptions extends L . ControlOptions { /**
     * Minimum time, in ms, for the playback to take. Will almost certainly
     * actually take at least a bit longer; after each frame, the next one
     * displays in `duration/steps` ms, so each frame really takes frame
     * processing time PLUS step time.
     *
     * Default: 10000
     */ duration ? : number ; /**
     * Allow playback to be controlled using the spacebar (play/pause) and
     * right/left arrow keys (next/previous).
     *
     * Default: false
     */ enableKeyboardControls ? : boolean ; /**
     * Show playback controls (i.e.  prev/play/pause/next).
     *
     * Default: true
     */ enablePlayback ? : boolean ; /**
     * Show ticks on the timeline (if the browser supports it)
     *
     * See here for support:
     * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#Browser_compatibility
     *
     * Default: true
     */ showTicks ? : boolean ; /**
     * Wait until the user is finished changing the date to update the map. By
     * default, both the map and the date update for every change. With complex
     * data, this can slow things down, so set this to true to only update the
     * displayed date.
     */ waitToUpdateMap ? : boolean ; /**
     * The start time of the timeline. If unset, this will be calculated
     * automatically based on the timelines registered to this control.
     */ start ? : number ; /**
     * The end time of the timeline. If unset, this will be calculated
     * automatically based on the timelines registered to this control.
     */ end ? : number ; /**
     * How many steps to break the timeline into. Each step will then be
     * `(end-start) / steps`. Only affects playback.
     *
     * Default: 1000
     */ steps ? : number ; /**
     * Start playback of the timeline as soon as the page is loaded.
     *
     * Default: false
     */ autoPlay ? : boolean ; /**
     * A function which takes the current time value (a Unix timestamp) and
     * outputs a string that is displayed beneath the control buttons.
     */ formatOutput ? ( time ) : string ; } ignore type PlaybackControl = "play" | "pause" | "prev" | "next" ; ignore type TSC = L . TimelineSliderControl ; declare module "leaflet" { export class TimelineSliderControl extends L . Control { container : HTMLElement ; options : Required < TimelineSliderControlOptions > ; timelines : L . Timeline [ ] ; start : number ; end : number ; map : L . Map ; time : number ; syncedControl : TSC [ ] ; ignore _datalist ? : HTMLDataListElement ; ignore _output ? : HTMLOutputElement ; ignore _stepDuration : number ; ignore _stepSize : number ; ignore _timeSlider : HTMLInputElement ; ignore _playing : boolean ; ignore _timer : number ; ignore _listener : ( ev : KeyboardEvent ) => any ; ignore initialize ( this , options ) ; ignore _getTimes ( this ) ; ignore _nearestEventTime ( this , findTime , mode ? ) ; ignore _recalculate ( this ) ; ignore _createDOM ( this ) ; ignore _addKeyListeners ( this ) ; ignore _removeKeyListeners ( this ) ; ignore _buildDataList ( this , container ) ; ignore _rebuildDataList ( this ) ; ignore _makeButton ( this , container , name ) ; ignore _makeButtons ( this , container ) ; ignore _makeOutput ( this , container ) ; ignore _makeSlider ( this , container ) ; ignore _onKeydown ( this , ev ) ; ignore _sliderChanged ( this , e ) ; ignore _setTime ( this , time , type ) ; ignore _disableMapDragging ( this ) ; ignore _enableMapDragging ( this ) ; ignore _resetIfTimelinesChanged ( this , oldTimelineCount ) ; ignore _autoPlay ( this ) ; play ( this , fromSynced ? ) ; pause ( this , fromSynced ? ) ; prev ( this ) ; next ( this ) ; toggle ( this ) ; setTime ( this , time ) ; addTimelines ( this , ... timelines ) ; removeTimelines ( this , ... timelines ) ; syncControl ( this , controlToSync ) ; } let timelineSliderControl ; } L . TimelineSliderControl = L . Control . extend ( { initialize ( options = { } ) { const defaultOptions = { duration : 10000 , enableKeyboardControls : false , enablePlayback : true , formatOutput : ( output ) => `${ output || "" }` , showTicks : true , waitToUpdateMap : false , position : "bottomleft" , steps : 1000 , autoPlay : false , } ; this . timelines = [ ] ; L . Util . setOptions ( this , defaultOptions ) ; L . Util . setOptions ( this , options ) ; this . start = options . start || 0 ; this . end = options . end || 0 ; } , private returns _getTimes ( ) { const times = [ ] ; this . timelines . forEach ( ( timeline ) => { const timesInRange = timeline . times . filter ( ( time ) => time >= this . start && time <= this . end ) ; times . push ( ... timesInRange ) ; } ) ; if ( times . length ) { times . sort ( ( a , b ) => a - b ) ; const dedupedTimes = [ times [ 0 ] ] ; times . reduce ( ( a , b ) => { if ( a !== b ) { dedupedTimes . push ( b ) ; } return b ; } ) ; return dedupedTimes ; } return times ; } , private _recalculate ( ) { const manualStart = typeof this . options . start !== "undefined" ; const manualEnd = typeof this . options . end !== "undefined" ; const duration = this . options . duration ; let min = Infinity ; let max = - Infinity ; this . timelines . forEach ( ( timeline ) => { if ( timeline . start < min ) { min = timeline . start ; } if ( timeline . end > max ) { max = timeline . end ; } } ) ; if ( ! manualStart ) { this . start = min ; this . _timeSlider . min = ( min === Infinity ? 0 : min ) . toString ( ) ; this . _timeSlider . value = this . _timeSlider . min ; } if ( ! manualEnd ) { this . end = max ; this . _timeSlider . max = ( max === - Infinity ? 0 : max ) . toString ( ) ; } this . _stepSize = Math . max ( 1 , ( this . end - this . start ) / this . options . steps ) ; this . _stepDuration = Math . max ( 1 , duration / this . options . steps ) ; } , private param findTime param mode returns _nearestEventTime ( findTime , mode = 1 ) { const times = this . _getTimes ( ) ; let retNext = false ; let lastTime = times [ 0 ] ; for ( let i = 1 ; i < times . length ; i ++ ) { const time = times [ i ] ; if ( retNext ) { return time ; } if ( time >= findTime ) { if ( mode === - 1 ) { return lastTime ; } if ( time === findTime ) { retNext = true ; } else { return time ; } } lastTime = time ; } return lastTime ; } , private _createDOM ( ) { const classes = [ "leaflet-control-layers" , "leaflet-control-layers-expanded" , "leaflet-timeline-control" , ] ; const container = L . DomUtil . create ( "div" , classes . join ( " " ) ) ; this . container = container ; if ( this . options . enablePlayback ) { const sliderCtrlC = L . DomUtil . create ( "div" , "sldr-ctrl-container" , container ) ; const buttonContainer = L . DomUtil . create ( "div" , "button-container" , sliderCtrlC ) ; this . _makeButtons ( buttonContainer ) ; if ( this . options . enableKeyboardControls ) { this . _addKeyListeners ( ) ; } this . _makeOutput ( sliderCtrlC ) ; } this . _makeSlider ( container ) ; if ( this . options . showTicks ) { this . _buildDataList ( container ) ; } if ( this . options . autoPlay ) { this . _autoPlay ( ) ; } } , private _addKeyListeners ( ) { this . _listener = ( ev ) => this . _onKeydown ( ev ) ; document . addEventListener ( "keydown" , this . _listener ) ; } , private _removeKeyListeners ( ) { document . removeEventListener ( "keydown" , this . _listener ) ; } , private param container _buildDataList ( container ) { this . _datalist = L . DomUtil . create ( "datalist" , "" , container ) as HTMLDataListElement ; const idNum = Math . floor ( Math . random ( ) * 1000000 ) ; this . _datalist . id = `timeline-datalist-${ idNum }` ; this . _timeSlider . setAttribute ( "list" , this . _datalist . id ) ; this . _rebuildDataList ( ) ; } , /**
     * Reconstructs the <datalist>. Should be called when new data comes in.
     */ _rebuildDataList ( ) { const datalist = this . _datalist ; if ( ! datalist ) return ; while ( datalist . firstChild ) { datalist . removeChild ( datalist . firstChild ) ; } const datalistSelect = L . DomUtil . create ( "select" , "" , this . _datalist ) ; datalistSelect . setAttribute ( "aria-label" , "List of times" ) ; this . _getTimes ( ) . forEach ( ( time ) => { ( L . DomUtil . create ( "option" , "" , datalistSelect ) as HTMLOptionElement ) . value = time . toString ( ) ; } ) ; } , private param container param name _makeButton ( container , name ) { const button = L . DomUtil . create ( "button" , name , container ) ; button . setAttribute ( "aria-label" , name ) ; button . addEventListener ( "click" , ( ) => this [ name ] ( ) ) ; L . DomEvent . disableClickPropagation ( button ) ; } , private param container _makeButtons ( container ) { this . _makeButton ( container , "prev" ) ; this . _makeButton ( container , "play" ) ; this . _makeButton ( container , "pause" ) ; this . _makeButton ( container , "next" ) ; } , private _disableMapDragging ( ) { this . map . dragging . disable ( ) ; } , private _enableMapDragging ( ) { this . map . dragging . enable ( ) ; } , private param container _makeSlider ( container ) { const slider = L . DomUtil . create ( "input" , "time-slider" , container ) as HTMLInputElement ; slider . setAttribute ( "aria-label" , "Slider" ) ; slider . type = "range" ; slider . min = ( this . start || 0 ) . toString ( ) ; slider . max = ( this . end || 0 ) . toString ( ) ; slider . value = ( this . start || 0 ) . toString ( ) ; this . _timeSlider = slider ; L . DomEvent . on ( this . _timeSlider , "mousedown mouseup click touchstart" , L . DomEvent . stopPropagation ) ; L . DomEvent . on ( this . _timeSlider , "change input" , this . _sliderChanged , this ) ; L . DomEvent . on ( this . _timeSlider , "mouseenter" , this . _disableMapDragging , this ) ; L . DomEvent . on ( this . _timeSlider , "mouseleave" , this . _enableMapDragging , this ) ; } , _makeOutput ( container ) { this . _output = L . DomUtil . create ( "output" , "time-text" , container ) as HTMLOutputElement ; this . _output . innerHTML = this . options . formatOutput ( this . start ) ; } , _onKeydown ( e ) { let target = ( e . target || e . srcElement ) as HTMLElement ; if ( ! /INPUT|TEXTAREA/ . test ( target . tagName ) ) { switch ( e . keyCode || e . which ) { case 37 : this . prev ( ) ; break ; case 39 : this . next ( ) ; break ; case 32 : this . toggle ( ) ; break ; default : return ; } e . preventDefault ( ) ; } } , _sliderChanged ( e ) { const { target } = e ; const time = parseFloat ( target instanceof HTMLInputElement ? target . value : "0" ) ; this . _setTime ( time , e . type ) ; } , _setTime ( time , type ) { this . time = time ; if ( ! this . options . waitToUpdateMap || type === "change" ) { this . timelines . forEach ( ( timeline ) => timeline . setTime ( time ) ) ; } if ( this . _output ) { this . _output . innerHTML = this . options . formatOutput ( time ) ; } } , _resetIfTimelinesChanged ( oldTimelineCount ) { if ( this . timelines . length !== oldTimelineCount ) { this . _recalculate ( ) ; if ( this . options . showTicks ) { this . _rebuildDataList ( ) ; } this . setTime ( this . start ) ; } } , _autoPlay ( ) { if ( document . readyState === "loading" ) { window . addEventListener ( "load" , ( ) => this . _autoPlay ( ) ) ; } else { this . play ( ) ; } } , param timelines addTimelines ( ... timelines ) { this . pause ( ) ; const timelineCount = this . timelines . length ; timelines . forEach ( ( timeline ) => { if ( this . timelines . indexOf ( timeline ) === - 1 ) { this . timelines . push ( timeline ) ; } } ) ; this . _resetIfTimelinesChanged ( timelineCount ) ; } , param timelines removeTimelines ( ... timelines ) { this . pause ( ) ; const timelineCount = this . timelines . length ; timelines . forEach ( ( timeline ) => { const index = this . timelines . indexOf ( timeline ) ; if ( index !== - 1 ) { this . timelines . splice ( index , 1 ) ; } } ) ; this . _resetIfTimelinesChanged ( timelineCount ) ; } , /**
     * Toggles play/pause state.
     */ toggle ( ) { if ( this . _playing ) { this . pause ( ) ; } else { this . play ( ) ; } } , /**
     * Pauses playback and goes to the previous event.
     */ prev ( ) { this . pause ( ) ; const prevTime = this . _nearestEventTime ( this . time , - 1 ) ; this . _timeSlider . value = prevTime . toString ( ) ; this . setTime ( prevTime ) ; } , /**
     * Pauses playback.
     */ pause ( fromSynced ) { window . clearTimeout ( this . _timer ) ; this . _playing = false ; this . container ?. classList . remove ( "playing" ) ; if ( this . syncedControl && ! fromSynced ) { this . syncedControl . map ( function ( control ) { control . pause ( true ) ; } ) ; } } , /**
     * Starts playback.
     */ play ( fromSynced ) { window . clearTimeout ( this . _timer ) ; if ( parseFloat ( this . _timeSlider . value ) === this . end ) { this . _timeSlider . value = this . start . toString ( ) ; } this . _timeSlider . value = ( parseFloat ( this . _timeSlider . value ) + this . _stepSize ) . toString ( ) ; this . setTime ( + this . _timeSlider . value ) ; if ( parseFloat ( this . _timeSlider . value ) === this . end ) { this . _playing = false ; this . container ?. classList . remove ( "playing" ) ; } else { this . _playing = true ; this . container ?. classList . add ( "playing" ) ; this . _timer = window . setTimeout ( ( ) => this . play ( true ) , this . _stepDuration ) ; } if ( this . syncedControl && ! fromSynced ) { this . syncedControl . map ( function ( control ) { control . play ( true ) ; } ) ; } } , /**
     * Pauses playback and goes to the next event.
     */ next ( ) { this . pause ( ) ; const nextTime = this . _nearestEventTime ( this . time , 1 ) ; this . _timeSlider . value = nextTime . toString ( ) ; this . setTime ( nextTime ) ; } , param time setTime ( time ) { if ( this . _timeSlider ) this . _timeSlider . value = time . toString ( ) ; this . _setTime ( time , "change" ) ; } , onAdd ( map ) { this . map = map ; this . _createDOM ( ) ; this . setTime ( this . start ) ; return this . container ; } , onRemove ( ) { if ( this . options . enableKeyboardControls ) { this . _removeKeyListeners ( ) ; } L . DomEvent . off ( this . _timeSlider , "change input" , this . _sliderChanged , this ) ; L . DomEvent . off ( this . _timeSlider , "pointerdown mousedown touchstart" , this . _disableMapDragging , this ) ; L . DomEvent . off ( document . body , "pointerup mouseup touchend" , this . _enableMapDragging , this ) ; this . _enableMapDragging ( ) ; } , syncControl ( controlToSync ) { if ( ! this . syncedControl ) { this . syncedControl = [ ] ; } this . syncedControl . push ( controlToSync ) ; } , } ) ; L . timelineSliderControl = ( options ? ) => new L . TimelineSliderControl ( options ) ;