From 770efdb5e213b9adb1c38c0a68c5aaeab1c82e54 Mon Sep 17 00:00:00 2001 From: Anselm Bradford Date: Thu, 3 Jul 2014 18:59:15 -0700 Subject: [PATCH] Formatting updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removes clearMarkers method, which is only needed if the map results are updated via ajax (which isn’t currently used) - Removes the metadata and summary text variables, which were no longer used. - Adds additional code comments. - Moves info window styles from JS to CSS. - Configures info window states using a bit mask. - Configures jshint task to allow bitwise operations. --- .../result/result-map-manager.js.erb | 275 +++++++++++------- app/assets/javascripts/util/bitmask.js | 43 +++ app/assets/stylesheets/_base.scss | 68 ++++- lib/tasks/jshint.rake | 2 +- 4 files changed, 280 insertions(+), 108 deletions(-) create mode 100644 app/assets/javascripts/util/bitmask.js diff --git a/app/assets/javascripts/result/result-map-manager.js.erb b/app/assets/javascripts/result/result-map-manager.js.erb index db59a7917..baeb986af 100644 --- a/app/assets/javascripts/result/result-map-manager.js.erb +++ b/app/assets/javascripts/result/result-map-manager.js.erb @@ -1,37 +1,69 @@ //= depend_on_asset 'markers/human_services.png' -// manages results maps view -define(['domReady!', - 'async!https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false!callback'], function () { +// Manages results maps view. +define(['util/bitmask','domReady!', + 'async!https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false!callback'], function (bitmask) { 'use strict'; - require(['http://google-maps-utility-library-v3.googlecode.com/svn/tags/infobox/1.1.9/src/infobox.js'], + require(['https://google-maps-utility-library-v3.googlecode.com/svn/trunk/infobox/src/infobox_packed.js'], function() { // PRIVATE PROPERTIES - var _map; // the map div that the Google map loads into - var _mapCanvas; // the parent element of _map - var _mapViewControl; // the element that controls the expanding/contracting of the map - var _atMaxSize = false; // whether the map is at its max size or not + // The
element that the Google map loads into. + var _map; - var _markerData; // markers on the map - var _markersArray = []; // array for storing markers - var _markerBounds; // the bounds of the markers + // The parent HTML element of the map. + var _mapCanvas; - // constants for map button text content + // The element that controls the expanding/contracting of the map. + var _mapViewControl; + + // Whether the map is at its max size or not. + var _atMaxSize = false; + + // Parsed JSON object of map marker data (positioning, label, etc.). + var _markerData; + + // The collective map bounds of the markers. + var _markerBounds; + + // "Constants" for map button text content. var LARGER_MAP_TEXT = " Smaller map"; var SMALLER_MAP_TEXT = " Larger map"; - var DEFAULT_INFOWINDOW_DELAY = 200; - var _infoWindow; // info window to pop up on roll over - var _infoWindowSelected = false; // whether infowindow is showing or not. - var _overInfoWindow = false; - var _selectedMarker; // the marker that's currently clicked. + // "Constant" for delay when showing/hiding the marker info box. + var DEFAULT_infoBox_DELAY = 400; + + // The info box to pop up when rolling over a marker. + var _infoBox; + + // A bitmask instance for tracking the different states of the infobox. + var _infoBoxState; + + // The possible conditions that determine the infobox's behavior. + var SHOW_INFOBOX = 1; + var OVER_INFOBOX = 2; + var OVER_MARKER = 4; + var PIN_INFOBOX = 8; + + // The timer for delaying the info box display. + var _infoBoxDelay; + + // The marker that is currently clicked. + var _selectedMarker; + + // The marker the cursor is currently over. + var _overMarker; + + // The marker InfoBox content that is currently showing. + var _infoBoxContent; // PUBLIC METHODS function init() { + _infoBoxState = bitmask.create(); + + // Only check for result map if the page isn't showing the no search results view. var noResults = document.querySelector("#results-entries .no-results"); - // only check for result map if the page isn't showing no results if (!noResults) { var mapContainer = document.getElementById('map-view'); @@ -55,28 +87,21 @@ define(['domReady!', mapTypeId: google.maps.MapTypeId.ROADMAP }; + _map = new google.maps.Map(_mapCanvas, mapOptions); + var infoBoxOptions = { disableAutoPan: false, maxWidth: 0, - pixelOffset: new google.maps.Size(11, -17), + pixelOffset: new google.maps.Size(8, -10), zIndex: null, - boxStyle: { - backgroundColor: "white", - borderRadius: "4px", - border: "1px solid #bdc3c7", - padding: "3px 6px", - opacity: 0.95 - }, infoBoxClearance: new google.maps.Size(1, 1), isHidden: false, - closeBoxMargin: "5px 2px 2px 2px", - pane: "floatPane", + closeBoxURL: '', + pane: 'floatPane', enableEventPropagation: false }; - _map = new google.maps.Map(_mapCanvas, mapOptions); - - _infoWindow = new InfoBox(infoBoxOptions); + _infoBox = new InfoBox(infoBoxOptions); _mapViewControl.addEventListener('click', _mapViewControlClicked, false); @@ -90,9 +115,8 @@ define(['domReady!', } } - // map view control clicked - function _mapViewControlClicked(evt) - { + // Map view control was clicked. This control toggles the large and small maps. + function _mapViewControlClicked(evt) { if (_atMaxSize) { _mapCanvas.classList.remove('max'); @@ -110,43 +134,81 @@ define(['domReady!', evt.preventDefault(); } - // loads markers + // Loads the map markers. function _loadMarkers() { var locations = document.getElementById("map-locations"); if (locations) { + // Load the map marker data from the JSON map data embedded in the DOM. _markerData = JSON.parse(locations.innerHTML); - locations.parentNode.removeChild(locations); // remove script element - _markerBounds = new google.maps.LatLngBounds(); - _clearMarkers(); + // Remove the script element from the DOM + locations.parentNode.removeChild(locations); + _markerBounds = new google.maps.LatLngBounds(); - var dataLength = _markerData.length; - for(var m = 0; m=0) { - _loadMarker( _markerData[m] ); + marker = _loadMarker( _markerData[index--] ); } - var metadata = _markerData[dataLength-1]; - var summaryText = ""+metadata.count+" of "+metadata.total+" results located!"; - } - else - { - // no entries found - _clearMarkers(); + + _overMarker = marker; + + // Register events for info box interactivity. + google.maps.event.addListener(_infoBox, 'domready', function() { + var contentDiv = _mapCanvas.querySelector('.infoBox'); + var buttonClose = contentDiv.querySelector('.button-close'); + contentDiv.addEventListener('mousemove', _overInfoBoxHandler, false); + contentDiv.addEventListener('mouseleave', _leaveInfoBoxHandler, false); + buttonClose.addEventListener('mousedown', _closeInfoBoxHandler, false); + }); } } - // clears all markers - function _clearMarkers() - { - for (var i = 0; i < _markersArray.length; i++ ) { - _markersArray[i].setMap(null); + function _overInfoBoxHandler(evt) { + _infoBoxState.turnOn(OVER_INFOBOX); + _updateInfoBoxState(); + } + + function _leaveInfoBoxHandler(evt) { + _infoBoxState.turnOff(OVER_INFOBOX); + _updateInfoBoxState(); + } + + function _closeInfoBoxHandler(evt) { + _infoBoxState.turnOff(OVER_INFOBOX); + _infoBoxState.turnOff(SHOW_INFOBOX); + _infoBoxState.turnOff(PIN_INFOBOX); + _updateInfoBoxState(0); + } + + function _updateInfoBoxState(delay) { + + // Clear any transitions in progress. + if (_infoBoxDelay) clearTimeout(_infoBoxDelay); + + // If delay is not set use the default delay value. + var setDelay = delay !== undefined ? delay : DEFAULT_infoBox_DELAY; + + if ( _infoBoxState.isOn(OVER_MARKER) ) { + if ( _infoBoxState.isOff(SHOW_INFOBOX) ) { + _openInfoBox(setDelay); + } + else if (_infoBox.getContent() !== _infoBoxContent ) { + _openInfoBox(setDelay); + } + } + else if ( _infoBoxState.isOff(PIN_INFOBOX) && + _infoBoxState.isOff(OVER_INFOBOX) && + _infoBoxState.isOff(OVER_MARKER) ) { + _closeInfoBox(setDelay); } - _markersArray = []; } - // load a single marker + // Load a single map marker. + // @returns [Object] A google.maps.Marker instance that was created. function _loadMarker(markerData) { if (markerData['coordinates'] && markerData['coordinates'][0] && markerData['coordinates'][1]) @@ -164,86 +226,89 @@ define(['domReady!', optimized: false }); - _markersArray.push(marker); - var agency = markerData['agency'] ? "

"+markerData['agency']+"

" : ""; - var content = "

"+markerData['name']+"

"+agency+ - "

Click to view details.

"; + var content = "

"+markerData['name']+"

"+agency+ + "

View more details…

"; - _makeInfoWindowEvent(_map, _infoWindow, content, marker); + _makeInfoBoxEvent(marker, content); _markerBounds.extend(myLatlng); - } + + return marker; } - function _openInfoWindow(marker,content) + // Open the global info box after a delay. + // @param delay [Number] Delay in milliseconds before opening the info box. + // If not specified, the delay will be the DEFAULT_infoBox_DELAY value. + function _openInfoBox(delay) { - if (!_infoWindowSelected) { - setTimeout(function() { - _infoWindow.setContent(content); - _infoWindow.open(_map, marker); - }, DEFAULT_INFOWINDOW_DELAY); - } + _infoBoxDelay = setTimeout(function() { + _infoBox.setContent(_infoBoxContent); + _infoBox.open(_map, _selectedMarker); + _infoBoxState.turnOn(SHOW_INFOBOX); + }, delay); } - function _closeInfoWindow() + // Open the global info box after a delay. + // @param delay [Number] Delay in milliseconds before closing the info box. + // If not specified, the delay will be the DEFAULT_infoBox_DELAY value. + function _closeInfoBox(delay) { - setTimeout(function() { - if (!_infoWindowSelected && !_overInfoWindow) { - _infoWindow.close(); - } - }, DEFAULT_INFOWINDOW_DELAY); + _infoBoxDelay = setTimeout(function() { + _infoBox.close(); + _infoBoxState.turnOff(SHOW_INFOBOX); + }, delay); } - // make info window events associated with this marker - function _makeInfoWindowEvent(map, infowindow, content, marker) { - var contentDiv; - google.maps.event.addListener(_infoWindow, 'domready', function() { - contentDiv = _mapCanvas.querySelector('.infoBox'); - contentDiv.addEventListener('mousemove',function(evt){ _overInfoWindow = true; }); - contentDiv.addEventListener('mousedown',function(evt){ _infoWindowSelected = true; }); - contentDiv.addEventListener('mouseleave',function(evt){ _overInfoWindow = false; _closeInfoWindow(); }); + // 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. + function _makeInfoBoxEvent(marker, content) { + + // When user mouses over the marker, open the infoBox and update its contents. + google.maps.event.addListener(marker, 'mouseover', function() { + if (_overMarker !== marker) _infoBoxState.turnOff(PIN_INFOBOX); + _overMarker = marker; + _infoBoxState.turnOn(OVER_MARKER); + _infoBoxContent = content; + _selectedMarker = marker; + _updateInfoBoxState(); + }); + google.maps.event.addListener(marker, 'mouseout', function() { + _infoBoxState.turnOff(OVER_MARKER); + _updateInfoBoxState(); }); - // when user mouses over the marker, open the infoBox and update its contents - google.maps.event.addListener(marker, 'mouseover', function(evt) { _openInfoWindow(marker,content); }); - - google.maps.event.addListener(marker, 'mouseout', function() { _closeInfoWindow(); }); - - // when user clicks the marker, open the infoBox and center the map on the marker + // When user clicks the marker, open the infoBox and center the map on the marker google.maps.event.addListener(marker, 'click', function() { - if (_selectedMarker === marker) + if (_infoBoxState.isOn(PIN_INFOBOX)) { - _infoWindow.close(); - _infoWindowSelected = false; - _selectedMarker = undefined; + _infoBoxState.turnOff(SHOW_INFOBOX); + _infoBoxState.turnOff(PIN_INFOBOX); + _infoBoxState.turnOff(OVER_MARKER); + _updateInfoBoxState(0); } else { - - _infoWindow.open(map, marker) - _infoWindowSelected = true; - _selectedMarker = marker; - map.panTo(marker.position); + _map.panTo(marker.position); + _infoBoxState.turnOn(PIN_INFOBOX); + _infoBoxState.turnOn(OVER_MARKER); + _updateInfoBoxState(0); } }); - - google.maps.event.addListener(_infoWindow,'closeclick', function() { - _infoWindowSelected = false; - }) } // Triggers a resize event and refits the map to the bounds of the markers function _refresh() { google.maps.event.trigger(_map, "resize"); - if (_markersArray.length > 0) - _map.fitBounds(_markerBounds); + _map.fitBounds(_markerBounds); } - init(); // makes the module initialization self-executing once dependencies have loaded. + // Makes the module initialization self-executing once dependencies have loaded. + init(); }); }); diff --git a/app/assets/javascripts/util/bitmask.js b/app/assets/javascripts/util/bitmask.js new file mode 100644 index 000000000..cf8f3052d --- /dev/null +++ b/app/assets/javascripts/util/bitmask.js @@ -0,0 +1,43 @@ +// Used for a creating and managing a bitmask of binary settings. +// Bitmask bits can be flipped on or off to create a very compact +// list of toggleable settings that are stored in one integer. +define(function() { + 'use strict'; + + var _bitmask = 0; + + function create() + { + return new BitMask(); + } + + function BitMask() { + + function turnOn(state) { + _bitmask |= state; + } + + function turnOff(state) { + _bitmask &= ~state; + } + + function isOn(state) { + return (_bitmask & state); + } + + function isOff(state) { + return !(_bitmask & state); + } + + return { + turnOn:turnOn, + turnOff:turnOff, + isOn:isOn, + isOff:isOff + }; + } + + return { + create:create + }; +}); \ No newline at end of file diff --git a/app/assets/stylesheets/_base.scss b/app/assets/stylesheets/_base.scss index 5930bbd1f..744deecea 100644 --- a/app/assets/stylesheets/_base.scss +++ b/app/assets/stylesheets/_base.scss @@ -1796,10 +1796,74 @@ body { top: 0px !important; } border-bottom: 1px solid midtone($blue); .infoBox { - img + background-color: $white; + @include border-radius(0px 4px 4px 4px); + @include single-box-shadow(midtone($black,0.4), 0px, 0px, 5px); + border: 1px solid dark($blue); + padding: 3px 6px; + opacity: 0.95; + h1 + { + font-weight: bold; + font-size: $font_size_95; + margin-right: 20px; + } + h2 { - z-index:9000; + font-size: $font_size_95; + } + a + { + color: $greyscale_midtone; // IE fallback + color: rgba($black, .6); + cursor: pointer; + border-bottom: 1px solid $white; + @include inline-block(); + } + a:hover + { + color: dark($blue); + border-bottom: 1px solid $greyscale_midtone; + border-bottom: 1px solid rgba($black, .4); + text-decoration: none; } + + p + { + text-align: center; + margin-top: 4px; + border-top: 1px solid midtone($blue,0.5); + font-size: $font_size_90; + margin-bottom: 2px; + } + + .button-close + { + color: $greyscale_midtone; // IE Fallback + color: rgba($black, .5); + position: absolute; + right: 0px; + top: -10px; + font-size: $font_size_120; + font-family: $font_serif; + } + + .button-close:after, + .button-close::after + { + content: "×"; + } + + .button-close:hover + { + color: $black; + cursor: pointer; + } + } + + .infoBox:hover a + { + color: dark($blue); } } #map-canvas.max diff --git a/lib/tasks/jshint.rake b/lib/tasks/jshint.rake index 3a58acf34..6e1c36617 100644 --- a/lib/tasks/jshint.rake +++ b/lib/tasks/jshint.rake @@ -7,7 +7,7 @@ begin # Custom options are below, uncomment the line # above for the default set of options. t.options = { - :bitwise => true, + :bitwise => false, :curly => false, :eqeqeq => true, :forin => true,