demarches-normaliennes/app/assets/javascripts/api_carto/lib/leaflet.photon.js
2015-08-10 11:05:06 +02:00

444 lines
13 KiB
JavaScript

L.PhotonBase = L.Class.extend({
forEach: function (els, callback) {
Array.prototype.forEach.call(els, callback);
},
ajax: function (callback, thisobj) {
if (typeof this.xhr === 'object') {
this.xhr.abort();
}
this.xhr = new XMLHttpRequest();
var self = this;
this.xhr.open('GET', this.options.url + this.buildQueryString(this.getParams()), true);
this.xhr.onload = function(e) {
self.fire('ajax:return');
if (this.status === 200) {
if (callback) {
var raw = this.response;
raw = JSON.parse(raw);
callback.call(thisobj || this, raw);
}
}
delete this.xhr;
};
this.fire('ajax:send');
this.xhr.send();
},
buildQueryString: function (params) {
var queryString = [];
for (var key in params) {
if (params[key]) {
queryString.push(encodeURIComponent(key) + '=' + encodeURIComponent(params[key]));
}
}
return queryString.join('&');
},
featureToPopupContent: function (feature) {
var container = L.DomUtil.create('div', 'leaflet-photon-popup'),
title = L.DomUtil.create('h3', '', container);
title.innerHTML = feature.properties.label;
return container;
}
});
L.PhotonSearch = L.PhotonBase.extend({
includes: L.Mixin.Events,
options: {
url: 'http://photon.komoot.de/api/?',
placeholder: 'Start typing...',
minChar: 3,
limit: 5,
submitDelay: 300,
includePosition: true,
noResultLabel: 'No result',
feedbackEmail: 'photon@komoot.de', // Set to null to remove feedback box
feedbackLabel: 'Feedback'
},
CACHE: '',
RESULTS: [],
KEYS: {
LEFT: 37,
UP: 38,
RIGHT: 39,
DOWN: 40,
TAB: 9,
RETURN: 13,
ESC: 27,
APPLE: 91,
SHIFT: 16,
ALT: 17,
CTRL: 18
},
initialize: function (map, input, options) {
this.map = map;
this.input = input;
L.setOptions(this, options);
var CURRENT = null;
try {
Object.defineProperty(this, 'CURRENT', {
get: function () {
return CURRENT;
},
set: function (index) {
if (typeof index === 'object') {
index = this.resultToIndex(index);
}
CURRENT = index;
}
});
} catch (e) {
// Hello IE8
}
this.input.type = 'search';
this.input.placeholder = this.options.placeholder;
this.input.autocomplete = 'off';
this.input.autocorrect = 'off';
L.DomEvent.disableClickPropagation(this.input);
L.DomEvent.on(this.input, 'keydown', this.onKeyDown, this);
L.DomEvent.on(this.input, 'input', this.onInput, this);
L.DomEvent.on(this.input, 'blur', this.onBlur, this);
L.DomEvent.on(this.input, 'focus', this.onFocus, this);
this.createResultsContainer();
},
createResultsContainer: function () {
this.resultsContainer = L.DomUtil.create('ul', 'photon-autocomplete', document.querySelector('body'));
},
resizeContainer: function()
{
var l = this.getLeft(this.input);
var t = this.getTop(this.input) + this.input.offsetHeight;
this.resultsContainer.style.left = l + 'px';
this.resultsContainer.style.top = t + 'px';
var width = this.options.width ? this.options.width : this.input.offsetWidth - 2;
this.resultsContainer.style.width = width + 'px';
},
onKeyDown: function (e) {
switch (e.keyCode) {
case this.KEYS.TAB:
if(this.CURRENT !== null)
{
this.setChoice();
}
L.DomEvent.stop(e);
break;
case this.KEYS.RETURN:
L.DomEvent.stop(e);
this.setChoice();
break;
case this.KEYS.ESC:
L.DomEvent.stop(e);
this.hide();
this.input.blur();
break;
case this.KEYS.DOWN:
if(this.RESULTS.length > 0) {
if(this.CURRENT !== null && this.CURRENT < this.RESULTS.length - 1) { // what if one resutl?
this.CURRENT++;
this.highlight();
}
else if(this.CURRENT === null) {
this.CURRENT = 0;
this.highlight();
}
}
break;
case this.KEYS.UP:
if(this.CURRENT !== null) {
L.DomEvent.stop(e);
}
if(this.RESULTS.length > 0) {
if(this.CURRENT > 0) {
this.CURRENT--;
this.highlight();
}
else if(this.CURRENT === 0) {
this.CURRENT = null;
this.highlight();
}
}
break;
}
},
onInput: function (e) {
if (typeof this.submitDelay === 'number') {
window.clearTimeout(this.submitDelay);
delete this.submitDelay;
}
this.submitDelay = window.setTimeout(L.Util.bind(this.search, this), this.options.submitDelay);
},
onBlur: function (e) {
this.fire('blur');
var self = this;
setTimeout(function () {
self.hide();
}, 100);
},
onFocus: function (e) {
this.fire('focus');
this.input.select();
this.search(); // In case we have a value from a previous search.
},
clear: function () {
this.RESULTS = [];
this.CURRENT = null;
this.CACHE = '';
this.resultsContainer.innerHTML = '';
},
hide: function() {
this.fire('hide');
this.clear();
this.resultsContainer.style.display = 'none';
},
setChoice: function (choice) {
choice = choice || this.RESULTS[this.CURRENT];
if (choice) {
this.hide();
this.fire('selected', {choice: choice.feature});
this.onSelected(choice.feature);
this.input.value = '';
}
},
search: function() {
var val = this.input.value;
var minChar = typeof this.options.minChar === 'function' ? this.options.minChar(val) : val.length >= this.options.minChar;
if (!val || !minChar) return this.clear();
if(val + '' === this.CACHE + '') return;
else this.CACHE = val;
this._doSearch();
},
_doSearch: function () {
this.ajax(this.handleResults, this);
},
_onSelected: function (feature) {
this.map.setView([feature.geometry.coordinates[1], feature.geometry.coordinates[0]], 16);
},
onSelected: function (choice) {
return (this.options.onSelected || this._onSelected).call(this, choice);
},
_formatResult: function (feature, el) {
var title = L.DomUtil.create('strong', '', el),
detailsContainer = L.DomUtil.create('small', '', el),
details = [],
type = this.formatType(feature);
title.innerHTML = feature.properties.name;
if (type) details.push(type);
if (feature.properties.city && feature.properties.city !== feature.properties.name) {
details.push(feature.properties.city);
}
if (feature.properties.country) details.push(feature.properties.country);
detailsContainer.innerHTML = details.join(', ');
},
formatResult: function (feature, el) {
return (this.options.formatResult || this._formatResult).call(this, feature, el);
},
formatType: function (feature) {
return (this.options.formatType || this._formatType).call(this, feature);
},
_formatType: function (feature) {
return feature.properties.osm_value;
},
createResult: function (feature) {
var el = L.DomUtil.create('li', '', this.resultsContainer);
this.formatResult(feature, el);
var result = {
feature: feature,
el: el
};
// Touch handling needed
L.DomEvent.on(el, 'mouseover', function (e) {
this.CURRENT = result;
this.highlight();
}, this);
L.DomEvent.on(el, 'mousedown', function (e) {
this.setChoice();
}, this);
return result;
},
resultToIndex: function (result) {
var out = null;
this.forEach(this.RESULTS, function (item, index) {
if (item === result) {
out = index;
return;
}
});
return out;
},
handleResults: function(geojson) {
var self = this;
this.clear();
this.resultsContainer.style.display = 'block';
this.resizeContainer();
this.forEach(geojson.features, function (feature) {
self.RESULTS.push(self.createResult(feature));
});
if (geojson.features.length === 0) {
var noresult = L.DomUtil.create('li', 'photon-no-result', this.resultsContainer);
noresult.innerHTML = this.options.noResultLabel;
}
if (this.options.feedbackEmail) {
var feedback = L.DomUtil.create('a', 'photon-feedback', this.resultsContainer);
feedback.href = 'mailto:' + this.options.feedbackEmail;
feedback.innerHTML = this.options.feedbackLabel;
}
this.CURRENT = 0;
this.highlight();
if (this.options.resultsHandler) {
this.options.resultsHandler(geojson);
}
},
highlight: function () {
var self = this;
this.forEach(this.RESULTS, function (item, index) {
if (index === self.CURRENT) {
L.DomUtil.addClass(item.el, 'on');
}
else {
L.DomUtil.removeClass(item.el, 'on');
}
});
},
getLeft: function (el) {
var tmp = el.offsetLeft;
el = el.offsetParent;
while(el) {
tmp += el.offsetLeft;
el = el.offsetParent;
}
return tmp;
},
getTop: function (el) {
var tmp = el.offsetTop;
el = el.offsetParent;
while(el) {
tmp += el.offsetTop;
el = el.offsetParent;
}
return tmp;
},
getParams: function () {
return {
q: this.CACHE,
lang: this.options.lang,
limit: this.options.limit,
lat: this.options.includePosition ? this.map.getCenter().lat : null,
lon: this.options.includePosition ? this.map.getCenter().lng : null
};
}
});
L.Control.Photon = L.Control.extend({
includes: L.Mixin.Events,
onAdd: function (map, options) {
this.map = map;
this.container = L.DomUtil.create('div', 'leaflet-photon');
this.options = L.Util.extend(this.options, options);
this.input = L.DomUtil.create('input', 'photon-input', this.container);
this.search = new L.PhotonSearch(map, this.input, this.options);
this.search.on('blur', this.forwardEvent, this);
this.search.on('focus', this.forwardEvent, this);
this.search.on('hide', this.forwardEvent, this);
this.search.on('selected', this.forwardEvent, this);
this.search.on('ajax:send', this.forwardEvent, this);
this.search.on('ajax:return', this.forwardEvent, this);
return this.container;
},
forwardEvent: function (e) {
this.fire(e.type, e);
}
});
L.Map.addInitHook(function () {
if (this.options.photonControl) {
this.photonControl = new L.Control.Photon(this.options.photonControlOptions || {});
this.addControl(this.photonControl);
}
});
L.PhotonReverse = L.PhotonBase.extend({
includes: L.Mixin.Events,
options: {
url: 'http://photon.komoot.de/reverse/?',
limit: 1,
handleResults: null
},
initialize: function (options) {
L.setOptions(this, options);
},
doReverse: function (latlng) {
latlng = L.latLng(latlng);
this.fire('reverse', {latlng: latlng});
this.latlng = latlng;
this.ajax(this.handleResults, this);
},
_handleResults: function (data) {
/*eslint-disable no-console */
console.log(data);
/*eslint-enable no-alert */
},
handleResults: function (data) {
return (this.options.handleResults || this._handleResults).call(this, data);
},
getParams: function () {
return {
lang: this.options.lang,
limit: this.options.limit,
lat: this.latlng.lat,
lon: this.latlng.lng
};
}
});