Extract libraries to an separate extension.

This commit is contained in:
David Molineus
2015-01-10 12:55:48 +01:00
commit 02ac641e5f
30 changed files with 12132 additions and 0 deletions

View File

@@ -0,0 +1,140 @@
.leaflet-control-geocoder {
background: white;
box-shadow: 0 1px 7px rgba(0,0,0,0.65);
-webkit-border-radius: 4px;
border-radius: 4px;
line-height: 26px;
overflow: hidden;
}
.leaflet-touch .leaflet-control-geocoder {
box-shadow: none;
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
line-height: 30px;
}
.leaflet-control-geocoder-form {
display: inline;
}
.leaflet-control-geocoder-form input, .leaflet-control-geocoder-form ul, .leaflet-control-geocoder-error {
border: 0;
color: transparent;
background: white;
}
.leaflet-control-geocoder-form input {
font-size: 16px;
width: 0;
transition: width 0.125s ease-in;
}
.leaflet-touch .leaflet-control-geocoder-form input {
font-size: 22px;
}
.leaflet-control-geocoder-icon {
width: 26px;
height: 26px;
background-image: url('images/geocoder.png');
background-repeat: no-repeat;
background-position: center;
float: right;
cursor: pointer;
}
.leaflet-touch .leaflet-control-geocoder-icon {
margin-top: 2px;
width: 30px;
}
.leaflet-control-geocoder-throbber .leaflet-control-geocoder-icon {
background-image: url('images/throbber.gif');
}
.leaflet-control-geocoder-expanded input, .leaflet-control-geocoder-error {
width: 226px;
margin: 0 0 0 4px;
padding: 0 0 0 4px;
vertical-align: middle;
color: #000;
}
.leaflet-control-geocoder-form input:focus {
outline: none;
}
.leaflet-control-geocoder-form button {
display: none;
}
.leaflet-control-geocoder-form-no-error {
display: none;
}
.leaflet-control-geocoder-error {
margin-top: 8px;
display: block;
color: #444;
}
ul.leaflet-control-geocoder-alternatives {
width: 260px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
list-style: none;
padding: 0;
transition: height 0.125s ease-in;
}
.leaflet-control-geocoder-alternatives-minimized {
width: 0 !important;
height: 0;
overflow: hidden;
margin: 0;
padding: 0;
}
.leaflet-control-geocoder-alternatives li {
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
border-bottom: 1px solid #eee;
padding: 0;
}
.leaflet-control-geocoder-alternatives li:last-child {
border-bottom: none;
}
.leaflet-control-geocoder-alternatives a {
display: block;
text-decoration: none;
color: black;
padding: 6px 8px 16px 6px;
font-size: 14px;
line-height: 1;
font-weight: bold;
}
.leaflet-touch .leaflet-control-geocoder-alternatives a {
font-size: 18px;
}
.leaflet-control-geocoder-alternatives a:hover, .leaflet-control-geocoder-selected {
background-color: #ddd;
}
.leaflet-control-geocoder-address-detail {
font-size: 12px;
font-weight: normal;
}
.leaflet-control-geocoder-address-context {
color: #666;
font-size: 12px;
font-weight: lighter;
}

View File

@@ -0,0 +1,735 @@
(function (factory) {
// Packaging/modules magic dance
var L;
if (typeof define === 'function' && define.amd) {
// AMD
define(['leaflet'], factory);
} else if (typeof module !== 'undefined') {
// Node/CommonJS
L = require('leaflet');
module.exports = factory(L);
} else {
// Browser globals
if (typeof window.L === 'undefined')
throw 'Leaflet must be loaded first';
factory(window.L);
}
}(function (L) {
'use strict';
L.Control.Geocoder = L.Control.extend({
options: {
showResultIcons: false,
collapsed: true,
expand: 'click',
position: 'topright',
placeholder: 'Search...',
errorMessage: 'Nothing found.'
},
_callbackId: 0,
initialize: function (options) {
L.Util.setOptions(this, options);
if (!this.options.geocoder) {
this.options.geocoder = new L.Control.Geocoder.Nominatim();
}
},
onAdd: function (map) {
var className = 'leaflet-control-geocoder',
container = L.DomUtil.create('div', className),
icon = L.DomUtil.create('div', 'leaflet-control-geocoder-icon', container),
form = this._form = L.DomUtil.create('form', className + '-form', container),
input;
this._map = map;
this._container = container;
input = this._input = L.DomUtil.create('input');
input.type = 'text';
input.placeholder = this.options.placeholder;
L.DomEvent.addListener(input, 'keydown', this._keydown, this);
//L.DomEvent.addListener(input, 'onpaste', this._clearResults, this);
//L.DomEvent.addListener(input, 'oninput', this._clearResults, this);
this._errorElement = document.createElement('div');
this._errorElement.className = className + '-form-no-error';
this._errorElement.innerHTML = this.options.errorMessage;
this._alts = L.DomUtil.create('ul', className + '-alternatives leaflet-control-geocoder-alternatives-minimized');
form.appendChild(input);
form.appendChild(this._errorElement);
container.appendChild(this._alts);
L.DomEvent.addListener(form, 'submit', this._geocode, this);
if (this.options.collapsed) {
if (this.options.expand === 'click') {
L.DomEvent.addListener(icon, 'click', function(e) {
// TODO: touch
if (e.button === 0 && e.detail !== 2) {
this._toggle();
}
}, this);
} else {
L.DomEvent.addListener(icon, 'mouseover', this._expand, this);
L.DomEvent.addListener(icon, 'mouseout', this._collapse, this);
this._map.on('movestart', this._collapse, this);
}
} else {
this._expand();
}
L.DomEvent.disableClickPropagation(container);
return container;
},
_geocodeResult: function (results) {
L.DomUtil.removeClass(this._container, 'leaflet-control-geocoder-throbber');
if (results.length === 1) {
this._geocodeResultSelected(results[0]);
} else if (results.length > 0) {
this._alts.innerHTML = '';
this._results = results;
L.DomUtil.removeClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
for (var i = 0; i < results.length; i++) {
this._alts.appendChild(this._createAlt(results[i], i));
}
} else {
L.DomUtil.addClass(this._errorElement, 'leaflet-control-geocoder-error');
}
},
markGeocode: function(result) {
this._map.fitBounds(result.bbox);
if (this._geocodeMarker) {
this._map.removeLayer(this._geocodeMarker);
}
this._geocodeMarker = new L.Marker(result.center)
.bindPopup(result.html || result.name)
.addTo(this._map)
.openPopup();
return this;
},
_geocode: function(event) {
L.DomEvent.preventDefault(event);
L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-throbber');
this._clearResults();
this.options.geocoder.geocode(this._input.value, this._geocodeResult, this);
return false;
},
_geocodeResultSelected: function(result) {
if (this.options.collapsed) {
this._collapse();
} else {
this._clearResults();
}
this.markGeocode(result);
},
_toggle: function() {
if (this._container.className.indexOf('leaflet-control-geocoder-expanded') >= 0) {
this._collapse();
} else {
this._expand();
}
},
_expand: function () {
L.DomUtil.addClass(this._container, 'leaflet-control-geocoder-expanded');
this._input.select();
},
_collapse: function () {
this._container.className = this._container.className.replace(' leaflet-control-geocoder-expanded', '');
L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
},
_clearResults: function () {
L.DomUtil.addClass(this._alts, 'leaflet-control-geocoder-alternatives-minimized');
this._selection = null;
L.DomUtil.removeClass(this._errorElement, 'leaflet-control-geocoder-error');
},
_createAlt: function(result, index) {
var li = document.createElement('li'),
a = L.DomUtil.create('a', '', li),
icon = this.options.showResultIcons && result.icon ? L.DomUtil.create('img', '', a) : null,
text = result.html ? undefined : document.createTextNode(result.name);
if (icon) {
icon.src = result.icon;
}
a.href = '#';
a.setAttribute('data-result-index', index);
if (result.html) {
a.innerHTML = result.html;
} else {
a.appendChild(text);
}
L.DomEvent.addListener(li, 'click', function clickHandler(e) {
L.DomEvent.preventDefault(e);
this._geocodeResultSelected(result);
}, this);
return li;
},
_keydown: function(e) {
var _this = this,
select = function select(dir) {
if (_this._selection) {
L.DomUtil.removeClass(_this._selection.firstChild, 'leaflet-control-geocoder-selected');
_this._selection = _this._selection[dir > 0 ? 'nextSibling' : 'previousSibling'];
}
if (!_this._selection) {
_this._selection = _this._alts[dir > 0 ? 'firstChild' : 'lastChild'];
}
if (_this._selection) {
L.DomUtil.addClass(_this._selection.firstChild, 'leaflet-control-geocoder-selected');
}
};
switch (e.keyCode) {
// Escape
case 27:
if (this.options.collapsed) {
this._collapse();
}
break;
// Up
case 38:
select(-1);
L.DomEvent.preventDefault(e);
break;
// Up
case 40:
select(1);
L.DomEvent.preventDefault(e);
break;
// Enter
case 13:
if (this._selection) {
var index = parseInt(this._selection.firstChild.getAttribute('data-result-index'), 10);
this._geocodeResultSelected(this._results[index]);
this._clearResults();
L.DomEvent.preventDefault(e);
}
}
return true;
}
});
L.Control.geocoder = function(id, options) {
return new L.Control.Geocoder(id, options);
};
L.Control.Geocoder.callbackId = 0;
L.Control.Geocoder.jsonp = function(url, params, callback, context, jsonpParam) {
var callbackId = '_l_geocoder_' + (L.Control.Geocoder.callbackId++);
params[jsonpParam || 'callback'] = callbackId;
window[callbackId] = L.Util.bind(callback, context);
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = url + L.Util.getParamString(params);
script.id = callbackId;
document.getElementsByTagName('head')[0].appendChild(script);
};
L.Control.Geocoder.getJSON = function(url, params, callback) {
var xmlHttp = new XMLHttpRequest();
xmlHttp.open( "GET", url + L.Util.getParamString(params), true);
xmlHttp.send(null);
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState != 4) return;
if (xmlHttp.status != 200 && req.status != 304) return;
callback(JSON.parse(xmlHttp.response));
};
};
L.Control.Geocoder.template = function (str, data, htmlEscape) {
return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) {
var value = data[key];
if (value === undefined) {
value = '';
} else if (typeof value === 'function') {
value = value(data);
}
return L.Control.Geocoder.htmlEscape(value);
});
};
// Adapted from handlebars.js
// https://github.com/wycats/handlebars.js/
L.Control.Geocoder.htmlEscape = (function() {
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escape = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
'\'': '&#x27;',
'`': '&#x60;'
};
function escapeChar(chr) {
return escape[chr];
}
return function(string) {
if (string == null) {
return '';
} else if (!string) {
return string + '';
}
// Force a string conversion as this will be done by the append regardless and
// the regex test will do this transparently behind the scenes, causing issues if
// an object's to string has escaped characters in it.
string = '' + string;
if (!possible.test(string)) {
return string;
}
return string.replace(badChars, escapeChar);
};
})();
L.Control.Geocoder.Nominatim = L.Class.extend({
options: {
serviceUrl: '//nominatim.openstreetmap.org/',
geocodingQueryParams: {},
reverseQueryParams: {},
htmlTemplate: function(r) {
var a = r.address,
parts = [];
if (a.road || a.building) {
parts.push('{building} {road} {house_number}');
}
if (a.city || a.town || a.village) {
parts.push('<span class="' + (parts.length > 0 ? 'leaflet-control-geocoder-address-detail' : '') +
'">{postcode} {city}{town}{village}</span>');
}
if (a.state || a.country) {
parts.push('<span class="' + (parts.length > 0 ? 'leaflet-control-geocoder-address-context' : '') +
'">{state} {country}</span>');
}
return L.Control.Geocoder.template(parts.join('<br/>'), a, true);
}
},
initialize: function(options) {
L.Util.setOptions(this, options);
},
geocode: function(query, cb, context) {
L.Control.Geocoder.jsonp(this.options.serviceUrl + 'search/', L.extend({
q: query,
limit: 5,
format: 'json',
addressdetails: 1
}, this.options.geocodingQueryParams),
function(data) {
var results = [];
for (var i = data.length - 1; i >= 0; i--) {
var bbox = data[i].boundingbox;
for (var j = 0; j < 4; j++) bbox[j] = parseFloat(bbox[j]);
results[i] = {
icon: data[i].icon,
name: data[i].display_name,
html: this.options.htmlTemplate ?
this.options.htmlTemplate(data[i])
: undefined,
bbox: L.latLngBounds([bbox[0], bbox[2]], [bbox[1], bbox[3]]),
center: L.latLng(data[i].lat, data[i].lon),
properties: data[i]
};
}
cb.call(context, results);
}, this, 'json_callback');
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.jsonp(this.options.serviceUrl + 'reverse/', L.extend({
lat: location.lat,
lon: location.lng,
zoom: Math.round(Math.log(scale / 256) / Math.log(2)),
addressdetails: 1,
format: 'json'
}, this.options.reverseQueryParams), function(data) {
var result = [],
loc;
if (data && data.lat && data.lon) {
loc = L.latLng(data.lat, data.lon);
result.push({
name: data.display_name,
html: this.options.htmlTemplate ?
this.options.htmlTemplate(data)
: undefined,
center: loc,
bounds: L.latLngBounds(loc, loc),
properties: data
});
}
cb.call(context, result);
}, this, 'json_callback');
}
});
L.Control.Geocoder.nominatim = function(options) {
return new L.Control.Geocoder.Nominatim(options);
};
L.Control.Geocoder.Bing = L.Class.extend({
initialize: function(key) {
this.key = key;
},
geocode : function (query, cb, context) {
L.Control.Geocoder.jsonp('//dev.virtualearth.net/REST/v1/Locations', {
query: query,
key : this.key
}, function(data) {
var results = [];
for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
var resource = data.resourceSets[0].resources[i],
bbox = resource.bbox;
results[i] = {
name: resource.name,
bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
center: L.latLng(resource.point.coordinates)
};
}
cb.call(context, results);
}, this, 'jsonp');
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.jsonp('//dev.virtualearth.net/REST/v1/Locations/' + location.lat + ',' + location.lng, {
key : this.key
}, function(data) {
var results = [];
for (var i = data.resourceSets[0].resources.length - 1; i >= 0; i--) {
var resource = data.resourceSets[0].resources[i],
bbox = resource.bbox;
results[i] = {
name: resource.name,
bbox: L.latLngBounds([bbox[0], bbox[1]], [bbox[2], bbox[3]]),
center: L.latLng(resource.point.coordinates)
};
}
cb.call(context, results);
}, this, 'jsonp');
}
});
L.Control.Geocoder.bing = function(key) {
return new L.Control.Geocoder.Bing(key);
};
L.Control.Geocoder.RaveGeo = L.Class.extend({
options: {
querySuffix: '',
deepSearch: true,
wordBased: false
},
jsonp: function(params, callback, context) {
var callbackId = '_l_geocoder_' + (L.Control.Geocoder.callbackId++),
paramParts = [];
params.prepend = callbackId + '(';
params.append = ')';
for (var p in params) {
paramParts.push(p + '=' + escape(params[p]));
}
window[callbackId] = L.Util.bind(callback, context);
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = this._serviceUrl + '?' + paramParts.join('&');
script.id = callbackId;
document.getElementsByTagName('head')[0].appendChild(script);
},
initialize: function(serviceUrl, scheme, options) {
L.Util.setOptions(this, options);
this._serviceUrl = serviceUrl;
this._scheme = scheme;
},
geocode: function(query, cb, context) {
L.Control.Geocoder.jsonp(this._serviceUrl, {
address: query + this.options.querySuffix,
scheme: this._scheme,
outputFormat: 'jsonp',
deepSearch: this.options.deepSearch,
wordBased: this.options.wordBased
}, function(data) {
var results = [];
for (var i = data.length - 1; i >= 0; i--) {
var r = data[i],
c = L.latLng(r.y, r.x);
results[i] = {
name: r.address,
bbox: L.latLngBounds([c]),
center: c
};
}
cb.call(context, results);
}, this);
}
});
L.Control.Geocoder.raveGeo = function(serviceUrl, scheme, options) {
return new L.Control.Geocoder.RaveGeo(serviceUrl, scheme, options);
};
L.Control.Geocoder.MapQuest = L.Class.extend({
initialize: function(key) {
// MapQuest seems to provide URI encoded API keys,
// so to avoid encoding them twice, we decode them here
this._key = decodeURIComponent(key);
},
_formatName: function() {
var r = [],
i;
for (i = 0; i < arguments.length; i++) {
if (arguments[i]) {
r.push(arguments[i]);
}
}
return r.join(', ');
},
geocode: function(query, cb, context) {
L.Control.Geocoder.jsonp('//www.mapquestapi.com/geocoding/v1/address', {
key: this._key,
location: query,
limit: 5,
outFormat: 'json'
}, function(data) {
var results = [],
loc,
latLng;
if (data.results && data.results[0].locations) {
for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
loc = data.results[0].locations[i];
latLng = L.latLng(loc.latLng);
results[i] = {
name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
bbox: L.latLngBounds(latLng, latLng),
center: latLng
};
}
}
cb.call(context, results);
}, this);
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.jsonp('//www.mapquestapi.com/geocoding/v1/reverse', {
key: this._key,
location: location.lat + ',' + location.lng,
outputFormat: 'json'
}, function(data) {
var results = [],
loc,
latLng;
if (data.results && data.results[0].locations) {
for (var i = data.results[0].locations.length - 1; i >= 0; i--) {
loc = data.results[0].locations[i];
latLng = L.latLng(loc.latLng);
results[i] = {
name: this._formatName(loc.street, loc.adminArea4, loc.adminArea3, loc.adminArea1),
bbox: L.latLngBounds(latLng, latLng),
center: latLng
};
}
}
cb.call(context, results);
}, this);
}
});
L.Control.Geocoder.mapQuest = function(key) {
return new L.Control.Geocoder.MapQuest(key);
};
L.Control.Geocoder.Mapbox = L.Class.extend({
options: {
service_url: 'https://api.tiles.mapbox.com/v4/geocode/mapbox.places-v1/'
},
initialize: function(access_token) {
this._access_token = access_token;
},
geocode: function(query, cb, context) {
L.Control.Geocoder.getJSON(this.options.service_url + encodeURIComponent(query) + '.json', {
access_token: this._access_token,
}, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.features && data.features.length) {
for (var i = 0; i <= data.features.length - 1; i++) {
loc = data.features[i];
latLng = L.latLng(loc.center.reverse());
if(loc.hasOwnProperty('bbox'))
{
latLngBounds = L.latLngBounds(L.latLng(loc.bbox.slice(0, 2).reverse()), L.latLng(loc.bbox.slice(2, 4).reverse()));
}
else
{
latLngBounds = L.latLngBounds(latLng, latLng);
}
results[i] = {
name: loc.place_name,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
},
reverse: function(location, scale, cb, context) {
L.Control.Geocoder.getJSON(this.options.service_url + encodeURIComponent(location.lng) + ',' + encodeURIComponent(location.lat) + '.json', {
access_token: this._access_token,
}, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.features && data.features.length) {
for (var i = 0; i <= data.features.length - 1; i++) {
loc = data.features[i];
latLng = L.latLng(loc.center.reverse());
if(loc.hasOwnProperty('bbox'))
{
latLngBounds = L.latLngBounds(L.latLng(loc.bbox.slice(0, 2).reverse()), L.latLng(loc.bbox.slice(2, 4).reverse()));
}
else
{
latLngBounds = L.latLngBounds(latLng, latLng);
}
results[i] = {
name: loc.place_name,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
}
});
L.Control.Geocoder.mapbox = function(access_token) {
return new L.Control.Geocoder.Mapbox(access_token);
};
L.Control.Geocoder.Google = L.Class.extend({
options: {
service_url: 'https://maps.googleapis.com/maps/api/geocode/json'
},
initialize: function(key) {
this._key = key;
},
geocode: function(query, cb, context) {
var params = {
address: query,
};
if(this._key && this._key.length)
{
params['key'] = this._key
}
L.Control.Geocoder.getJSON(this.options.service_url, params, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.results && data.results.length) {
for (var i = 0; i <= data.results.length - 1; i++) {
loc = data.results[i];
latLng = L.latLng(loc.geometry.location);
latLngBounds = L.latLngBounds(L.latLng(loc.geometry.viewport.northeast), L.latLng(loc.geometry.viewport.southwest));
results[i] = {
name: loc.formatted_address,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
},
reverse: function(location, scale, cb, context) {
var params = {
latlng: encodeURIComponent(location.lat) + ',' + encodeURIComponent(location.lng)
};
if(this._key && this._key.length)
{
params['key'] = this._key
}
L.Control.Geocoder.getJSON(this.options.service_url, params, function(data) {
var results = [],
loc,
latLng,
latLngBounds;
if (data.results && data.results.length) {
for (var i = 0; i <= data.results.length - 1; i++) {
loc = data.results[i];
latLng = L.latLng(loc.geometry.location);
latLngBounds = L.latLngBounds(L.latLng(loc.geometry.viewport.northeast), L.latLng(loc.geometry.viewport.southwest));
results[i] = {
name: loc.formatted_address,
bbox: latLngBounds,
center: latLng
};
}
}
cb.call(context, results);
});
}
});
L.Control.Geocoder.google = function(key) {
return new L.Control.Geocoder.Google(key);
};
return L.Control.Geocoder;
}));

View File

@@ -0,0 +1,23 @@
Copyright (c) 2012 sa3m (https://github.com/sa3m)
Copyright (c) 2013 Per Liedman
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,156 @@
## A few words on diversity in tech
I need to take some of your time. I can't believe we let shit like [the Kathy Sierra incident](http://www.wired.com/2014/10/trolls-will-always-win/) or [what happened to Brianna Wu](https://twitter.com/Spacekatgal/status/520739878993420290) happen over and over again. I can't believe we, the open source community, let [sexist, misogynous shit happen over and over again](http://geekfeminism.wikia.com/wiki/Timeline_of_incidents).
I strongly believe that it is my &mdash; and your &mdash; duty to make the open source community, as well as the tech community at large, a community where everyone feel welcome and is accepted. At the very minimum, that means making sure the community and its forums both _are_ safe, and are perceived as safe. It means being friendly and inclusive, even when you disagree with people. It means not shrugging of discussions about sexism and inclusiveness with [handwaving about censorship and free speech](https://josm.openstreetmap.de/ticket/10568). For a more elaborate document on what that means, [the NPM Code of Conduct](http://www.npmjs.com/policies/conduct) is a good start, [Geek Feminism's resources for allies](http://geekfeminism.wikia.com/wiki/Resources_for_allies) contains much more.
While I can't force anyone to do anything, if you happen to disagree with this, I ask of you not to use any of the open source I have published. Nor am I interested in contributions from people who can't accept or act respectfully towards other humans regardless of gender identity, sexual orientation, disability, ethnicity, religion, age, physical appearance, body size, race, or similar personal characteristics. If you think feminism, anti-racism or the LGBT movement is somehow wrong, disturbing or irrelevant, I ask you to go elsewhere to find software.
Leaflet Control Geocoder [![NPM version](https://badge.fury.io/js/leaflet-control-geocoder.png)](http://badge.fury.io/js/leaflet-control-geocoder)
=============================
A simple geocoder for [Leaflet](http://leafletjs.com/) that by default uses [OSM](http://www.openstreetmap.org/)/[Nominatim](http://wiki.openstreetmap.org/wiki/Nominatim).
The plugin supports many different data providers:
* [OSM](http://www.openstreetmap.org/)/[Nominatim](http://wiki.openstreetmap.org/wiki/Nominatim)
* [Bing Locations API](http://msdn.microsoft.com/en-us/library/ff701715.aspx)
* [Google Geocoding API](https://developers.google.com/maps/documentation/geocoding/)
* [Mapbox Geocoding](https://www.mapbox.com/developers/api/geocoding/)
* [MapQuest Geocoding API](http://developer.mapquest.com/web/products/dev-services/geocoding-ws)
* [RaveGeo](http://www2.idevio.com/ravegeo-server.html)
The plugin can easily be extended to support other providers.
See the [Leaflet Control Geocoder Demo](http://perliedman.github.com/leaflet-control-geocoder/).
# Usage
Load the CSS and Javascript:
```HTML
<link rel="stylesheet" href="../Control.Geocoder.css" />
<script src="Control.Geocoder.js"></script>
```
Add the control to a map instance:
```javascript
var map = L.map('map').setView([0, 0], 2);
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
L.Control.geocoder().addTo(map);
```
# Customizing
By default, when a geocoding result is found, the control will center the map on it and place
a marker at its location. This can be customized by overwriting the control's ```markGeocode```
function, to perform any action desired.
For example:
```javascript
var geocoder = L.Control.geocoder().addTo(map);
geocoder.markGeocode = function(result) {
var bbox = result.bbox;
L.polygon([
bbox.getSouthEast(),
bbox.getNorthEast(),
bbox.getNorthWest(),
bbox.getSouthWest()
]).addTo(map);
};
```
This will add a polygon representing the result's boundingbox when a result is selected.
# API
## L.Control.Geocoder
This is the geocoder control. It works like any other Leaflet control, and is added to the map.
### Constructor
```js
L.Control.Geocoder(options)
```
### Options
| Option | Type | Default | Description |
| --------------- | ---------------- | ----------------- | ----------- |
| collapsed | Boolean | true | Collapse control unless hovered/clicked |
| position | String | "topright" | Control [position](http://leafletjs.com/reference.html#control-positions) |
| placeholder | String | "Search..." | Placeholder text for text input
| errorMessage | String | "Nothing found." | Message when no result found / geocoding error occurs |
| geocoder | IGeocoder | new L.Control.Geocoder.Nominatim() | Object to perform the actual geocoding queries |
| showResultIcons | Boolean | false | Show icons for geocoding results (if available); supported by Nominatim |
### Methods
| Method | Returns | Description |
| ------------------------------------- | ------------------- | ----------------- |
| markGeocode(<GeocodingResult> result) | this | Marks a geocoding result on the map |
## L.Control.Geocoder.Nominatim
Uses [Nominatim](http://wiki.openstreetmap.org/wiki/Nominatim) to respond to geocoding queries. This is the default
geocoding service used by the control, unless otherwise specified in the options. Implements ```IGeocoder```.
Unless using your own Nominatim installation, please refer to the [Nominatim usage policy](http://wiki.openstreetmap.org/wiki/Nominatim_usage_policy).
### Constructor
```js
L.Control.Geocoder.Nominatim(options)
```
## Options
| Option | Type | Default | Description |
| --------------- | ---------------- | ----------------- | ----------- |
| serviceUrl | String | "http://nominatim.openstreetmap.org/" | URL of the service |
| geocodingQueryParams | Object | {} | Additional URL parameters (strings) that will be added to geocoding requests; can be used to restrict results to a specific country for example, by providing the [`countrycodes`](http://wiki.openstreetmap.org/wiki/Nominatim#Parameters) parameter to Nominatim |
| reverseQueryParams | Object | {} | Additional URL parameters (strings) that will be added to reverse geocoding requests |
| htmlTemplate | function | special | A function that takes an GeocodingResult as argument and returns an HTML formatted string that represents the result. Default function breaks up address in parts from most to least specific, in attempt to increase readability compared to Nominatim's naming
## L.Control.Geocoder.Bing
Uses [Bing Locations API](http://msdn.microsoft.com/en-us/library/ff701715.aspx) to respond to geocoding queries. Implements ```IGeocoder```.
Note that you need an API key to use this service.
### Constructor
```
L.Control.Geocoder.Bing(<String> key)
```
## IGeocoder
An interface implemented to respond to geocoding queries.
### Methods
| Method | Returns | Description |
| ------------------------------------- | ------------------- | ----------------- |
| geocode(<String> query, callback, context) | GeocodingResult[] | Performs a geocoding query and returns the results to the callback in the provided context |
| reverse(<L.LatLng> location, <Number> scale, callback, context) | GeocodingResult[] | Performs a reverse geocoding query and returns the results to the callback in the provided context |
## GeocodingResult
An object that represents a result from a geocoding query.
### Properties
| Property | Type | Description |
| ---------- | ---------------- | ------------------------------------- |
| name | String | Name of found location |
| bounds | L.LatLngBounds | The bounds of the location |
| center | L.LatLng | The center coordinate of the location |
| icon | String | URL for icon representing result; optional |
| html | String | (optional) HTML formatted representation of the name |

Binary file not shown.

After

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@@ -0,0 +1,26 @@
{
"name": "leaflet-control-geocoder",
"version": "1.0.0",
"description": "Extendable geocoder with builtin OSM/Nominatim support",
"main": "Control.Geocoder.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git://github.com/perliedman/leaflet-control-geocoder.git"
},
"keywords": [
"leaflet",
"geocoder",
"nominatim"
],
"author": "Per Liedman <per@liedman.net>",
"license": "BSD-2-Clause",
"bugs": {
"url": "https://github.com/perliedman/leaflet-control-geocoder/issues"
},
"dependencies": {
"leaflet": "~0.7.2"
}
}

View File

@@ -0,0 +1,75 @@
leaflet-ajax
===========
Allows you to call JSON via an Ajax call with a jsonp fallback.
```javascript
var geojsonLayer = new L.GeoJSON.AJAX("geojson.json");
```
for jsonp add the option "dataType" and set it to "jsonp"
``` javascript
var geojsonLayer = L.geoJson.ajax("http:webhost.fake/geojson.jsonp",{dataType:"jsonp"});
```
Note that data starts to download when the layer is created NOT when its added to the map in order to get a head start.
You may pass either a url string or an array of url strings if you want to download multiple things (handy
if your downloading data from an ESRI based thing which will have separate line, point, and poly features).
As you see you can also use lower case methods without creating new objects
For weirder jsonp you can set "callbackParam" for if you need to change the name of the callback parameter to something besides "callback", e.g. [Mapquest Nominative Open](http://open.mapquestapi.com/nominatim/) uses "json_callback" instead of "callback".
If you want to use `eval` to parse JSON in older browsers you need to pass an option `evil` set to something truthy.
If you want to be able to load stuff from the file system (with appropriate custom flags set) set local to true.
Gives off three events `data:loading`, `data:progress` and `data:loaded`.
- `data:loading` fires before we start downloading things, note if the constructor is given a url it won't wait to be added to the map
to start downloading the data, but it does do an async wait so you have time to add a listener to it (and so [leaflet.spin](https://github.com/makinacorpus/Leaflet.Spin) will work with it).
- `data:progress` is called each time a file is downloaded and passes the downloaded geojson as event data.
- `data:loaded` is called when all files have downloaded, this is mainly different from `data:progress` when you are downloading multiple things.
You can also add a middleware function which is called after you download the data but before you add it to leaflet:
```javascript
var geojsonLayer = L.geoJson.ajax("route/to/esri.json",{
middleware:function(data){
return esri2geoOrSomething(json);
}
});
```
addUrl does not clear the current layers but adds to the current one, e.g.:
```javascript
var geojsonLayer = L.geoJson.ajax("data.json");
geojsonLayer.addUrl("data2.json");//we now have 2 layers
geojsonLayer.refresh();//redownload the most recent layer
geojsonLayer.refresh("new1.json");//add a new layer replacing whatever is there
```
last but now least we can refilter layers without re adding them
```javascript
var geojsonLayer = L.geoJson.ajax("data.json");
geojsonLayer.refilter(function(feature){
return feature.properties.key === values;
});
```
Behind the scenes are two new classes L.Util.ajax = function (url) for same origin requests and L.Util.jsonp = function (url,options) cross origin ones. Both return promises, which have an additional abort method that will abort the ajax request.
```js
L.Util.ajax("url/same/origin.xml").then(function(data){
doStuff(data);
});
//or
L.Util.jsonp("http://www.dif.ori/gin").then(function(data){
doStuff(data);
});
```
In related news `L.Util.Promise` is now a constructor for a promise, it takes one argument, a resolver function.
Some of the jsonp code inspired by/taken from [this interesting looking plugin](https://github.com/stefanocudini/leaflet-search) that I have failed to make heads nor tails of (the plugin, not the jsonp code)

View File

@@ -0,0 +1,740 @@
;(function(){
/**
* Require the given path.
*
* @param {String} path
* @return {Object} exports
* @api public
*/
function require(path, parent, orig) {
var resolved = require.resolve(path);
// lookup failed
if (null == resolved) {
orig = orig || path;
parent = parent || 'root';
var err = new Error('Failed to require "' + orig + '" from "' + parent + '"');
err.path = orig;
err.parent = parent;
err.require = true;
throw err;
}
var module = require.modules[resolved];
// perform real require()
// by invoking the module's
// registered function
if (!module.exports) {
module.exports = {};
module.client = module.component = true;
module.call(this, module.exports, require.relative(resolved), module);
}
return module.exports;
}
/**
* Registered modules.
*/
require.modules = {};
/**
* Registered aliases.
*/
require.aliases = {};
/**
* Resolve `path`.
*
* Lookup:
*
* - PATH/index.js
* - PATH.js
* - PATH
*
* @param {String} path
* @return {String} path or null
* @api private
*/
require.resolve = function(path) {
if (path.charAt(0) === '/') path = path.slice(1);
var paths = [
path,
path + '.js',
path + '.json',
path + '/index.js',
path + '/index.json'
];
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
if (require.modules.hasOwnProperty(path)) return path;
if (require.aliases.hasOwnProperty(path)) return require.aliases[path];
}
};
/**
* Normalize `path` relative to the current path.
*
* @param {String} curr
* @param {String} path
* @return {String}
* @api private
*/
require.normalize = function(curr, path) {
var segs = [];
if ('.' != path.charAt(0)) return path;
curr = curr.split('/');
path = path.split('/');
for (var i = 0; i < path.length; ++i) {
if ('..' == path[i]) {
curr.pop();
} else if ('.' != path[i] && '' != path[i]) {
segs.push(path[i]);
}
}
return curr.concat(segs).join('/');
};
/**
* Register module at `path` with callback `definition`.
*
* @param {String} path
* @param {Function} definition
* @api private
*/
require.register = function(path, definition) {
require.modules[path] = definition;
};
/**
* Alias a module definition.
*
* @param {String} from
* @param {String} to
* @api private
*/
require.alias = function(from, to) {
if (!require.modules.hasOwnProperty(from)) {
throw new Error('Failed to alias "' + from + '", it does not exist');
}
require.aliases[to] = from;
};
/**
* Return a require function relative to the `parent` path.
*
* @param {String} parent
* @return {Function}
* @api private
*/
require.relative = function(parent) {
var p = require.normalize(parent, '..');
/**
* lastIndexOf helper.
*/
function lastIndexOf(arr, obj) {
var i = arr.length;
while (i--) {
if (arr[i] === obj) return i;
}
return -1;
}
/**
* The relative require() itself.
*/
function localRequire(path) {
var resolved = localRequire.resolve(path);
return require(resolved, parent, path);
}
/**
* Resolve relative to the parent.
*/
localRequire.resolve = function(path) {
var c = path.charAt(0);
if ('/' == c) return path.slice(1);
if ('.' == c) return require.normalize(p, path);
// resolve deps by returning
// the dep in the nearest "deps"
// directory
var segs = parent.split('/');
var i = lastIndexOf(segs, 'deps') + 1;
if (!i) i = 0;
path = segs.slice(0, i + 1).join('/') + '/deps/' + path;
return path;
};
/**
* Check if module is defined at `path`.
*/
localRequire.exists = function(path) {
return require.modules.hasOwnProperty(localRequire.resolve(path));
};
return localRequire;
};
require.register("calvinmetcalf-setImmediate/lib/index.js", function(exports, require, module){
"use strict";
var types = [
require("./nextTick"),
require("./mutation"),
require("./postMessage"),
require("./messageChannel"),
require("./stateChange"),
require("./timeout")
];
var handlerQueue = [];
function drainQueue() {
var i = 0,
task,
innerQueue = handlerQueue;
handlerQueue = [];
/*jslint boss: true */
while (task = innerQueue[i++]) {
task();
}
}
var nextTick;
types.some(function (obj) {
var t = obj.test();
if (t) {
nextTick = obj.install(drainQueue);
}
return t;
});
var retFunc = function (task) {
var len, args;
if (arguments.length > 1 && typeof task === "function") {
args = Array.prototype.slice.call(arguments, 1);
args.unshift(undefined);
task = task.bind.apply(task, args);
}
if ((len = handlerQueue.push(task)) === 1) {
nextTick(drainQueue);
}
return len;
};
retFunc.clear = function (n) {
if (n <= handlerQueue.length) {
handlerQueue[n - 1] = function () {};
}
return this;
};
module.exports = retFunc;
});
require.register("calvinmetcalf-setImmediate/lib/nextTick.js", function(exports, require, module){
"use strict";
exports.test = function () {
// Don't get fooled by e.g. browserify environments.
return typeof process === "object" && Object.prototype.toString.call(process) === "[object process]";
};
exports.install = function () {
return process.nextTick;
};
});
require.register("calvinmetcalf-setImmediate/lib/postMessage.js", function(exports, require, module){
"use strict";
var globe = require("./global");
exports.test = function () {
// The test against `importScripts` prevents this implementation from being installed inside a web worker,
// where `global.postMessage` means something completely different and can"t be used for this purpose.
if (!globe.postMessage || globe.importScripts) {
return false;
}
var postMessageIsAsynchronous = true;
var oldOnMessage = globe.onmessage;
globe.onmessage = function () {
postMessageIsAsynchronous = false;
};
globe.postMessage("", "*");
globe.onmessage = oldOnMessage;
return postMessageIsAsynchronous;
};
exports.install = function (func) {
var codeWord = "com.calvinmetcalf.setImmediate" + Math.random();
function globalMessage(event) {
if (event.source === globe && event.data === codeWord) {
func();
}
}
if (globe.addEventListener) {
globe.addEventListener("message", globalMessage, false);
} else {
globe.attachEvent("onmessage", globalMessage);
}
return function () {
globe.postMessage(codeWord, "*");
};
};
});
require.register("calvinmetcalf-setImmediate/lib/messageChannel.js", function(exports, require, module){
"use strict";
var globe = require("./global");
exports.test = function () {
return !!globe.MessageChannel;
};
exports.install = function (func) {
var channel = new globe.MessageChannel();
channel.port1.onmessage = func;
return function () {
channel.port2.postMessage(0);
};
};
});
require.register("calvinmetcalf-setImmediate/lib/stateChange.js", function(exports, require, module){
"use strict";
var globe = require("./global");
exports.test = function () {
return "document" in globe && "onreadystatechange" in globe.document.createElement("script");
};
exports.install = function (handle) {
return function () {
// Create a <script> element; its readystatechange event will be fired asynchronously once it is inserted
// into the document. Do so, thus queuing up the task. Remember to clean up once it's been called.
var scriptEl = globe.document.createElement("script");
scriptEl.onreadystatechange = function () {
handle();
scriptEl.onreadystatechange = null;
scriptEl.parentNode.removeChild(scriptEl);
scriptEl = null;
};
globe.document.documentElement.appendChild(scriptEl);
return handle;
};
};
});
require.register("calvinmetcalf-setImmediate/lib/timeout.js", function(exports, require, module){
"use strict";
exports.test = function () {
return true;
};
exports.install = function (t) {
return function () {
setTimeout(t, 0);
};
};
});
require.register("calvinmetcalf-setImmediate/lib/global.js", function(exports, require, module){
module.exports = typeof global === "object" && global ? global : this;
});
require.register("calvinmetcalf-setImmediate/lib/mutation.js", function(exports, require, module){
"use strict";
//based off rsvp
//https://github.com/tildeio/rsvp.js/blob/master/lib/rsvp/async.js
var globe = require("./global");
var MutationObserver = globe.MutationObserver || globe.WebKitMutationObserver;
exports.test = function () {
return MutationObserver;
};
exports.install = function (handle) {
var observer = new MutationObserver(handle);
var element = globe.document.createElement("div");
observer.observe(element, { attributes: true });
// Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
globe.addEventListener("unload", function () {
observer.disconnect();
observer = null;
}, false);
return function () {
element.setAttribute("drainQueue", "drainQueue");
};
};
});
require.register("lie/lie.js", function(exports, require, module){
var immediate = require('immediate');
// Creates a deferred: an object with a promise and corresponding resolve/reject methods
function Promise(resolver) {
if (!(this instanceof Promise)) {
return new Promise(resolver);
}
var queue = [];
var resolved = false;
// The `handler` variable points to the function that will
// 1) handle a .then(onFulfilled, onRejected) call
// 2) handle a .resolve or .reject call (if not fulfilled)
// Before 2), `handler` holds a queue of callbacks.
// After 2), `handler` is a simple .then handler.
// We use only one function to save memory and complexity.
// Case 1) handle a .then(onFulfilled, onRejected) call
function pending(onFulfilled, onRejected){
return Promise(function(resolver,rejecter){
queue.push({
resolve: onFulfilled,
reject: onRejected,
resolver:resolver,
rejecter:rejecter
});
});
}
function then(onFulfilled, onRejected) {
return resolved?resolved(onFulfilled, onRejected):pending(onFulfilled, onRejected);
}
// Case 2) handle a .resolve or .reject call
// (`onFulfilled` acts as a sentinel)
// The actual function signature is
// .re[ject|solve](sentinel, success, value)
function resolve(success, value){
var action = success ? 'resolve' : 'reject';
var queued;
var callback;
for (var i = 0, l = queue.length; i < l; i++) {
queued = queue[i];
callback = queued[action];
if (typeof callback === 'function') {
immediate(execute,callback, value, queued.resolver, queued.rejecter);
}else if(success){
queued.resolver(value);
}else{
queued.rejecter(value);
}
}
// Replace this handler with a simple resolved or rejected handler
resolved = createHandler(then, value, success);
}
this.then = then;
function yes(value) {
if (!resolved) {
resolve(true, value);
}
}
function no (reason) {
if (!resolved) {
resolve(false, reason);
}
}
try{
resolver(function(a){
if(a && typeof a.then==='function'){
a.then(yes,no);
}else{
yes(a);
}
},no);
}catch(e){
no(e);
}
}
// Creates a fulfilled or rejected .then function
function createHandler(then, value, success) {
return function(onFulfilled, onRejected) {
var callback = success ? onFulfilled : onRejected;
if (typeof callback !== 'function') {
return Promise(function(resolve,reject){
then(resolve,reject);
});
}
return Promise(function(resolve,reject){
immediate(execute,callback,value,resolve,reject);
});
};
}
// Executes the callback with the specified value,
// resolving or rejecting the deferred
function execute(callback, value, resolve, reject) {
try {
var result = callback(value);
if (result && typeof result.then === 'function') {
result.then(resolve, reject);
}
else {
resolve(result);
}
}
catch (error) {
reject(error);
}
}
module.exports = Promise;
});
require.alias("calvinmetcalf-setImmediate/lib/index.js", "lie/deps/immediate/lib/index.js");
require.alias("calvinmetcalf-setImmediate/lib/nextTick.js", "lie/deps/immediate/lib/nextTick.js");
require.alias("calvinmetcalf-setImmediate/lib/postMessage.js", "lie/deps/immediate/lib/postMessage.js");
require.alias("calvinmetcalf-setImmediate/lib/messageChannel.js", "lie/deps/immediate/lib/messageChannel.js");
require.alias("calvinmetcalf-setImmediate/lib/stateChange.js", "lie/deps/immediate/lib/stateChange.js");
require.alias("calvinmetcalf-setImmediate/lib/timeout.js", "lie/deps/immediate/lib/timeout.js");
require.alias("calvinmetcalf-setImmediate/lib/global.js", "lie/deps/immediate/lib/global.js");
require.alias("calvinmetcalf-setImmediate/lib/mutation.js", "lie/deps/immediate/lib/mutation.js");
require.alias("calvinmetcalf-setImmediate/lib/index.js", "lie/deps/immediate/index.js");
require.alias("calvinmetcalf-setImmediate/lib/index.js", "immediate/index.js");
require.alias("calvinmetcalf-setImmediate/lib/index.js", "calvinmetcalf-setImmediate/index.js");
require.alias("lie/lie.js", "lie/index.js");
L.Util.Promise = require("lie");
})();
L.Util.ajax = function(url, options) {
'use strict';
options = options || {};
if (options.jsonp) {
return L.Util.ajax.jsonp(url, options);
}
var request;
var cancel;
var out = L.Util.Promise(function(resolve,reject){
var Ajax;
cancel=reject;
// the following is from JavaScript: The Definitive Guide
if (window.XMLHttpRequest === undefined) {
Ajax = function() {
try {
return new ActiveXObject('Microsoft.XMLHTTP.6.0');
}
catch (e1) {
try {
return new ActiveXObject('Microsoft.XMLHTTP.3.0');
}
catch (e2) {
reject('XMLHttpRequest is not supported');
}
}
};
}
else {
Ajax = window.XMLHttpRequest;
}
var response;
request = new Ajax();
request.open('GET', url);
request.onreadystatechange = function() {
/*jslint evil: true */
if (request.readyState === 4) {
if((request.status < 400&&options.local)|| request.status===200) {
if (window.JSON) {
response = JSON.parse(request.responseText);
} else if (options.evil) {
response = eval('(' + request.responseText + ')');
}
resolve(response);
} else {
if(!request.status){
reject('Attempted cross origin request without CORS enabled');
}else{
reject(request.statusText);
}
}
}
};
request.send();
});
out.then(null,function(reason){
request.abort();
return reason;
});
out.abort = cancel;
return out;
};
L.Util.jsonp = function(url, options) {
options = options || {};
var head = document.getElementsByTagName('head')[0];
var scriptNode = L.DomUtil.create('script', '', head);
var cbName, ourl, cbSuffix, cancel;
var out = L.Util.Promise(function(resolve, reject){
cancel=reject;
var cbParam = options.cbParam || 'callback';
if (options.callbackName) {
cbName = options.callbackName;
}
else {
cbSuffix = '_' + ('' + Math.random()).slice(2);
cbName = 'L.Util.jsonp.cb.' + cbSuffix;
}
scriptNode.type = 'text/javascript';
if (cbSuffix) {
L.Util.jsonp.cb[cbSuffix] = function(data) {
head.removeChild(scriptNode);
delete L.Util.jsonp.cb[cbSuffix];
resolve(data);
};
}
if (url.indexOf('?') === -1) {
ourl = url + '?' + cbParam + '=' + cbName;
}
else {
ourl = url + '&' + cbParam + '=' + cbName;
}
scriptNode.src = ourl;
}).then(null,function(reason){
head.removeChild(scriptNode);
delete L.Util.ajax.cb[cbSuffix];
return reason;
});
out.abort = cancel;
return out;
};
L.Util.jsonp.cb = {};
L.GeoJSON.AJAX = L.GeoJSON.extend({
defaultAJAXparams: {
dataType: 'json',
callbackParam: 'callback',
local:false,
middleware: function(f) {
return f;
}
},
initialize: function(url, options) {
this.urls = [];
if (url) {
if (typeof url === 'string') {
this.urls.push(url);
}
else if (typeof url.pop === 'function') {
this.urls = this.urls.concat(url);
}
else {
options = url;
url = undefined;
}
}
var ajaxParams = L.Util.extend({}, this.defaultAJAXparams);
for (var i in options) {
if (this.defaultAJAXparams.hasOwnProperty(i)) {
ajaxParams[i] = options[i];
}
}
this.ajaxParams = ajaxParams;
this._layers = {};
L.Util.setOptions(this, options);
this.on('data:loaded', function() {
if (this.filter) {
this.refilter(this.filter);
}
}, this);
var self = this;
if (this.urls.length > 0) {
L.Util.Promise(function(yes){
yes();
}).then(function(){
self.addUrl();
});
}
},
clearLayers: function() {
this.urls = [];
L.GeoJSON.prototype.clearLayers.call(this);
return this;
},
addUrl: function(url) {
var self = this;
if (url) {
if (typeof url === 'string') {
self.urls.push(url);
}
else if (typeof url.pop === 'function') {
self.urls = self.urls.concat(url);
}
}
var loading = self.urls.length;
var done = 0;
self.fire('data:loading');
self.urls.forEach(function(url) {
if (self.ajaxParams.dataType.toLowerCase() === 'json') {
L.Util.ajax(url,self.ajaxParams).then(function(d) {
var data = self.ajaxParams.middleware(d);
self.addData(data);
self.fire('data:progress',data);
},function(err){
self.fire('data:progress',{error:err});
});
}
else if (self.ajaxParams.dataType.toLowerCase() === 'jsonp') {
L.Util.jsonp(url,self.ajaxParams).then(function(d) {
var data = self.ajaxParams.middleware(d);
self.addData(data);
self.fire('data:progress',data);
},function(err){
self.fire('data:progress',{error:err});
});
}
});
self.on('data:progress', function() {
if (++done === loading) {
self.fire('data:loaded');
}
});
},
refresh: function(url) {
url = url || this.urls;
this.clearLayers();
this.addUrl(url);
},
refilter: function(func) {
if (typeof func !== 'function') {
this.filter = false;
this.eachLayer(function(a) {
a.setStyle({
stroke: true,
clickable: true
});
});
}
else {
this.filter = func;
this.eachLayer(function(a) {
if (func(a.feature)) {
a.setStyle({
stroke: true,
clickable: true
});
}
else {
a.setStyle({
stroke: false,
clickable: false
});
}
});
}
}
});
L.geoJson.ajax = function(geojson, options) {
return new L.GeoJSON.AJAX(geojson, options);
};

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,7 @@
#Copyright (c) 2012-2013 Calvin Metcalf
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
_THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE._

View File

@@ -0,0 +1,29 @@
{
"name": "leaflet.ajax",
"version": "1.1.0",
"description": "AJAX and JSONP in Leaflet",
"main": "dist/leaflet.ajax.js",
"directories": {
"example": "example"
},
"devDependencies": {
"grunt": "~0.4.1",
"grunt-contrib-concat": "~0.3.0",
"grunt-contrib-uglify": "~0.2.4",
"grunt-contrib-jshint": "~0.6.4"
},
"repository": {
"type": "git",
"url": "git://github.com/calvinmetcalf/leaflet-ajax.git"
},
"keywords": [
"leaflet",
"ajax",
"geojson"
],
"author": "Calvin Metcalf",
"license": "MIT",
"bugs": {
"url": "https://github.com/calvinmetcalf/leaflet-ajax/issues"
}
}

View File

@@ -0,0 +1,19 @@
Contributing
============
Your pull requests are very welcome! Please follow these guidelines:
* Keep code compatible with the same browsers Leaflet aims to be [compatible
with](http://leafletjs.com/features.html).
* Use four spaces rather than tabs.
## Contributors
* [Eric Brelsford](https://github.com/ebrelsford)
* [Nick Iaconis](https://github.com/codefox421)
* [Matthias Althaus](https://github.com/althaus)
* [Yohan Boniface](https://github.com/yohanboniface)
* [kermit-the-frog](https://github.com/kermit-the-frog)
* [kcwu](https://github.com/kcwu)
* [Robbie Trencheny](https://github.com/robbiet480)

View File

@@ -0,0 +1,23 @@
.leaflet-control-loading:empty {
/* This is where your loading indicator would go */
background-image: url('loading.gif');
}
.leaflet-control-loading,
.leaflet-control-zoom a.leaflet-control-loading ,
.leaflet-control-zoomslider a.leaflet-control-loading {
display: none;
}
.leaflet-control-loading.is-loading,
.leaflet-control-zoom a.leaflet-control-loading.is-loading,
.leaflet-control-zoomslider a.leaflet-control-loading.is-loading {
display: block;
}
/* Necessary for display consistency in Leaflet >= 0.6 */
.leaflet-bar-part-bottom {
border-bottom: medium none;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}

View File

@@ -0,0 +1,262 @@
/*
* 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 () {
function defineLeafletLoading(L) {
L.Control.Loading = L.Control.extend({
options: {
position: 'topleft',
separate: false,
zoomControl: null,
spinjs: false,
spin: {
lines: 7,
length: 3,
width: 3,
radius: 5,
rotate: 13,
top: "83%"
}
},
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';
}
else {
// Otherwise, create a container for the indicator
container = L.DomUtil.create('div', 'leaflet-control-zoom leaflet-bar');
}
this._indicator = L.DomUtil.create('a', classes, container);
if (this.options.spinjs) {
this._spinner = new Spinner(this.options.spin).spin();
this._indicator.appendChild(this._spinner.el);
}
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;
this.updateIndicator();
},
removeLoader: function(id) {
delete this._dataLoaders[id];
this.updateIndicator();
},
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');
// If zoomControl exists, make the zoom-out button not last
if (!this.options.separate) {
if (this.zoomControl instanceof L.Control.Zoom) {
L.DomUtil.removeClass(this.zoomControl._zoomOutButton, 'leaflet-bar-part-bottom');
}
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');
// If zoomControl exists, make the zoom-out button last
if (!this.options.separate) {
if (this.zoomControl instanceof L.Control.Zoom) {
L.DomUtil.addClass(this.zoomControl._zoomOutButton, 'leaflet-bar-part-bottom');
}
else if (typeof L.Control.Zoomslider === 'function' && this.zoomControl instanceof L.Control.Zoomslider) {
L.DomUtil.addClass(this.zoomControl._ui.zoomOut, 'leaflet-bar-part-bottom');
}
}
},
_handleLoading: function(e) {
this.addLoader(this.getEventId(e));
},
_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);
}
},
_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);
},
_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);
// Remove layeradd listener from map
map.off('layeradd', this._layerAdd, this);
},
_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({
dataloading: this._handleLoading,
dataload: this._handleLoad,
layerremove: this._handleLoad
}, this);
},
_removeMapListeners: function(map) {
map.off({
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);
}
})();

View File

@@ -0,0 +1,19 @@
Copyright (c) 2013 Eric Brelsford
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,83 @@
Leaflet.loading
===============
Leaflet.loading is a simple loading control for [Leaflet][]. An unobtrusive
loading indicator is added below the zoom control if one exists. The indicator
is visible when tiles are loading or when other data is loading, as indicated by
firing custom events on a map. The indicator can be an image, or a [spin.js][] spinner (image-less).
## Usage
Leaflet.loading is only tested on Leaflet version 0.6 or greater. It will almost
certainly not work with older versions of Leaflet.
Include `Control.Loading.js` and `Control.Loading.css`, then create a map with `loadingControl: true`
in its options.
By default, Leaflet.loading expects an image. `Control.Loading.css` contains a
start in this direction. The simplest case would be adding a 16 x 16 loading gif
in `.leaflet-control-loading`.
You can also set `spinjs: true` in the options, and load [spin.js][]
to use that instead of an image. A spin.js options object can be passed as the spin key
when initializing the control.
Whichever method you use, make sure you only use one.
Once the above is complete you will have a loading indicator that only appears
when tiles are loading.
If you want to show the loading indicator while other AJAX requests or something
else is occurring, simply fire the `dataloading` event on your map when you
begin loading and `dataload` when you are finished loading. The control tracks
the number of concurrent loaders, so it is your responsibility to ensure that
the `dataloading` and `dataload` are called symmetrically.
### Options
- **position**: (string) Where you want the control to show up on the map (standard
Leaflet control option). Optional, defaults to `topleft`
- **separate**: (boolean) Whether the control should be separate from the zoom
control or not, defaults to false.
- **zoomControl**: (L.Control.Zoom) The zoom control that the control should be
added to. This is only necessary when adding a loading control to a zoom
control that you added manually and do not want a separate loading control.
- **spinjs**: (boolean) Enable the use of [spin.js][]. Optional, defaults to `false`
- **spin**: (object) A [spin.js][] options object. Optional, defaults to
```
{
lines: 7,
length: 3,
width: 3,
radius: 5,
rotate: 13,
top: "83%"
}
```
## Demos
See Leaflet.loading in action (zoom or pan to make tiles load):
- Using the [simplest setup][simple], with the loading indicator attached to
the zoom control.
- With the loading indicator [separate][] from the zoom control.
- With the loading indicator and zoom control on the [top right][topright] of
the map.
- The [simplest example using spin.js](http://ebrelsford.github.io/Leaflet.loading/spinjs.html) instead of an image
## License
Leaflet.loading is free software, and may be redistributed under the MIT
License.
[Leaflet]: https://github.com/Leaflet/Leaflet
[spin.js]: https://github.com/fgnass/spin.js/
[simple]: http://ebrelsford.github.io/Leaflet.loading/simple.html
[separate]: http://ebrelsford.github.io/Leaflet.loading/separate.html
[topright]: http://ebrelsford.github.io/Leaflet.loading/topright.html

View File

@@ -0,0 +1,27 @@
{
"name": "leaflet.loading",
"version": "0.1.13",
"homepage": "https://github.com/ebrelsford/leaflet.loading",
"authors": [
"Eric Brelsford <ebrelsford@gmail.com>"
],
"description": "A loading-indicator control for Leaflet",
"main": "src/Control.Loading.js",
"keywords": [
"leaflet",
"map",
"gis",
"loading"
],
"dependencies": {
"leaflet": ">=0.6"
},
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

File diff suppressed because it is too large Load Diff

478
assets/leaflet/leaflet.css Normal file
View File

@@ -0,0 +1,478 @@
/* required styles */
.leaflet-map-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-pane,
.leaflet-tile-container,
.leaflet-overlay-pane,
.leaflet-shadow-pane,
.leaflet-marker-pane,
.leaflet-popup-pane,
.leaflet-overlay-pane svg,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
-ms-touch-action: none;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container img {
max-width: none !important;
}
/* stupid Android 2 doesn't understand "max-width: none" properly */
.leaflet-container img.leaflet-image-layer {
max-width: 15000px !important;
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-tile-pane { z-index: 2; }
.leaflet-objects-pane { z-index: 3; }
.leaflet-overlay-pane { z-index: 4; }
.leaflet-shadow-pane { z-index: 5; }
.leaflet-marker-pane { z-index: 6; }
.leaflet-popup-pane { z-index: 7; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 7;
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile,
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-tile-loaded,
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile,
.leaflet-touching .leaflet-zoom-animated {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-clickable {
cursor: pointer;
}
.leaflet-container {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-container,
.leaflet-dragging .leaflet-clickable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-control-zoom-out {
font-size: 20px;
}
.leaflet-touch .leaflet-control-zoom-in {
font-size: 22px;
}
.leaflet-touch .leaflet-control-zoom-out {
font-size: 24px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url('images/layers.png');
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url('images/layers-2x.png');
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: content-box;
box-sizing: content-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
margin: 0 auto;
width: 40px;
height: 20px;
position: relative;
overflow: hidden;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}

File diff suppressed because one or more lines are too long

35
composer.json Normal file
View File

@@ -0,0 +1,35 @@
{
"name":"netzmacht/contao-leaflet-libraries",
"description":"Javascript libraries for the Leaflet integration for Contao",
"keywords":["contao", "maps", "leaflet"],
"type":"contao-module",
"license":"LGPL-3.0+",
"authors":[
{
"name":"David Molineus",
"email":"mail@netzmacht.de",
"homepage":"http://www.netzmacht.de",
"role":"Project leader"
}
],
"support":{
"email":"mail@netzmacht.de",
"issues":"https://github.com/netzmacht/contao-leaflet-libraries/issues",
"source":"https://github.com/netzmacht/contao-leaflet-libraries"
},
"require":{
"php":">=5.4",
"contao/core":">=3.2,<3.5-dev"
},
"extra":{
"branch-alias": {
"dev-master": "0.7.3.x-dev"
},
"contao": {
"sources":{
"module": "system/modules/leaflet-libs",
"assets": "assets/leaflet/libs"
}
}
}
}

View File

@@ -0,0 +1,2 @@
requires[] = "leaflet"

63
module/config/config.php Normal file
View File

@@ -0,0 +1,63 @@
<?php
/*
* Leaflet assets.
*
* The leaflet definition are aware of the required javascript libraries. Register the assets so that they are
* loaded automatically.
*
* Each entry is an array of 2 values. The first is the resource. The second is a type. Supported types are:
* - url: An valid url.
* - file: An file path relative to the Contao Root.
* - source: Inline css/javascript.
*
* You don't have to define it as array if you simply add a file. Do not add |static and or media type flag to it.
* It's getting added by default if not being in debug mode.
*/
$GLOBALS['LEAFLET_LIBRARIES']['leaflet'] = array
(
'name' => 'Leaflet',
'version' => '0.7.3',
'license' => '<a href="https://github.com/Leaflet/Leaflet/blob/master/LICENSE" target="_blank">BSD-2-Clause</a>',
'homepage' => 'http://leafletjs.com',
'css' => 'assets/leaflet/libs/leaflet/leaflet.css',
'javascript' => 'assets/leaflet/libs/leaflet/leaflet.js',
);
$GLOBALS['LEAFLET_LIBRARIES']['leaflet-providers'] = array
(
'name' => 'Leaflet-providers',
'version' => '1.0.12',
'license' => '<a href="https://github.com/leaflet-extras/leaflet-providers/blob/master/license.md" target="_blank">BSD-2-Clause</a>',
'homepage' => 'http://leaflet-extras.github.io/leaflet-providers',
'javascript' => 'assets/leaflet/libs/leaflet-providers/leaflet-providers.js'
);
$GLOBALS['LEAFLET_LIBRARIES']['leaflet-ajax'] = array
(
'name' => 'Leaflet-ajax',
'version' => '1.1.0',
'license' => '<a href="https://github.com/calvinmetcalf/leaflet-ajax/blob/master/license.md" target="_blank">MIT</a>',
'homepage' => 'https://github.com/calvinmetcalf/leaflet-ajax',
'javascript' => 'assets/leaflet/libs/leaflet-ajax/leaflet.ajax.min.js'
);
$GLOBALS['LEAFLET_LIBRARIES']['leaflet-loading'] = array
(
'name' => 'Leaflet.loading',
'version' => '0.1.13 ',
'license' => '<a href="https://github.com/ebrelsford/Leaflet.loading/blob/master/LICENSE" target="_blank">MIT</a>',
'homepage' => 'https://github.com/ebrelsford/Leaflet.loading',
'css' => 'assets/leaflet/libs/leaflet-loading/Control.Loading.css',
'javascript' => 'assets/leaflet/libs/leaflet-loading/Control.Loading.js'
);
$GLOBALS['LEAFLET_LIBRARIES']['spin.js'] = array
(
'name' => 'spin.js',
'version' => '2.0.2',
'license' => '<a href="https://github.com/fgnass/spin.js/blob/master/LICENSE.txt" target="_blank">MIT</a>',
'homepage' => 'http://fgnass.github.io/spin.js',
'javascript' => 'assets/leaflet/libs/spin-js/spin.min.js'
);