2015-01-10 12:55:48 +01:00
/ *
* L . Control . Loading is a control that shows a loading indicator when tiles are
* loading or when map - related AJAX requests are taking place .
* /
( function ( ) {
2015-06-18 16:08:17 +02:00
var console = window . console || {
error : function ( ) { } ,
warn : function ( ) { }
} ;
2015-01-10 12:55:48 +01:00
function defineLeafletLoading ( L ) {
L . Control . Loading = L . Control . extend ( {
options : {
2016-10-04 13:47:23 +02:00
delayIndicator : null ,
2015-01-10 12:55:48 +01:00
position : 'topleft' ,
separate : false ,
zoomControl : null ,
spinjs : false ,
spin : {
2016-10-04 13:47:23 +02:00
lines : 7 ,
length : 3 ,
width : 3 ,
radius : 5 ,
rotate : 13 ,
top : "83%"
2015-01-10 12:55:48 +01:00
}
} ,
initialize : function ( options ) {
L . setOptions ( this , options ) ;
this . _dataLoaders = { } ;
// Try to set the zoom control this control is attached to from the
// options
if ( this . options . zoomControl !== null ) {
this . zoomControl = this . options . zoomControl ;
}
} ,
onAdd : function ( map ) {
if ( this . options . spinjs && ( typeof Spinner !== 'function' ) ) {
return console . error ( "Leaflet.loading cannot load because you didn't load spin.js (http://fgnass.github.io/spin.js/), even though you set it in options." ) ;
}
this . _addLayerListeners ( map ) ;
this . _addMapListeners ( map ) ;
// Try to set the zoom control this control is attached to from the map
// the control is being added to
if ( ! this . options . separate && ! this . zoomControl ) {
if ( map . zoomControl ) {
this . zoomControl = map . zoomControl ;
} else if ( map . zoomsliderControl ) {
this . zoomControl = map . zoomsliderControl ;
}
}
// Create the loading indicator
var classes = 'leaflet-control-loading' ;
var container ;
if ( this . zoomControl && ! this . options . separate ) {
// If there is a zoom control, hook into the bottom of it
container = this . zoomControl . _container ;
// These classes are no longer used as of Leaflet 0.6
classes += ' leaflet-bar-part-bottom leaflet-bar-part last' ;
2015-01-23 15:59:27 +01:00
// Loading control will be added to the zoom control. So the visible last element is not the
// last dom element anymore. So add the part-bottom class.
L . DomUtil . addClass ( this . _getLastControlButton ( ) , 'leaflet-bar-part-bottom' ) ;
2015-01-10 12:55:48 +01:00
}
else {
// Otherwise, create a container for the indicator
2017-01-27 12:06:34 +01:00
container = L . DomUtil . create ( 'div' , 'leaflet-control-zoom leaflet-control-layer-container leaflet-bar' ) ;
2015-01-10 12:55:48 +01:00
}
2017-01-27 12:06:34 +01:00
this . _indicatorContainer = container ;
2015-01-10 12:55:48 +01:00
this . _indicator = L . DomUtil . create ( 'a' , classes , container ) ;
if ( this . options . spinjs ) {
2016-10-04 13:47:23 +02:00
this . _spinner = new Spinner ( this . options . spin ) . spin ( ) ;
this . _indicator . appendChild ( this . _spinner . el ) ;
2015-01-10 12:55:48 +01:00
}
return container ;
} ,
onRemove : function ( map ) {
this . _removeLayerListeners ( map ) ;
this . _removeMapListeners ( map ) ;
} ,
removeFrom : function ( map ) {
if ( this . zoomControl && ! this . options . separate ) {
// Override Control.removeFrom() to avoid clobbering the entire
// _container, which is the same as zoomControl's
this . _container . removeChild ( this . _indicator ) ;
this . _map = null ;
this . onRemove ( map ) ;
return this ;
}
else {
// If this control is separate from the zoomControl, call the
// parent method so we don't leave behind an empty container
return L . Control . prototype . removeFrom . call ( this , map ) ;
}
} ,
addLoader : function ( id ) {
this . _dataLoaders [ id ] = true ;
2016-10-04 13:47:23 +02:00
if ( this . options . delayIndicator && ! this . delayIndicatorTimeout ) {
// If we are delaying showing the indicator and we're not
// already waiting for that delay, set up a timeout.
var that = this ;
this . delayIndicatorTimeout = setTimeout ( function ( ) {
that . updateIndicator ( ) ;
that . delayIndicatorTimeout = null ;
} , this . options . delayIndicator ) ;
}
else {
// Otherwise show the indicator immediately
this . updateIndicator ( ) ;
}
2015-01-10 12:55:48 +01:00
} ,
removeLoader : function ( id ) {
delete this . _dataLoaders [ id ] ;
this . updateIndicator ( ) ;
2016-10-04 13:47:23 +02:00
// If removing this loader means we're in no danger of loading,
// clear the timeout. This prevents old delays from instantly
// triggering the indicator.
if ( this . options . delayIndicator && this . delayIndicatorTimeout && ! this . isLoading ( ) ) {
clearTimeout ( this . delayIndicatorTimeout ) ;
this . delayIndicatorTimeout = null ;
}
2015-01-10 12:55:48 +01:00
} ,
updateIndicator : function ( ) {
if ( this . isLoading ( ) ) {
this . _showIndicator ( ) ;
}
else {
this . _hideIndicator ( ) ;
}
} ,
isLoading : function ( ) {
return this . _countLoaders ( ) > 0 ;
} ,
_countLoaders : function ( ) {
var size = 0 , key ;
for ( key in this . _dataLoaders ) {
if ( this . _dataLoaders . hasOwnProperty ( key ) ) size ++ ;
}
return size ;
} ,
_showIndicator : function ( ) {
// Show loading indicator
L . DomUtil . addClass ( this . _indicator , 'is-loading' ) ;
2017-01-27 12:06:34 +01:00
L . DomUtil . addClass ( this . _indicatorContainer , 'is-loading' ) ;
2015-01-10 12:55:48 +01:00
// If zoomControl exists, make the zoom-out button not last
if ( ! this . options . separate ) {
if ( this . zoomControl instanceof L . Control . Zoom ) {
2015-01-23 15:59:27 +01:00
L . DomUtil . removeClass ( this . _getLastControlButton ( ) , 'leaflet-bar-part-bottom' ) ;
2015-01-10 12:55:48 +01:00
}
else if ( typeof L . Control . Zoomslider === 'function' && this . zoomControl instanceof L . Control . Zoomslider ) {
L . DomUtil . removeClass ( this . zoomControl . _ui . zoomOut , 'leaflet-bar-part-bottom' ) ;
}
}
} ,
_hideIndicator : function ( ) {
// Hide loading indicator
L . DomUtil . removeClass ( this . _indicator , 'is-loading' ) ;
2017-01-27 12:06:34 +01:00
L . DomUtil . removeClass ( this . _indicatorContainer , 'is-loading' ) ;
2015-01-10 12:55:48 +01:00
// If zoomControl exists, make the zoom-out button last
if ( ! this . options . separate ) {
if ( this . zoomControl instanceof L . Control . Zoom ) {
2015-01-23 15:59:27 +01:00
L . DomUtil . addClass ( this . _getLastControlButton ( ) , 'leaflet-bar-part-bottom' ) ;
2015-01-10 12:55:48 +01:00
}
else if ( typeof L . Control . Zoomslider === 'function' && this . zoomControl instanceof L . Control . Zoomslider ) {
L . DomUtil . addClass ( this . zoomControl . _ui . zoomOut , 'leaflet-bar-part-bottom' ) ;
}
}
} ,
2015-01-23 15:59:27 +01:00
_getLastControlButton : function ( ) {
var container = this . zoomControl . _container ,
index = container . children . length - 1 ;
// Find the last visible control button that is not our loading
// indicator
while ( index > 0 ) {
var button = container . children [ index ] ;
if ( ! ( this . _indicator === button || button . offsetWidth === 0 || button . offsetHeight === 0 ) ) {
break ;
}
index -- ;
}
return container . children [ index ] ;
} ,
2015-01-10 12:55:48 +01:00
_handleLoading : function ( e ) {
this . addLoader ( this . getEventId ( e ) ) ;
} ,
2016-10-04 13:47:23 +02:00
_handleBaseLayerChange : function ( e ) {
var that = this ;
// Check for a target 'layer' that contains multiple layers, such as
// L.LayerGroup. This will happen if you have an L.LayerGroup in an
// L.Control.Layers.
if ( e . layer && e . layer . eachLayer && typeof e . layer . eachLayer === 'function' ) {
e . layer . eachLayer ( function ( layer ) {
that . _handleBaseLayerChange ( { layer : layer } ) ;
} ) ;
}
else {
// If we're changing to a canvas layer, don't handle loading
// as canvas layers will not fire load events.
if ( ! ( L . TileLayer . Canvas && e . layer instanceof L . TileLayer . Canvas ) ) {
that . _handleLoading ( e ) ;
}
}
} ,
2015-01-10 12:55:48 +01:00
_handleLoad : function ( e ) {
this . removeLoader ( this . getEventId ( e ) ) ;
} ,
getEventId : function ( e ) {
if ( e . id ) {
return e . id ;
}
else if ( e . layer ) {
return e . layer . _leaflet _id ;
}
return e . target . _leaflet _id ;
} ,
_layerAdd : function ( e ) {
if ( ! e . layer || ! e . layer . on ) return
try {
e . layer . on ( {
loading : this . _handleLoading ,
load : this . _handleLoad
} , this ) ;
}
catch ( exception ) {
console . warn ( 'L.Control.Loading: Tried and failed to add ' +
' event handlers to layer' , e . layer ) ;
console . warn ( 'L.Control.Loading: Full details' , exception ) ;
}
} ,
2016-10-04 13:47:23 +02:00
_layerRemove : function ( e ) {
if ( ! e . layer || ! e . layer . off ) return ;
try {
e . layer . off ( {
loading : this . _handleLoading ,
load : this . _handleLoad
} , this ) ;
}
catch ( exception ) {
console . warn ( 'L.Control.Loading: Tried and failed to remove ' +
'event handlers from layer' , e . layer ) ;
console . warn ( 'L.Control.Loading: Full details' , exception ) ;
}
} ,
2015-01-10 12:55:48 +01:00
_addLayerListeners : function ( map ) {
// Add listeners for begin and end of load to any layers already on the
// map
map . eachLayer ( function ( layer ) {
if ( ! layer . on ) return ;
layer . on ( {
loading : this . _handleLoading ,
load : this . _handleLoad
} , this ) ;
} , this ) ;
// When a layer is added to the map, add listeners for begin and end
// of load
map . on ( 'layeradd' , this . _layerAdd , this ) ;
2016-10-04 13:47:23 +02:00
map . on ( 'layerremove' , this . _layerRemove , this ) ;
2015-01-10 12:55:48 +01:00
} ,
_removeLayerListeners : function ( map ) {
// Remove listeners for begin and end of load from all layers
map . eachLayer ( function ( layer ) {
if ( ! layer . off ) return ;
layer . off ( {
loading : this . _handleLoading ,
load : this . _handleLoad
} , this ) ;
} , this ) ;
2016-10-04 13:47:23 +02:00
// Remove layeradd/layerremove listener from map
2015-01-10 12:55:48 +01:00
map . off ( 'layeradd' , this . _layerAdd , this ) ;
2016-10-04 13:47:23 +02:00
map . off ( 'layerremove' , this . _layerRemove , this ) ;
2015-01-10 12:55:48 +01:00
} ,
_addMapListeners : function ( map ) {
// Add listeners to the map for (custom) dataloading and dataload
// events, eg, for AJAX calls that affect the map but will not be
// reflected in the above layer events.
map . on ( {
2016-10-04 13:47:23 +02:00
baselayerchange : this . _handleBaseLayerChange ,
2015-01-10 12:55:48 +01:00
dataloading : this . _handleLoading ,
dataload : this . _handleLoad ,
layerremove : this . _handleLoad
} , this ) ;
} ,
_removeMapListeners : function ( map ) {
map . off ( {
2016-10-04 13:47:23 +02:00
baselayerchange : this . _handleBaseLayerChange ,
2015-01-10 12:55:48 +01:00
dataloading : this . _handleLoading ,
dataload : this . _handleLoad ,
layerremove : this . _handleLoad
} , this ) ;
}
} ) ;
L . Map . addInitHook ( function ( ) {
if ( this . options . loadingControl ) {
this . loadingControl = new L . Control . Loading ( ) ;
this . addControl ( this . loadingControl ) ;
}
} ) ;
L . Control . loading = function ( options ) {
return new L . Control . Loading ( options ) ;
} ;
}
if ( typeof define === 'function' && define . amd ) {
// Try to add leaflet.loading to Leaflet using AMD
define ( [ 'leaflet' ] , function ( L ) {
defineLeafletLoading ( L ) ;
} ) ;
}
else {
// Else use the global L
defineLeafletLoading ( L ) ;
}
} ) ( ) ;