new search

This commit is contained in:
fkloft 2015-02-12 14:37:49 +01:00
parent 7fd88dd1e3
commit 4c7b8e0e70
6 changed files with 312 additions and 71 deletions

View File

@ -591,7 +591,7 @@ function boot() {
window.setupDialogs();
window.setupMap();
window.setupOMS();
window.setupGeosearch();
window.search.setup();
window.setupRedeem();
window.setupLargeImagePreview();
window.setupSidebarToggle();

View File

@ -1,50 +0,0 @@
// GEOSEARCH /////////////////////////////////////////////////////////
window.setupGeosearch = function() {
$('#geosearch').keypress(function(e) {
if((e.keyCode ? e.keyCode : e.which) != 13) return;
var search = $(this).val();
if ( window.search(search) ) return;
e.preventDefault();
});
$('#geosearchwrapper img').click(function(){
map.locate({setView : true, maxZoom: 13});
});
$('#geosearch').keyup(function(){$(this).removeClass('search_not_found')});
}
window.search = function(search) {
if (!runHooks('geoSearch', search)) {
return true;
}
if(search.split(",").length == 2) {
var ll = search.split(",");
if(!isNaN(ll[0]) && !isNaN(ll[1])) {
ll = [parseFloat(ll[0]), parseFloat(ll[1])];
if(ll[0] >= -90 && ll[0] <= 90 && ll[1] >= -180 && ll[1] <= 180) {
window.map.setView(L.latLng(ll[0], ll[1]), 17);
if(window.isSmartphone()) window.show('map');
return true;
}
}
}
$.getJSON(NOMINATIM + encodeURIComponent(search), function(data) {
if(!data || !data[0]) {
$('#geosearch').addClass('search_not_found');
return true;
}
var b = data[0].boundingbox;
if(!b) return true;
var southWest = new L.LatLng(b[0], b[2]),
northEast = new L.LatLng(b[1], b[3]),
bounds = new L.LatLngBounds(southWest, northEast);
window.map.fitBounds(bounds, {maxZoom: 17});
if(window.isSmartphone()) window.show('map');
});
}

View File

@ -59,7 +59,7 @@ window.VALID_HOOKS = [
'portalAdded', 'linkAdded', 'fieldAdded',
'publicChatDataAvailable', 'factionChatDataAvailable',
'requestFinished', 'nicknameClicked',
'geoSearch', 'iitcLoaded',
'geoSearch', 'search', 'iitcLoaded',
'portalDetailLoaded', 'paneChanged'];
window.runHooks = function(event, data) {

267
code/search.js Normal file
View File

@ -0,0 +1,267 @@
// SEARCH /////////////////////////////////////////////////////////
/*
you can implement your own result provider by listing to the search hook:
addHook('search', function(query) {});
`query` is an object with the following members:
- `term` is the term for which the user has searched
- `confirmed` is a boolean indicating if the user has pressed enter after searching. You should not search online or
heavy processing unless the user has confirmed the search term
- `addResult(result)` can be called to add a result to the query.
`result` may have the following members (`title` is required, as well as one of `position` and `bounds`):
- `title`: the label for this result
- `description`: secondary information for this result
- `position`: a L.LatLng object describing the position of this result
- `bounds`: a L.LatLngBounds object describing the bounds of this result
- `layer`: a ILayer to be added to the map when the user selects this search result. Will be generated if not set.
Set to `null` to prevent the result from being added to the map.
- `onSelected(result, event)`: a handler to be called when the result is selected. May return `true` to prevent the map
from being repositioned. You may reposition the map yourself or do other work.
- `onRemove(result)`: a handler to be called when the result is removed from the map (because another result has been
selected or the search was cancelled by the user).
*/
window.search = {
lastSearch: null,
};
window.search.Query = function(term, confirmed) {
this.term = term;
this.confirmed = confirmed;
this.init();
};
window.search.Query.prototype.init = function() {
this.results = [];
this.container = $('<div>');
this.header = $('<h3>')
.text(this.confirmed
? this.term
: ((this.term.length > 16
? this.term.substr(0,8) + "…" + this.term.substr(this.term.length-8,8)
: this.term)
+ ' (Return to load more)'))
.appendTo(this.container);
this.list = $('<ul>')
.appendTo(this.container)
.append($('<li>').text('No results'));
this.container.accordion({
collapsible: true,
heightStyle: 'content',
});
runHooks('search', this);
};
window.search.Query.prototype.show = function() {
this.container.appendTo('#searchwrapper');
};
window.search.Query.prototype.hide = function() {
this.container.remove();
this.removeSelectedResult();
};
window.search.Query.prototype.addResult = function(result) {
if(this.results.length == 0) {
// remove 'No results'
this.list.empty();
}
this.results.push(result);
var item = $('<li>')
.appendTo(this.list)
.attr('tabindex', '0')
.on("click dblclick", function(ev) {
this.onResultSelected(result, ev);
}.bind(this))
.keypress(function(ev) {
if((ev.keyCode || ev.charCode || ev.which) == 32) {
ev.preventDefault();
ev.type = 'click';
$(this).trigger(ev);
return;
}
if((ev.keyCode || ev.charCode || ev.which) == 13) {
ev.preventDefault();
ev.type = 'dblclick';
$(this).trigger(ev);
return;
}
});
$('<a>')
.text(result.title)
.appendTo(item);
if(result.description) {
item
.append($('<br>'))
.append($('<em>')
.text(result.description));
}
};
window.search.Query.prototype.onResultSelected = function(result, ev) {
this.removeSelectedResult();
this.selectedResult = result;
if(result.onSelected) {
if(result.onSelected(result, ev)) return;
}
if(ev.type == 'dblclick') {
if(result.position) {
map.setView(result.position, 17);
} else if(result.bounds) {
map.fitBounds(result.bounds, {maxZoom: 17});
}
} else { // ev.type != 'dblclick'
if(result.bounds) {
map.fitBounds(result.bounds, {maxZoom: 17});
} else if(result.position) {
map.setView(result.position);
}
}
if(result.layer !== null && !result.layer) {
result.layer = L.layerGroup();
if(result.position) {
var markerTemplate = '@@INCLUDESTRING:images/marker-icon.svg.template@@';
L.marker(result.position, {
icon: L.divIcon({
iconSize: new L.Point(25, 41),
iconAnchor: new L.Point(12, 41),
html: markerTemplate.replace(/%COLOR%/g, "red"),
className: 'leaflet-iitc-search-result-icon',
}),
title: result.title,
}).addTo(result.layer);
}
if(result.bounds) {
L.rectangle(result.bounds, {
title: result.title,
clickable: false,
color: 'red',
fill: false,
}).addTo(result.layer);
}
}
if(result.layer)
map.addLayer(result.layer);
if(window.isSmartphone()) window.show('map');
}
window.search.Query.prototype.removeSelectedResult = function() {
if(this.selectedResult) {
if(this.selectedResult.layer) map.removeLayer(this.selectedResult.layer);
if(this.selectedResult.onRemove) this.selectedResult.onRemove(this.selectedResult);
}
}
window.search.doSearch = function(term, confirmed) {
term = term.trim();
// minimum 3 characters for automatic search
if(term.length < 3 && !confirmed) return;
// don't clear last confirmed search
if(window.search.lastSearch
&& window.search.lastSearch.confirmed
&& !confirmed)
return;
// don't make the same query again
if(window.search.lastSearch
&& window.search.lastSearch.confirmed == confirmed
&& window.search.lastSearch.term == term)
return;
if(window.search.lastSearch) window.search.lastSearch.hide();
// clear results
if(term == "") return;
$("#search").tooltip().tooltip("close");
window.search.lastSearch = new window.search.Query(term, confirmed);
window.search.lastSearch.show();
};
window.search.setup = function() {
$('#search')
.keypress(function(e) {
if((e.keyCode ? e.keyCode : e.which) != 13) return;
e.preventDefault();
var term = $(this).val();
clearTimeout(window.search.timer);
window.search.doSearch(term, true);
})
.on('keyup keypress change paste', function(e) {
clearTimeout(window.search.timer);
window.search.timer = setTimeout(function() {
var term = $(this).val();
window.search.doSearch(term, false);
}.bind(this), 500);
});
$('#buttongeolocation').click(function(){
map.locate({setView : true, maxZoom: 13});
});
};
addHook('search', function(query) {
var term = query.term.toLowerCase();
$.each(portals, function(guid, portal) {
if(portal.options.data.title.toLowerCase().indexOf(term) !== -1) {
query.addResult({
title: portal.options.data.title,
position: portal.getLatLng(),
});
}
});
});
// TODO: recognize 50°31'03.8"N 7°59'05.3"E and similar formats
addHook('search', function(query) {
if(query.term.split(",").length == 2) {
var ll = query.term.split(",");
if(!isNaN(ll[0]) && !isNaN(ll[1])) {
query.addResult({
title: query.term,
position: L.latLng(parseFloat(ll[0]), parseFloat(ll[1])),
});
}
}
});
addHook('search', function(query) {
if(!query.confirmed) return;
$.getJSON(NOMINATIM + encodeURIComponent(query.term), function(data) {
data.forEach(function(item) {
var result = {
title: item.display_name,
description: 'Type: ' + item.type,
position: L.latLng(parseFloat(item.lat), parseFloat(item.lon)),
};
var b = item.boundingbox;
if(b) {
var southWest = new L.LatLng(b[0], b[2]),
northEast = new L.LatLng(b[1], b[3]);
result.bounds = new L.LatLngBounds(southWest, northEast);
}
query.addResult(result);
});
});
});

View File

@ -91,9 +91,9 @@ document.getElementsByTagName('body')[0].innerHTML = ''
+ ' <div id="sidebar" style="display: none">'
+ ' <div id="playerstat">t</div>'
+ ' <div id="gamestat">&nbsp;loading global control stats</div>'
+ ' <div id="geosearchwrapper">'
+ ' <input id="geosearch" placeholder="Search location…" type="text" accesskey="f" title="Search for a place [f]"/>'
+ ' <img src="@@INCLUDEIMAGE:images/current-location.png@@"/ title="Current Location">'
+ ' <div id="searchwrapper">'
+ ' <img src="@@INCLUDEIMAGE:images/current-location.png@@"/ title="Current Location" id="buttongeolocation">'
+ ' <input id="search" placeholder="Search location…" type="search" accesskey="f" title="Search for a place [f]"/>'
+ ' </div>'
+ ' <div id="portaldetails"></div>'
+ ' <input id="redeem" placeholder="Redeem code…" type="text"/>'
@ -167,7 +167,7 @@ window.PORTAL_RADIUS_ENLARGE_MOBILE = 5;
window.DEFAULT_PORTAL_IMG = '//commondatastorage.googleapis.com/ingress.com/img/default-portal-image.png';
//window.NOMINATIM = '//nominatim.openstreetmap.org/search?format=json&limit=1&q=';
window.NOMINATIM = '//open.mapquestapi.com/nominatim/v1/search.php?format=json&limit=1&q=';
window.NOMINATIM = '//open.mapquestapi.com/nominatim/v1/search.php?format=json&q=';
// INGRESS CONSTANTS /////////////////////////////////////////////////
// http://decodeingress.me/2012/11/18/ingress-portal-levels-and-link-range/

View File

@ -485,7 +485,7 @@ h2 sup, h2 sub {
}
/* geosearch input, and others */
/* search input, and others */
input:not([type]), .input,
input[type="text"], input[type="password"],
input[type="number"], input[type="email"],
@ -502,23 +502,47 @@ input[type="search"], input[type="url"] {
box-sizing: border-box;
}
#geosearch{
width:272px;
background-color: transparent;
#searchwrapper {
position: relative;
}
#geosearchwrapper {
height:25px;
background-color: rgba(0, 0, 0, 0.3);
#search {
width: 100%;
padding-right: 24px;
}
#geosearchwrapper img{
vertical-align: bottom;
margin-bottom: 2px;
#buttongeolocation {
position: absolute;
top: 2px;
right: 2px;
cursor: pointer;
}
.search_not_found{
color:red;
font-style: italic;
#searchwrapper h3 {
font-size: 1em;
height: auto;
cursor: pointer;
}
#searchwrapper .ui-accordion-header::before {
font-size: 18px;
margin-right: 2px;
font-weight: normal;
line-height: 1em;
content: "⊞";
}
#searchwrapper .ui-accordion-header-active::before {
content: "⊟";
}
#searchwrapper .ui-accordion-content {
margin: 0;
overflow: hidden;
}
#searchwrapper ul {
padding-left: 14px;
}
#searchwrapper li {
cursor: pointer;
}
#searchwrapper li em {
color: #ccc;
font-size: 0.9em;
}
::-webkit-input-placeholder {