/**
* @module nyc/ol/FinderApp
*/
import $ from 'jquery'
import nyc from 'nyc'
import MapMgr from 'nyc/ol/MapMgr'
import Dialog from 'nyc/Dialog'
import Share from 'nyc/Share'
import Tabs from 'nyc/Tabs'
import Directions from 'nyc/Directions'
import Translate from 'nyc/lang/Translate'
import Goog from 'nyc/lang/Goog'
import Filters from 'nyc/ol/Filters'
/**
* @desc A class that provides a template for creating basic finder apps
* @public
* @class
*/
class FinderApp extends MapMgr {
/**
* @desc Create an instance of FinderApp
* @public
* @constructor
* @param {module:nyc/ol/FinderApp~FinderApp.Options} options Constructor options
*/
constructor(options) {
$('body').append(FinderApp.HTML).addClass('fnd')
options.listTarget = '#facilities div'
options.mapTarget = '#map'
options.mouseWheelZoom = options.mouseWheelZoom === undefined ? true : options.mouseWheelZoom
super(options)
global.finderApp = this
this.pager.find('h2.info').addClass('screen-reader-only')
const title = $('<div></div>').html(options.title)
nyc.noSpaceBarScroll()
$('#banner').html(title)
$('#home').attr('title', title.text())
$('.fnd #home').on('click', () => document.location.reload())
/**
* @desc The filters for filtering the facilities
* @public
* @member {module:nyc/ol/Filters~Filters}
*/
this.filters = this.createFilters(options.filterChoiceOptions)
/**
* @desc The tabs containing the map, facilities and filters
* @public
* @member {module:nyc/Tabs~Tabs}
*/
this.tabs = this.createTabs(options)
$('#map').attr('tabindex', -1)
this.adjustTabs()
this.showSplash(options.splashOptions)
new Share({target: '#map'})
new Goog({
target: '#map',
languages: options.languages || Translate.DEFAULT_LANGUAGES,
button: true
})
/**
* @private
* @member {module:nyc/Directions~Directions}
*/
this.directions = null
/**
* @private
* @member {string}
*/
this.directionsUrl = options.directionsUrl
}
/**
* @desc Reset the facilities list
* @public
* @method
* @param {Object} event Event object
*/
resetList(event) {
super.resetList(event)
if (event instanceof Filters) {
$('#tabs .btns .btn-2').addClass('filtered')
}
}
/**
* @public
* @override
* @method
* @param {module:nyc/ol/FinderApp~FinderApp.Options} options Constructor options
* @return {ol.format.Feature} The parent format
*/
createParentFormat(options) {
return options.facilityFormat.parentFormat || options.facilityFormat
}
/**
* @public
* @override
* @method
* @param {module:nyc/ol/FinderApp~FinderApp.Options} options Constructor options
* @param {ol.format.Feature} format The OpenLayers feature format
* @return {Array<Object<string, fuction>>} Decorations
*/
createDecorations(options) {
const format = options.facilityFormat
let decorations = [MapMgr.FEATURE_DECORATIONS, {app: this}]
if (format.parentFormat && format.parentFormat.decorations) {
decorations = decorations.concat(format.parentFormat.decorations)
}
if (format.decorations) {
decorations = decorations.concat(format.decorations)
}
if (options.decorations) {
decorations = decorations.concat(options.decorations)
}
return decorations
}
/**
* @desc Centers and zooms to the provided feature
* @public
* @method
* @param {ol.Feature} feature OpenLayers feature
*/
zoomTo(feature) {
super.zoomTo(feature)
if ($('#tabs .btns h2:first-of-type').css('display') !== 'none') {
this.tabs.open('#map')
}
}
/**
* @desc Handles geocoded and geolocated events
* @access protected
* @method
* @param {module:nyc/Locator~Locator.Result} location Location
*/
located(location) {
super.located(location)
this.focusFacilities()
}
/**
* @desc Provides directions to the provided facility feature
* @public
* @override
* @method
* @param {ol.Feature} feature OpenLayers feature
*/
directionsTo(feature) {
this.directions = this.directions || new Directions({
url: this.directionsUrl,
toggle: '#tabs'
})
const to = feature.getFullAddress()
const name = feature.getName()
const from = this.getFromAddr()
this.directions.directions({
from: from,
to: to,
facility: name,
origin: this.location,
destination: {
name: feature.getName(),
coordinate: feature.getGeometry().getCoordinates()
}
})
}
/**
* @desc Creates the filters for the facility features
* @access protected
* @method
* @param {Array<module:nyc/ol/Filters~Filters.ChoiceOptions>=} choiceOptions Choice options
* @return {module:nyc/ol/Filters~Filters} Filters
*/
createFilters(choiceOptions) {
if (choiceOptions) {
$('#filters').empty()
const apply = $('<button class="screen-reader-only">Apply</button>')
const filters = new Filters({
target: '#filters',
source: this.source,
choiceOptions: choiceOptions
})
filters.on('change', this.resetList, this)
$('#filters').append(apply)
apply.click($.proxy(this.focusFacilities, this))
return filters
}
}
/**
* @desc Creates the tabs for the map, facilities and filters
* @access protected
* @method
* @param {module:nyc/ol/FinderApp~FinderApp.Options} options Options
* @return {module:nyc/Tabs~Tabs} Tabs
*/
createTabs(options) {
const pages = [
{tab: '#map', title: 'Map'},
{tab: '#facilities', title: options.facilityTabTitle || 'Facilities'}
]
if (options.splashOptions) {
$('#tabs').attr('aria-hidden', true)
}
if (this.filters) {
pages.push({tab: '#filters', title: options.filterTabTitle || 'Filters'})
}
const tabs = new Tabs({target: '#tabs', tabs: pages})
tabs.on('change', this.tabChange, this)
$(window).resize($.proxy(this.adjustTabs, this))
return tabs
}
/**
* @private
* @method
*/
focusFacilities() {
this.tabs.open('#facilities')
$('h2.info.screen-reader-only').focus()
}
/**
* @private
* @method
* @param {module:nyc/Dialog~Dialog.Options} options Dialog options
*/
showSplash(options) {
const input = this.locationMgr.search.input
if (options) {
options.buttonText = options.buttonText || ['Continue']
new Dialog({css: 'splash'}).ok(options).then(() => {
$('#tabs').attr('aria-hidden', false)
input.focus()
})
} else {
input.focus()
}
}
/**
* @private
* @method
* @return {boolean} True if the tabs fill the screen
*/
tabsFillScreen() {
return Math.abs(this.tabs.getContainer().width() - $(window).width()) < 1
}
/**
* @private
* @method
*/
adjustTabs() {
/*
* when input gets focus screen resizes on android
* causing input to lose focus when tabs are adjusted
* so we don't adjust tabs when input has focus
*/
if ($('#directions').css('display') !== 'block' && !nyc.activeElement().isTextInput) {
this.tabs.open(this.tabsFillScreen() ? '#map' : '#facilities')
}
}
/**
* @desc Handles the tab change event
* @access protected
* @method
* @param {module:nyc/Tabs~Tabs} tabs Tabs
*/
tabChange(tabs) {
if (!this.tabsFillScreen()) {
$('#map').attr('aria-hidden', false)
}
this.map.setSize([$('#map').width(), $('#map').height()])
}
}
/**
* @desc Constructor options for {@link module:nyc/ol/FinderApp~FinderApp}
* @public
* @typedef {Object}
* @property {string} title The app title
* @property {module:nyc/Dialog~Dialog.Options} splashOptions Content to display as a splash page
* @property {string} [facilityTabTitle=Facilities] Title for the facilites list
* @property {string} facilityUrl The URL for the facility features data
* @property {ol.format.Feature=} facilityFormat The format of the facilities data
* @property {ol.style.Style} facilityStyle The styling for the facilities layer
* @property {module:nyc/Search~Search.FeatureSearchOptions|boolean} [facilitySearch=false] Search options for feature searches or true to use default search options
* @property {string} [filterTabTitle=Filters] Title for the filters tab
* @property {Array<module:nyc/ol/Filters~Filters.ChoiceOptions>=} filterChoiceOptions Filter definitions
* @property {string} geoclientUrl The URL for the Geoclient geocoder with approriate keys
* @property {string} directionsUrl The URL for the Google directions API with approriate keys
* @property {boolean} [mouseWheelZoom=true] Allow mouse wheel map zooming
*/
FinderApp.Options
/**
* @private
* @const
* @type {string}
*/
FinderApp.HTML = '<h1 id="banner" role="banner"></h1>' +
'<a id="home" role="button" href="#">' +
'<span class="screen-reader-only">Reload page</span>' +
'<svg xmlns="http://www.w3.org/2000/svg" width="152" height="52">' +
'<g transform="translate(1.5,0)">' +
'<polygon points="15.5,1.2 3.1,1.2 0,4.3 0,47.7 3.1,50.8 15.5,50.8 18.6,47.7 18.6,35.3 34.1,50.8 46.6,50.8 49.7,47.7 49.7,4.3 46.6,1.2 34.1,1.2 31,4.3 31,16.7 "/>' +
'<polygon points="83.8,47.7 83.8,38.4 99.3,22.9 99.3,10.5 99.3,4.3 96.2,1.2 83.8,1.2 80.7,4.3 80.7,10.5 74.5,16.7 68.3,10.5 68.3,4.3 65.2,1.2 52.8,1.2 49.7,4.3 49.7,22.9 65.2,38.4 65.2,47.7 68.3,50.8 80.7,50.8 "/>' +
'<polygon points="145.9,29.1 130.4,29.1 130.4,32.2 118,32.2 118,19.8 130.4,19.8 130.4,22.9 145.9,22.9 149,19.8 149,10.5 139.7,1.2 108.6,1.2 99.3,10.5 99.3,41.5 108.6,50.8 139.7,50.8 149,41.5 149,32.2 "/>' +
'</g>' +
'</svg>' +
'</a>' +
'<div id="map"></div>' +
'<div id="tabs"></div>' +
'<div id="facilities"><div role="region"></div></div>' +
'<div id="filters"></div>'
export default FinderApp