text: // Manages search results view Google Map.
  import markers from 'app/util/map/markers.js.erb';
  import markerDataLoader from 'app/util/map/marker-data-loader';
  import MapDOM from 'app/util/map/google/MapDOM';
  import infoBoxManager from 'app/util/map/google/infobox-manager';
  
  const { InfoBox } = require("google-maps-infobox");
  const { OverlappingMarkerSpiderfier } = require('app/util/map/google/oms.min');
  
  // Module instance with map DOM and google.maps.Map instance references.
  var _mapDOM;
  
  // Whether the map is at its max size or not.
  var _atMaxSize = false;
  
  // The collective map bounds of the markers.
  var _markerBounds;
  
  // The spiderfier layer for handling overlapping markers.
  // See https://github.com/jawj/OverlappingMarkerSpiderfier
  var _spiderfier;
  
  // The marker the cursor is currently over.
  var _overMarker;
  
  // @param mapContainerSelector [String] DOM selector for the map container.
  // @param mapCanvasSelector [String] DOM selector for the map canvas.
  function init(mapContainerSelector, mapCanvasSelector) {
    _mapDOM = MapDOM.create(mapContainerSelector, mapCanvasSelector);
  }
  
  // Run all rendering methods for the map, markers, and info box.
  // Also register events for interactivity.
  function renderMapStack() {
    _mapDOM.render();
    _renderMarkers();
    infoBoxManager.render(_mapDOM, InfoBox);
    _registerMapEvents();
    infoBoxManager.registerInfoBoxEvents();
  }
  
  // Toggle size of map.
  function toggleSize() {
    _atMaxSize = !_atMaxSize;
    if (_atMaxSize)
      _mapDOM.canvas.classList.add('max');
    else
      _mapDOM.canvas.classList.remove('max');
  
    _updateMarkerSizes();
    _refresh();
  }
  
  // @param OverlappingMarkerSpiderfier [Object] Marker spiderfier plugin.
  function _renderMarkers() {
    var spiderfierOptions = {
      legWeight: 2,
      circleFootSeparation: 30,
      keepSpiderfied: true,
      nearbyDistance: 40
    };
  
    _spiderfier = new OverlappingMarkerSpiderfier(_mapDOM.map,
                                                  spiderfierOptions);
  
    // Initialize marker bounds.
    _markerBounds = new google.maps.LatLngBounds();
  
    // Loop over marker data and initialize each marker.
    var markerData = markerDataLoader.getData();
    var index = markerData.length - 1;
    var marker;
    while (index >= 0) {
      marker = _loadMarker(markerData[index--], _atMaxSize, _spiderfier);
    }
    _overMarker = marker;
  
    _refresh();
  }
  
  // Load a single map marker.
  // @returns [Object] A google.maps.Marker instance that was created,
  //   or null if there isn't a latitude and longitude in the data.
  function _loadMarker(markerData, atMaxSize, spiderfier) {
    if (!markerData.latitude && !markerData.longitude) return;
  
    var myLatLng = new google.maps.LatLng(markerData.latitude,
                                          markerData.longitude);
  
    var markerProxy = markers.create(markerData.kind);
    if (atMaxSize)
      markerProxy.turnOn(markerProxy.LARGE_ICON);
    else
      markerProxy.turnOn(markerProxy.SMALL_ICON);
  
    var markerOptions = {
      map: _mapDOM.map,
      position: myLatLng,
      icon: markerProxy.getIcon(),
      optimized: false,
      manager: markerProxy
    };
    var marker = new google.maps.Marker(markerOptions);
  
    spiderfier.addMarker(marker);
  
    var mainName = markerData.name;
    var orgName = markerData.org_name;
    var agency = '';
    if (orgName !== markerData.name)
      agency = '<h2>' + orgName + '</h2>';
  
    var content = "<div><div class='button-close'></div>" +
                  '<h1>' + mainName + '</h1>' + agency +
                  '<p>' + markerData.street_address + ', ' +
                  markerData.city + '</p>' + "<p><a href='/locations/" +
                  markerData.slug+(window.location.search) +
                  "'>View more details…</a></p></div>";
  
    _registerMarkerEvents(marker, content, spiderfier);
  
    _markerBounds.extend(myLatLng);
  
    return marker;
  }
  
  // Register events for map interactivity.
  function _registerMapEvents() {
  
    google.maps.event.addListener(_mapDOM.map, 'idle', _mapIdle);
    google.maps.event.addListener(_mapDOM.map, 'click', _mapClick);
  
    _mapDOM.canvas.addEventListener('touchstart', _mapTouch, false);
  }
  
  // Event handler for when the map is idle. This is used by the spiderfier
  // to style the map markers that will spiderfy when clicked.
  function _mapIdle() {
    _setAllIcons();
    google.maps.event.addListener(_mapDOM.map, 'zoom_changed', _mapZoomed);
    // Remove idle listeners as they aren't needed after the spiderfied markers
    // are styled for the first time.
    google.maps.event.clearListeners(_mapDOM.map, 'idle');
  }
  
  // Event handler for when the map is zoomed. This is used by the spiderfier
  // to handle style changes to the map markers that will spiderfy when clicked.
  function _mapZoomed() {
    _setAllIcons();
  }
  
  // Event handler for when the map is clicked. This is used to close
  // any currently open info box.
  function _mapClick() {
    infoBoxManager.turnOffInfoBoxStates();
    infoBoxManager.updateInfoBoxState(_overMarker, 0);
  }
  
  // Event handler for when a touch event occurs on the map, for
  // changing the interactivity to accommodate lack of mouseover/out events.
  function _mapTouch() {
    infoBoxManager.turnOn(infoBoxManager.STATE.IS_TOUCH);
    _mapDOM.canvas.removeEventListener('touchstart', _mapTouch, false);
  }
  
  // Run through the markers and set them to a spiderfied large or small
  // appearance based on the size of the map.
  function _setAllIcons() {
  
    // Style all markers.
    var markers = _spiderfier.getMarkers();
    var index = markers.length - 1;
    while(index >= 0) {
      _setIcon(markers[index--], false);
    }
  
    // Style spiderfier markers.
    markers = _spiderfier.markersNearAnyOtherMarker();
    index = markers.length - 1;
    while(index >= 0) {
      _setIcon(markers[index--], true);
    }
  }
  
  // Set the icon for a marker to the large or small regular
  // or spiderfied marker.
  // @param marker [Object] a map marker.
  // @param useSpiderfied [Boolean] true if the spiderfied marker should
  // be used, false otherwise.
  function _setIcon(marker, useSpiderfied) {
    var manager = marker.manager;
    if (useSpiderfied) {
      if (_atMaxSize)
        manager.turnOn(manager.LARGE_ICON | manager.SPIDERFIED_ICON);
      else
        manager.turnOn(manager.SMALL_ICON | manager.SPIDERFIED_ICON);
    }
    else {
      if (_atMaxSize)
        manager.turnOn(manager.LARGE_ICON | manager.UNSPIDERFIED_ICON);
      else
        manager.turnOn(manager.SMALL_ICON | manager.UNSPIDERFIED_ICON);
    }
    marker.setIcon(manager.getIcon());
  }
  
  // Updates the marker icons to the size set for the map.
  function _updateMarkerSizes() {
    var markers = _spiderfier.getMarkers();
    var index = markers.length - 1;
    var marker;
    while (index >= 0) {
      marker = markers[index--];
      marker.setIcon(marker.manager.getIcon());
    }
  }
  
  // Make info box events associated with a map marker.
  // @param marker [Object] The marker that triggered the opening of the
  // info box.
  // @param content [String] The text content of the info box.
  // @param spiderfier [Object] The spiderfier plugin instance.
  function _registerMarkerEvents(marker, content, spiderfier) {
  
    // Change marker icon appearances when the markers spiderfy.
    spiderfier.addListener('spiderfy',
                            function(spiderfied,
                                     unspiderfied) {
      infoBoxManager.turnOn(infoBoxManager.STATE.HAS_SPIDERFIED);
      var index = spiderfied.length - 1;
      while (index >= 0) {
        _setIcon(spiderfied[index--], false);
      }
    });
  
    // Change marker icon appearances when the markers unspiderfy.
    spiderfier.addListener('unspiderfy',
                            function(spiderfied,
                                     unspiderfied) {
      var index = spiderfied.length - 1;
      while (index >= 0) {
        _setIcon(spiderfied[index--], true);
      }
    });
  
    // Register the marker the cursor has rolled over.
    google.maps.event.addListener(marker, 'mouseover', function() {
      if (_overMarker !== marker)
        infoBoxManager.turnOff(infoBoxManager.STATE.PIN_INFOBOX);
      if (infoBoxManager.isOff(infoBoxManager.STATE.PIN_INFOBOX)) {
        _registerMarker(marker, content);
        infoBoxManager.updateInfoBoxState(_overMarker);
      }
    });
  
    // Unregister the marker the cursor rolled out of.
    google.maps.event.addListener(marker, 'mouseout', function() {
      infoBoxManager.turnOff(infoBoxManager.STATE.OVER_MARKER);
      infoBoxManager.turnOff(infoBoxManager.STATE.OVER_SPIDERFY_MARKER);
      infoBoxManager.updateInfoBoxState(_overMarker);
    });
  
    // When user clicks the marker, open the infoBox
    // and center the map on the marker,
    // unless the user clicked a marker that just spiderfied.
    google.maps.event.addListener(marker, 'click', function() {
  
      // Touch displays don't know they're over a marker till it's tapped,
      // so manually register the state as being over the marker in this case.
      if (infoBoxManager.isOn(infoBoxManager.STATE.IS_TOUCH))
        _registerMarker(marker, content);
  
      if (infoBoxManager.isOn(infoBoxManager.STATE.HAS_SPIDERFIED)) {
        infoBoxManager.turnOff(infoBoxManager.STATE.HAS_SPIDERFIED);
      }
      else if (infoBoxManager.isOn(infoBoxManager.STATE.PIN_INFOBOX)){
        infoBoxManager.turnOffInfoBoxStates();
        infoBoxManager.updateInfoBoxState(_overMarker, 0);
      }
      else {
        _mapDOM.map.panTo(marker.position);
        infoBoxManager.turnOn(infoBoxManager.STATE.PIN_INFOBOX);
        infoBoxManager.turnOn(infoBoxManager.STATE.OVER_MARKER);
        infoBoxManager.turnOff(infoBoxManager.STATE.OVER_SPIDERFY_MARKER);
        infoBoxManager.updateInfoBoxState(_overMarker, 0);
      }
    });
  }
  
  // Whether a map marker is a spiderfied marker.
  // @param marker [Object] a map marker.
  // @return [Boolean] true if the marker is spiderfied, false otherwise.
  function _isSpiderfyMarker(marker) {
    var manager = marker.manager;
    return manager.isOn(manager.SPIDERFIED_ICON);
  }
  
  // Register a marker as having been clicked.
  // @param marker [Object] The marker that was clicked.
  // @param content [String] The text content of the infobox for this marker.
  function _registerMarker(marker, content) {
    _overMarker = marker;
    if (_isSpiderfyMarker(marker))
      infoBoxManager.turnOn(infoBoxManager.STATE.OVER_SPIDERFY_MARKER);
    else
      infoBoxManager.turnOn(infoBoxManager.STATE.OVER_MARKER);
  
    infoBoxManager.turnOff(infoBoxManager.STATE.OVER_INFOBOX);
    infoBoxManager.turnOff(infoBoxManager.STATE.SHOW_INFOBOX);
    infoBoxManager.turnOff(infoBoxManager.STATE.PIN_INFOBOX);
  
    infoBoxManager.setContent(content);
  }
  
  // Triggers a resize event and refits the map to the bounds of the markers.
  function _refresh() {
    google.maps.event.trigger(_mapDOM.map, 'resize');
    _mapDOM.map.fitBounds(_markerBounds);
  }
  
  export default {
    init:init,
    renderMapStack:renderMapStack,
    toggleSize:toggleSize
  };
  