Initial commit
This commit is contained in:
342
assets/lib/leaflet/leaflet.fusesearch.js
Normal file
342
assets/lib/leaflet/leaflet.fusesearch.js
Normal file
@@ -0,0 +1,342 @@
|
||||
|
||||
// From http://www.tutorialspoint.com/javascript/array_map.htm
|
||||
if (!Array.prototype.map)
|
||||
{
|
||||
Array.prototype.map = function(fun /*, thisp*/)
|
||||
{
|
||||
var len = this.length;
|
||||
if (typeof fun !== "function")
|
||||
throw new TypeError();
|
||||
|
||||
var res = new Array(len);
|
||||
var thisp = arguments[1];
|
||||
for (var i = 0; i < len; i++)
|
||||
{
|
||||
if (i in this)
|
||||
res[i] = fun.call(thisp, this[i], i, this);
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
L.Control.FuseSearch = L.Control.extend({
|
||||
|
||||
includes: L.Evented.prototype,
|
||||
|
||||
options: {
|
||||
position: 'topright',
|
||||
title: 'Search',
|
||||
panelTitle: '',
|
||||
placeholder: 'Search',
|
||||
caseSensitive: false,
|
||||
threshold: 0.5,
|
||||
maxResultLength: null,
|
||||
showResultFct: null,
|
||||
showInvisibleFeatures: true
|
||||
},
|
||||
|
||||
initialize: function(options) {
|
||||
L.setOptions(this, options);
|
||||
this._panelOnLeftSide = (this.options.position.indexOf("left") !== -1);
|
||||
},
|
||||
|
||||
onAdd: function(map) {
|
||||
|
||||
var ctrl = this._createControl();
|
||||
this._createPanel(map);
|
||||
this._setEventListeners();
|
||||
map.invalidateSize();
|
||||
|
||||
return ctrl;
|
||||
},
|
||||
|
||||
onRemove: function(map) {
|
||||
|
||||
this.hidePanel(map);
|
||||
this._clearEventListeners();
|
||||
this._clearPanel(map);
|
||||
this._clearControl();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
_createControl: function() {
|
||||
|
||||
var className = 'leaflet-fusesearch-control',
|
||||
container = L.DomUtil.create('div', className);
|
||||
|
||||
// Control to open the search panel
|
||||
var butt = this._openButton = L.DomUtil.create('a', 'button', container);
|
||||
butt.href = '#';
|
||||
butt.title = this.options.title;
|
||||
var stop = L.DomEvent.stopPropagation;
|
||||
L.DomEvent.on(butt, 'click', stop)
|
||||
.on(butt, 'mousedown', stop)
|
||||
.on(butt, 'touchstart', stop)
|
||||
.on(butt, 'mousewheel', stop)
|
||||
.on(butt, 'MozMousePixelScroll', stop);
|
||||
L.DomEvent.on(butt, 'click', L.DomEvent.preventDefault);
|
||||
L.DomEvent.on(butt, 'click', this.showPanel, this);
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
_clearControl: function() {
|
||||
// Unregister events to prevent memory leak
|
||||
var butt = this._openButton;
|
||||
var stop = L.DomEvent.stopPropagation;
|
||||
L.DomEvent.off(butt, 'click', stop)
|
||||
.off(butt, 'mousedown', stop)
|
||||
.off(butt, 'touchstart', stop)
|
||||
.off(butt, 'mousewheel', stop)
|
||||
.off(butt, 'MozMousePixelScroll', stop);
|
||||
L.DomEvent.off(butt, 'click', L.DomEvent.preventDefault);
|
||||
L.DomEvent.off(butt, 'click', this.showPanel);
|
||||
},
|
||||
|
||||
_createPanel: function(map) {
|
||||
var _this = this;
|
||||
|
||||
// Create the search panel
|
||||
var mapContainer = map.getContainer();
|
||||
var className = 'leaflet-fusesearch-panel',
|
||||
pane = this._panel = L.DomUtil.create('div', className, mapContainer);
|
||||
|
||||
// Make sure we don't drag the map when we interact with the content
|
||||
var stop = L.DomEvent.stopPropagation;
|
||||
L.DomEvent.on(pane, 'click', stop)
|
||||
.on(pane, 'dblclick', stop)
|
||||
.on(pane, 'mousedown', stop)
|
||||
.on(pane, 'touchstart', stop)
|
||||
.on(pane, 'mousewheel', stop)
|
||||
.on(pane, 'MozMousePixelScroll', stop);
|
||||
|
||||
// place the pane on the same side as the control
|
||||
if (this._panelOnLeftSide) {
|
||||
L.DomUtil.addClass(pane, 'left');
|
||||
} else {
|
||||
L.DomUtil.addClass(pane, 'right');
|
||||
}
|
||||
|
||||
// Intermediate container to get the box sizing right
|
||||
var container = L.DomUtil.create('div', 'content', pane);
|
||||
|
||||
var header = L.DomUtil.create('div', 'header', container);
|
||||
if (this.options.panelTitle) {
|
||||
var title = L.DomUtil.create('p', 'panel-title', header);
|
||||
title.innerHTML = this.options.panelTitle;
|
||||
}
|
||||
|
||||
// Search image and input field
|
||||
L.DomUtil.create('img', 'search-image', header);
|
||||
this._input = L.DomUtil.create('input', 'search-input', header);
|
||||
this._input.maxLength = 30;
|
||||
this._input.placeholder = this.options.placeholder;
|
||||
this._input.onkeyup = function(evt) {
|
||||
var searchString = evt.currentTarget.value;
|
||||
_this.searchFeatures(searchString);
|
||||
};
|
||||
|
||||
// Close button
|
||||
var close = this._closeButton = L.DomUtil.create('a', 'close', header);
|
||||
close.innerHTML = '×';
|
||||
L.DomEvent.on(close, 'click', this.hidePanel, this);
|
||||
|
||||
// Where the result will be listed
|
||||
this._resultList = L.DomUtil.create('div', 'result-list', container);
|
||||
|
||||
return pane;
|
||||
},
|
||||
|
||||
_clearPanel: function(map) {
|
||||
|
||||
// Unregister event handlers
|
||||
var stop = L.DomEvent.stopPropagation;
|
||||
L.DomEvent.off(this._panel, 'click', stop)
|
||||
.off(this._panel, 'dblclick', stop)
|
||||
.off(this._panel, 'mousedown', stop)
|
||||
.off(this._panel, 'touchstart', stop)
|
||||
.off(this._panel, 'mousewheel', stop)
|
||||
.off(this._panel, 'MozMousePixelScroll', stop);
|
||||
|
||||
L.DomEvent.off(this._closeButton, 'click', this.hidePanel);
|
||||
|
||||
var mapContainer = map.getContainer();
|
||||
mapContainer.removeChild(this._panel);
|
||||
|
||||
this._panel = null;
|
||||
},
|
||||
|
||||
_setEventListeners : function() {
|
||||
var that = this;
|
||||
var input = this._input;
|
||||
this._map.addEventListener('overlayadd', function() {
|
||||
that.searchFeatures(input.value);
|
||||
});
|
||||
this._map.addEventListener('overlayremove', function() {
|
||||
that.searchFeatures(input.value);
|
||||
});
|
||||
},
|
||||
|
||||
_clearEventListeners: function() {
|
||||
this._map.removeEventListener('overlayadd');
|
||||
this._map.removeEventListener('overlayremove');
|
||||
},
|
||||
|
||||
isPanelVisible: function () {
|
||||
return L.DomUtil.hasClass(this._panel, 'visible');
|
||||
},
|
||||
|
||||
showPanel: function () {
|
||||
if (! this.isPanelVisible()) {
|
||||
L.DomUtil.addClass(this._panel, 'visible');
|
||||
// Preserve map centre
|
||||
this._map.panBy([this.getOffset() * 0.5, 0], {duration: 0.5});
|
||||
this.fire('show');
|
||||
this._input.select();
|
||||
// Search again as visibility of features might have changed
|
||||
this.searchFeatures(this._input.value);
|
||||
}
|
||||
},
|
||||
|
||||
hidePanel: function (e) {
|
||||
if (this.isPanelVisible()) {
|
||||
L.DomUtil.removeClass(this._panel, 'visible');
|
||||
// Move back the map centre - only if we still hold this._map
|
||||
// as this might already have been cleared up by removeFrom()
|
||||
if (null !== this._map) {
|
||||
this._map.panBy([this.getOffset() * -0.5, 0], {duration: 0.5});
|
||||
};
|
||||
this.fire('hide');
|
||||
if(e) {
|
||||
L.DomEvent.stopPropagation(e);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getOffset: function() {
|
||||
if (this._panelOnLeftSide) {
|
||||
return - this._panel.offsetWidth;
|
||||
} else {
|
||||
return this._panel.offsetWidth;
|
||||
}
|
||||
},
|
||||
|
||||
indexFeatures: function(data, keys) {
|
||||
|
||||
var jsonFeatures = data.features || data;
|
||||
|
||||
this._keys = keys;
|
||||
var properties = jsonFeatures.map(function(feature) {
|
||||
// Keep track of the original feature
|
||||
feature.properties._feature = feature;
|
||||
return feature.properties;
|
||||
});
|
||||
|
||||
var options = {
|
||||
keys: keys,
|
||||
caseSensitive: this.options.caseSensitive,
|
||||
threshold : this.options.threshold
|
||||
};
|
||||
|
||||
this._fuseIndex = new Fuse(properties, options);
|
||||
},
|
||||
|
||||
searchFeatures: function(string) {
|
||||
|
||||
var result = this._fuseIndex.search(string);
|
||||
|
||||
// Empty result list
|
||||
var listItems = document.querySelectorAll(".result-item");
|
||||
for (var i = 0 ; i < listItems.length ; i++) {
|
||||
listItems[i].remove();
|
||||
}
|
||||
|
||||
var resultList = document.querySelector('.result-list');
|
||||
var num = 0;
|
||||
var max = this.options.maxResultLength;
|
||||
for (var i in result) {
|
||||
var props = result[i];
|
||||
var feature = props._feature;
|
||||
var popup = this._getFeaturePopupIfVisible(feature);
|
||||
|
||||
if (undefined !== popup || this.options.showInvisibleFeatures) {
|
||||
this.createResultItem(props, resultList, popup);
|
||||
if (undefined !== max && ++num === max)
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
// Reapply the search on the indexed features - useful if features have been filtered out
|
||||
if (this.isPanelVisible()) {
|
||||
this.searchFeatures(this._input.value);
|
||||
}
|
||||
},
|
||||
|
||||
_getFeaturePopupIfVisible: function(feature) {
|
||||
var layer = feature.layer;
|
||||
if (undefined !== layer && this._map.hasLayer(layer)) {
|
||||
return layer.getPopup();
|
||||
}
|
||||
},
|
||||
|
||||
createResultItem: function(props, container, popup) {
|
||||
|
||||
var _this = this;
|
||||
var feature = props._feature;
|
||||
|
||||
// Create a container and open the associated popup on click
|
||||
var resultItem = L.DomUtil.create('p', 'result-item', container);
|
||||
|
||||
if (undefined !== popup) {
|
||||
L.DomUtil.addClass(resultItem, 'clickable');
|
||||
resultItem.onclick = function() {
|
||||
|
||||
if (window.matchMedia("(max-width:480px)").matches) {
|
||||
_this.hidePanel();
|
||||
feature.layer.openPopup();
|
||||
} else {
|
||||
_this._panAndPopup(feature, popup);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Fill in the container with the user-supplied function if any,
|
||||
// otherwise display the feature properties used for the search.
|
||||
if (null !== this.options.showResultFct) {
|
||||
this.options.showResultFct(feature, resultItem);
|
||||
} else {
|
||||
str = '<b>' + props[this._keys[0]] + '</b>';
|
||||
for (var i = 1; i < this._keys.length; i++) {
|
||||
str += '<br/>' + props[this._keys[i]];
|
||||
}
|
||||
resultItem.innerHTML = str;
|
||||
};
|
||||
|
||||
return resultItem;
|
||||
},
|
||||
|
||||
_panAndPopup : function(feature, popup) {
|
||||
// Temporarily adapt the map padding so that the popup is not hidden by the search pane
|
||||
if (this._panelOnLeftSide) {
|
||||
var oldPadding = popup.options.autoPanPaddingTopLeft;
|
||||
var newPadding = new L.Point(- this.getOffset(), 10);
|
||||
popup.options.autoPanPaddingTopLeft = newPadding;
|
||||
feature.layer.openPopup();
|
||||
popup.options.autoPanPaddingTopLeft = oldPadding;
|
||||
} else {
|
||||
var oldPadding = popup.options.autoPanPaddingBottomRight;
|
||||
var newPadding = new L.Point(this.getOffset(), 10);
|
||||
popup.options.autoPanPaddingBottomRight = newPadding;
|
||||
feature.layer.openPopup();
|
||||
popup.options.autoPanPaddingBottomRight = oldPadding;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
L.control.fuseSearch = function(options) {
|
||||
return new L.Control.FuseSearch(options);
|
||||
};
|
||||
Reference in New Issue
Block a user