app/javascript/src/maps-search.js

Summary

Maintainability
B
4 hrs
Test Coverage
import i18n from './i18n'

window.icare = window.icare || {}
const {
  icare
} = window

const routeColoursArray = ['#0000ff', '#ff0000', '#00ffff', '#ff00ff', '#ffff00']

const hexToRgba = function (color, alpha) {
  let h
  if (alpha == null) { alpha = 1 }
  if (color.charAt(0) === '#') { h = color.substring(1, 7) } else { h = color }
  return `rgba(${parseInt(h.substring(0, 2), 16)}, ${parseInt(h.substring(2, 4), 16)}, ${parseInt(h.substring(4, 6), 16)}, ${alpha})`
}

const indexItinerariesMapInit = function (id) {
  icare.map = icare.initGoogleMaps(id)

  icare.infoWindow = new google.maps.InfoWindow({
    maxWidth: 400,
    pixelOffset: {
      width: 0,
      height: -35
    }
  })
}

const drawPath = function (itinerary, strokeColor, strokeOpacity) {
  if (strokeColor == null) { strokeColor = '#0000FF' }
  if (strokeOpacity == null) { strokeOpacity = 0.45 }
  icare.latLngBounds.extend(new google.maps.LatLng(itinerary.start_location.lat, itinerary.start_location.lng))
  icare.latLngBounds.extend(new google.maps.LatLng(itinerary.end_location.lat, itinerary.end_location.lng))
  const overviewPath = google.maps.geometry.encoding.decodePath(itinerary.overview_polyline)
  if (!overviewPath) { return }
  const customMarker = new icare.CustomMarker(overviewPath[0], icare.map, {
    infoWindowContent: HandlebarsTemplates.gmaps_popup({
      user: {
        image: itinerary.user.image,
        name: itinerary.user.name
      },
      url: itinerary.url,
      content: itinerary.description
    }),
    type: 'user_profile_picture',
    image: itinerary.user.image
  }
  )
  google.maps.event.addListener(customMarker, 'click', function () {
    icare.infoWindow.setContent(customMarker.options.infoWindowContent)
    icare.infoWindow.setPosition(customMarker.position)
    icare.infoWindow.open(icare.map)
  })
  const directionsPath = new google.maps.Polyline({
    clickable: false,
    path: overviewPath,
    strokeColor,
    strokeOpacity,
    strokeWeight: 5,
    icons: [{
      icon: {
        path: google.maps.SymbolPath.CIRCLE
      },
      offset: '0%'
    },
    {
      icon: {
        path: google.maps.SymbolPath.CIRCLE
      },
      offet: '100%'
    }
    ]
  })
  directionsPath.setMap(icare.map)
  icare.customMarkers[itinerary.id] = customMarker
  icare.itineraries.push(directionsPath)
}

const clearItineraries = () => {
  if (icare.itineraries) {
    $(icare.itineraries).each(function () {
      this.setMap(null)
    })
  }

  if (icare.customMarkers) {
    for (const customMarker of Object.keys(icare.customMarkers)) {
      icare.customMarkers[customMarker].setMap(null)
    }
  }

  icare.itineraries = []
  icare.customMarkers = {}
}

const lookupPosition = function (searchField) {
  const deferred = $.Deferred()
  const geocoder = new google.maps.Geocoder()

  geocoder.geocode(
    { address: $(`#itineraries_search_${searchField}`).val() }
    , function (results, status) {
      if (status === google.maps.GeocoderStatus.OK) {
        const firstResult = results[0]
        return deferred.resolve({
          formatted_address: firstResult.formatted_address,
          lat: firstResult.geometry.location.lat(),
          lng: firstResult.geometry.location.lng()
        })
      } else {
        return deferred.reject(status)
      }
    })

  return deferred.promise()
}

const initItineraryIndex = function () {
  indexItinerariesMapInit('#index-itineraries-map')

  clearItineraries()

  $('#itineraries-search').on('click', function (e) {
    e.preventDefault()
    const validators = $('#new_itineraries_search')[0].ClientSideValidations.settings
    if (!$('#new_itineraries_search').isValid(validators)) { return }

    $('#map-error-j').hide()

    $.when(
      lookupPosition('from'),
      lookupPosition('to')
    ).then(function (fromResult, toResult) {
      $('#itineraries_search_from').val(fromResult.formatted_address)
      $('#itineraries_search_start_location_lat').val(fromResult.lat)
      $('#itineraries_search_start_location_lng').val(fromResult.lng)
      $('#itineraries_search_to').val(toResult.formatted_address)
      $('#itineraries_search_end_location_lat').val(toResult.lat)
      $('#itineraries_search_end_location_lng').val(toResult.lng)
      $('#new_itineraries_search').submit()
    })
  })

  $('#new_itineraries_search').on('keypress', function (e) {
    if (e && (e.keyCode === 13)) {
      e.preventDefault()
      $('#itineraries-search').click()
    }
  })

  $('#new_itineraries_search')
    .on('submit', evt => clearItineraries())
    .on('ajax:beforeSend', (evt, xhr, settings) => $('#itineraries-spinner-j').show())
    .on('ajax:complete', (evt, xhr, settings) => $('#itineraries-spinner-j').hide())
    .on('ajax:error', (evt, xhr, settings) => {
      $('#itineraries-thumbs-j').html(`<div class="col-12"><h3 class="error-text m-0">${i18n.t('javascript.an_error_occurred')}</h3></div>`)
    })
    .on('ajax:success', function (evt, data, status, xhr) {
      // FIXME: browser back calls ajax:success multiple times
      if (data.length === 0) {
        $('#itineraries-thumbs-j').html(`<div class="col-12"><h3 class="m-0">${i18n.t('javascript.no_itineraries_found')}</h3></div>`)
      } else {
        $('#itineraries-thumbs-j').html('')
        let index = 0
        icare.latLngBounds = new google.maps.LatLngBounds()
        $(data).each(function () {
          const color = routeColoursArray[index++ % routeColoursArray.length]
          drawPath(this, color)
          this.backgroundColor = hexToRgba(color, 0.45) // backgroundColor injection, waiting for proper @data support in handlebars
          $('#itineraries-thumbs-j').append(HandlebarsTemplates['itineraries/thumbnail'](this))
        })
        icare.map.fitBounds(icare.latLngBounds)
      }
    })

  $(document).on('click', '.show-itinerary-on-map', function (e) {
    e.preventDefault()
    google.maps.event.trigger(icare.customMarkers[$(this).closest('.itinerary-thumbnail').data('itineraryId')], 'click')
    $(window).scrollTop('#index-itineraries-map')
  })
}

$(document).on(window.initializeOnEvent, function () {
  if ((typeof google !== 'undefined' && google !== null) && ($('#index-itineraries-map')[0] != null) && !icare.map) {
    initItineraryIndex()
  }
})