332 lines
11 KiB
JavaScript
332 lines
11 KiB
JavaScript
|
/* global Metamaps, $, Hogan, Bloodhound, CanvasLoader */
|
||
|
|
||
|
import Active from '../Active'
|
||
|
import Router from '../Router'
|
||
|
|
||
|
/*
|
||
|
* Metamaps.Erb
|
||
|
* Metamaps.Maps
|
||
|
*/
|
||
|
|
||
|
const Search = {
|
||
|
locked: false,
|
||
|
isOpen: false,
|
||
|
limitTopicsToMe: false,
|
||
|
limitMapsToMe: false,
|
||
|
timeOut: null,
|
||
|
changing: false,
|
||
|
optionsInitialized: false,
|
||
|
init: function () {
|
||
|
var self = Search
|
||
|
|
||
|
// TODO does this overlap with Metamaps.Loading?
|
||
|
// devin sez: I'd like to remove Metamaps.Loading from the rails code
|
||
|
var loader = new CanvasLoader('searchLoading')
|
||
|
loader.setColor('#4fb5c0') // default is '#000000'
|
||
|
loader.setDiameter(24) // default is 40
|
||
|
loader.setDensity(41) // default is 40
|
||
|
loader.setRange(0.9) // default is 1.3
|
||
|
loader.show() // Hidden by default
|
||
|
|
||
|
// bind the hover events
|
||
|
$('.sidebarSearch').hover(function () {
|
||
|
self.open()
|
||
|
}, function () {
|
||
|
self.close(800, false)
|
||
|
})
|
||
|
|
||
|
$('.sidebarSearchIcon').click(function (e) {
|
||
|
$('.sidebarSearchField').focus()
|
||
|
})
|
||
|
$('.sidebarSearch').click(function (e) {
|
||
|
e.stopPropagation()
|
||
|
})
|
||
|
$('body').click(function (e) {
|
||
|
self.close(0, false)
|
||
|
})
|
||
|
|
||
|
// open if the search is closed and user hits ctrl+/
|
||
|
// close if they hit ESC
|
||
|
$('body').bind('keyup', function (e) {
|
||
|
switch (e.which) {
|
||
|
case 191:
|
||
|
if ((e.ctrlKey && !self.isOpen) || (e.ctrlKey && self.locked)) {
|
||
|
self.open(true) // true for focus
|
||
|
}
|
||
|
break
|
||
|
case 27:
|
||
|
if (self.isOpen) {
|
||
|
self.close(0, true)
|
||
|
}
|
||
|
break
|
||
|
|
||
|
default:
|
||
|
break // console.log(e.which)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
self.startTypeahead()
|
||
|
},
|
||
|
lock: function () {
|
||
|
var self = Search
|
||
|
self.locked = true
|
||
|
},
|
||
|
unlock: function () {
|
||
|
var self = Search
|
||
|
self.locked = false
|
||
|
},
|
||
|
open: function (focus) {
|
||
|
var self = Search
|
||
|
|
||
|
clearTimeout(self.timeOut)
|
||
|
if (!self.isOpen && !self.changing && !self.locked) {
|
||
|
self.changing = true
|
||
|
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
|
||
|
width: '400px'
|
||
|
}, 300, function () {
|
||
|
if (focus) $('.sidebarSearchField').focus()
|
||
|
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
|
||
|
padding: '7px 10px 3px 10px',
|
||
|
width: '380px'
|
||
|
})
|
||
|
self.changing = false
|
||
|
self.isOpen = true
|
||
|
})
|
||
|
}
|
||
|
},
|
||
|
close: function (closeAfter, bypass) {
|
||
|
// for now
|
||
|
return
|
||
|
|
||
|
var self = Search
|
||
|
|
||
|
self.timeOut = setTimeout(function () {
|
||
|
if (!self.locked && !self.changing && self.isOpen && (bypass || $('.sidebarSearchField.tt-input').val() === '')) {
|
||
|
self.changing = true
|
||
|
$('.sidebarSearchField, .sidebarSearch .tt-hint').css({
|
||
|
padding: '7px 0 3px 0',
|
||
|
width: '400px'
|
||
|
})
|
||
|
$('.sidebarSearch .twitter-typeahead, .sidebarSearch .tt-hint, .sidebarSearchField').animate({
|
||
|
width: '0'
|
||
|
}, 300, function () {
|
||
|
$('.sidebarSearchField').typeahead('val', '')
|
||
|
$('.sidebarSearchField').blur()
|
||
|
self.changing = false
|
||
|
self.isOpen = false
|
||
|
})
|
||
|
}
|
||
|
}, closeAfter)
|
||
|
},
|
||
|
startTypeahead: function () {
|
||
|
var self = Search
|
||
|
|
||
|
var mapheader = Active.Mapper ? '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><input type="checkbox" class="limitToMe" id="limitMapsToMe"></input><label for="limitMapsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>' : '<div class="searchMapsHeader searchHeader"><h3 class="search-heading">Maps</h3><div class="minimizeResults minimizeMapResults"></div><div class="clearfloat"></div></div>'
|
||
|
var topicheader = Active.Mapper ? '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><input type="checkbox" class="limitToMe" id="limitTopicsToMe"></input><label for="limitTopicsToMe" class="limitToMeLabel">added by me</label><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>' : '<div class="searchTopicsHeader searchHeader"><h3 class="search-heading">Topics</h3><div class="minimizeResults minimizeTopicResults"></div><div class="clearfloat"></div></div>'
|
||
|
var mapperheader = '<div class="searchMappersHeader searchHeader"><h3 class="search-heading">Mappers</h3><div class="minimizeResults minimizeMapperResults"></div><div class="clearfloat"></div></div>'
|
||
|
|
||
|
var topics = {
|
||
|
name: 'topics',
|
||
|
limit: 9999,
|
||
|
|
||
|
display: s => s.label,
|
||
|
templates: {
|
||
|
notFound: function (s) {
|
||
|
return Hogan.compile(topicheader + $('#topicSearchTemplate').html()).render({
|
||
|
value: 'No results',
|
||
|
label: 'No results',
|
||
|
typeImageURL: Metamaps.Erb['icons/wildcard.png'],
|
||
|
rtype: 'noresult'
|
||
|
})
|
||
|
},
|
||
|
header: topicheader,
|
||
|
suggestion: function (s) {
|
||
|
return Hogan.compile($('#topicSearchTemplate').html()).render(s)
|
||
|
}
|
||
|
},
|
||
|
source: new Bloodhound({
|
||
|
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||
|
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||
|
remote: {
|
||
|
url: '/search/topics',
|
||
|
prepare: function (query, settings) {
|
||
|
settings.url += '?term=' + query
|
||
|
if (Active.Mapper && self.limitTopicsToMe) {
|
||
|
settings.url += '&user=' + Active.Mapper.id.toString()
|
||
|
}
|
||
|
return settings
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
var maps = {
|
||
|
name: 'maps',
|
||
|
limit: 9999,
|
||
|
display: s => s.label,
|
||
|
templates: {
|
||
|
notFound: function (s) {
|
||
|
return Hogan.compile(mapheader + $('#mapSearchTemplate').html()).render({
|
||
|
value: 'No results',
|
||
|
label: 'No results',
|
||
|
rtype: 'noresult'
|
||
|
})
|
||
|
},
|
||
|
header: mapheader,
|
||
|
suggestion: function (s) {
|
||
|
return Hogan.compile($('#mapSearchTemplate').html()).render(s)
|
||
|
}
|
||
|
},
|
||
|
source: new Bloodhound({
|
||
|
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||
|
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||
|
remote: {
|
||
|
url: '/search/maps',
|
||
|
prepare: function (query, settings) {
|
||
|
settings.url += '?term=' + query
|
||
|
if (Active.Mapper && self.limitMapsToMe) {
|
||
|
settings.url += '&user=' + Active.Mapper.id.toString()
|
||
|
}
|
||
|
return settings
|
||
|
}
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
var mappers = {
|
||
|
name: 'mappers',
|
||
|
limit: 9999,
|
||
|
display: s => s.label,
|
||
|
templates: {
|
||
|
notFound: function (s) {
|
||
|
return Hogan.compile(mapperheader + $('#mapperSearchTemplate').html()).render({
|
||
|
value: 'No results',
|
||
|
label: 'No results',
|
||
|
rtype: 'noresult',
|
||
|
profile: Metamaps.Erb['user.png']
|
||
|
})
|
||
|
},
|
||
|
header: mapperheader,
|
||
|
suggestion: function (s) {
|
||
|
return Hogan.compile($('#mapperSearchTemplate').html()).render(s)
|
||
|
}
|
||
|
},
|
||
|
source: new Bloodhound({
|
||
|
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||
|
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||
|
remote: {
|
||
|
url: '/search/mappers?term=%QUERY',
|
||
|
wildcard: '%QUERY'
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
// Take all that crazy setup data and put it together into one beautiful typeahead call!
|
||
|
$('.sidebarSearchField').typeahead(
|
||
|
{
|
||
|
highlight: true
|
||
|
},
|
||
|
[topics, maps, mappers]
|
||
|
)
|
||
|
|
||
|
// Set max height of the search results box to prevent it from covering bottom left footer
|
||
|
$('.sidebarSearchField').bind('typeahead:render', function (event) {
|
||
|
self.initSearchOptions()
|
||
|
self.hideLoader()
|
||
|
var h = $(window).height()
|
||
|
$('.tt-dropdown-menu').css('max-height', h - 100)
|
||
|
if (self.limitTopicsToMe) {
|
||
|
$('#limitTopicsToMe').prop('checked', true)
|
||
|
}
|
||
|
if (self.limitMapsToMe) {
|
||
|
$('#limitMapsToMe').prop('checked', true)
|
||
|
}
|
||
|
})
|
||
|
$(window).resize(function () {
|
||
|
var h = $(window).height()
|
||
|
$('.tt-dropdown-menu').css('max-height', h - 100)
|
||
|
})
|
||
|
|
||
|
// tell the autocomplete to launch a new tab with the topic, map, or mapper you clicked on
|
||
|
$('.sidebarSearchField').bind('typeahead:select', self.handleResultClick)
|
||
|
|
||
|
// don't do it, if they clicked on a 'addToMap' button
|
||
|
$('.sidebarSearch button.addToMap').click(function (event) {
|
||
|
event.stopPropagation()
|
||
|
})
|
||
|
|
||
|
// make sure that when you click on 'limit to me' or 'toggle section' it works
|
||
|
$('.sidebarSearchField.tt-input').keyup(function () {
|
||
|
if ($('.sidebarSearchField.tt-input').val() === '') {
|
||
|
self.hideLoader()
|
||
|
} else {
|
||
|
self.showLoader()
|
||
|
}
|
||
|
})
|
||
|
},
|
||
|
handleResultClick: function (event, datum, dataset) {
|
||
|
var self = Search
|
||
|
|
||
|
self.hideLoader()
|
||
|
|
||
|
if (['topic', 'map', 'mapper'].indexOf(datum.rtype) !== -1) {
|
||
|
self.close(0, true)
|
||
|
if (datum.rtype === 'topic') {
|
||
|
Router.topics(datum.id)
|
||
|
} else if (datum.rtype === 'map') {
|
||
|
Router.maps(datum.id)
|
||
|
} else if (datum.rtype === 'mapper') {
|
||
|
Router.explore('mapper', datum.id)
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
initSearchOptions: function () {
|
||
|
var self = Search
|
||
|
|
||
|
function toggleResultSet (set) {
|
||
|
var s = $('.tt-dataset-' + set + ' .tt-suggestion, .tt-dataset-' + set + ' .resultnoresult')
|
||
|
if (s.is(':visible')) {
|
||
|
s.hide()
|
||
|
$(this).removeClass('minimizeResults').addClass('maximizeResults')
|
||
|
} else {
|
||
|
s.show()
|
||
|
$(this).removeClass('maximizeResults').addClass('minimizeResults')
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$('.limitToMe').unbind().bind('change', function (e) {
|
||
|
if ($(this).attr('id') === 'limitTopicsToMe') {
|
||
|
self.limitTopicsToMe = !self.limitTopicsToMe
|
||
|
}
|
||
|
if ($(this).attr('id') === 'limitMapsToMe') {
|
||
|
self.limitMapsToMe = !self.limitMapsToMe
|
||
|
}
|
||
|
|
||
|
// set the value of the search equal to itself to retrigger the
|
||
|
// autocomplete event
|
||
|
var searchQuery = $('.sidebarSearchField.tt-input').val()
|
||
|
$('.sidebarSearchField').typeahead('val', '')
|
||
|
.typeahead('val', searchQuery)
|
||
|
})
|
||
|
|
||
|
// when the user clicks minimize section, hide the results for that section
|
||
|
$('.minimizeMapperResults').unbind().click(function (e) {
|
||
|
toggleResultSet.call(this, 'mappers')
|
||
|
})
|
||
|
$('.minimizeTopicResults').unbind().click(function (e) {
|
||
|
toggleResultSet.call(this, 'topics')
|
||
|
})
|
||
|
$('.minimizeMapResults').unbind().click(function (e) {
|
||
|
toggleResultSet.call(this, 'maps')
|
||
|
})
|
||
|
},
|
||
|
hideLoader: function () {
|
||
|
$('#searchLoading').hide()
|
||
|
},
|
||
|
showLoader: function () {
|
||
|
$('#searchLoading').show()
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export default Search
|