(function () { "use strict"; angular.module("enrMapModule") .directive("enrMap", ["mapSelectedRegion", function (mapSelectedRegion) { function link(scope) { // When map is redrawn, reset the map. scope.$watch("mapGeo", function () { if (scope.mapGeo) { mapSelectedRegion.clearSelectedRegion(); scope.zoomOut(); } }); // When selected party changed, trigger a map redraw by binding to an identical copy of the geo data. scope.$on("SELECTED_PARTY_CHANGED", function (event, partyCode) { scope.mapGeo = angular.copy(scope.mapGeo) }); //Map clicked region cliecked event call binding the corresponding div after refresh if (((localStorage.getItem("GeometryObjProperties") != "null" && localStorage.getItem("GeometryObjProperties") != null && localStorage.getItem("GeometryObjProperties") != "undefined")) && ((localStorage.getItem("GeometryObjProperties").length > 0)) && ((localStorage.getItem("GeometryEventPath") != "null" && localStorage.getItem("GeometryEventPath") != null && localStorage.getItem("GeometryEventPath") != null && localStorage.getItem("GeometryEventPath") != "undefined")) && ((localStorage.getItem("GeometryEventPath").length > 0)) ) { scope.handleMapRegionClicked(JSON.parse(localStorage.getItem("GeometryObjProperties")), JSON.parse(localStorage.getItem("GeometryEventPath"))); } } return { scope: { mapGeo: "=", mapColors: "=", electionData: "=", referendumsMap: "=" }, controller: "enrMapController", templateUrl: "Scripts/EnrAngular/EnrMap/enrMap.html", link: link, restrict: "E" } }]) .controller("enrMapController", ["$scope", "$rootScope", "$filter", "$timeout", "topoJsonService", "d3Service", "mapSvgPathBuilder", "mapClassGenerator", "mapRegionLeaderCalculator", "$window", "enrModeManager", "jsonDataFetcher", "mapFillColorCalculator", "mapSelectedRegion", "detailsData", "regionPropertiesParser", "primaryService", "gradientService", "detailsViewModeManager", function ($scope, $rootScope, $filter, $timeout, topoJsonService, d3Service, mapSvgPathBuilder, mapClassGenerator, mapRegionLeaderCalculator, $window, enrModeManager, jsonDataFetcher, mapFillColorCalculator, mapSelectedRegion, detailsData, regionPropertiesParser, primaryService, gradientService, detailsViewModeManager) { var clickHandle = false; $scope.isMouseInZoomTargetModeE = false; $scope.$on("SLICK_OFFICE_SELECTION", function (event) { hideLegend(); }); $scope.electionType = primaryService.getElectionType(); // Respond to primary mode being set. $scope.$on("IS_PRIMARY_SET", function (event) { $scope.isPrimary = true; }); $scope.modelhidden = true; // Respond to selected primary party changing $scope.$on("SELECTED_PARTY_CHANGED", function (event, partyCode) { $scope.selectedPartyCode = partyCode; updateLegendCandidates(); hideLegend(); }); // Respond to selected office changing $scope.$on("SELECTED_OFFICE_CHANGED", function (event) { $scope.disableGradients = gradientService.isCurOfficeExcludedFromGradients(); $scope.electionType = primaryService.getElectionType(); hideLegend(); }); // Respond to data changes. $scope.$watch("electionData", function () { updateLegendCandidates(); }); $scope.$on("COUNTY_CHNAGED", function (event) { hideLegend(); }); // Offices/Referendums mode handling $scope.isOfficeMode = enrModeManager.getCurMode() === enrModeManager.modeEnum.offices; $scope.$on(enrModeManager.modeChangeEventName, function (event, newMode) { hideLegend(); $scope.isOfficeMode = newMode === enrModeManager.modeEnum.offices; if (newMode === enrModeManager.modeEnum.referendums) { jsonDataFetcher.fetchMultipleJson([$scope.referendumsMap || $rootScope.DefaultMap, "statewideElectionsR"], [false, true], [true, false], function (jsonResults) { var mapJson = jsonResults[0]; var electionData = jsonResults[1]; $scope.electionData = electionData; $scope.mapGeo = mapJson; }); } else { // enrOfficeSelection Directive Handles This (since it knows what item is currently picked, which will dictate the map data assigned) } }); $scope.$on(detailsViewModeManager.modeChangeEventName, function (event) { hideLegend(); }); // Region Drawing $scope.generateRegionClass = function (geometryObjProperties) { var regionId = regionPropertiesParser.getRegionPrimaryId(geometryObjProperties); var isRegionSelected = mapSelectedRegion.isRegionSelected(geometryObjProperties); return mapClassGenerator.generateRegionClass(regionId, isRegionSelected); }; $scope.regionFillColor = function (geometryObjProperties) { var fillColor = "#000000"; if (enrModeManager.isReferendumsMode()) fillColor = mapFillColorCalculator.calculateFillColorReferendums(geometryObjProperties, $scope.electionData, $scope.mapColors); else if (enrModeManager.isOfficeMode()) { fillColor = mapFillColorCalculator.calculateFillColorOffice(geometryObjProperties, $scope.electionData, $scope.mapColors, $rootScope.defaultOfficeSelection); } return fillColor; }; // Tooltips $scope.getRegionDisplayName = function (geometryObjProperties) { var jsonResult = primaryService.getMapDistrictData(); return regionPropertiesParser.getRegionDisplayName(geometryObjProperties, jsonResult); } //$scope.getRegionDisplayNumber = function (geometryObjProperties) { // var jsonResult = primaryService.getMapDistrictData(); // return regionPropertiesParser.getRegionDistrictNumber(geometryObjProperties, jsonResult); //} function addPathZoomEffect(currentPathElement) { var $this = $(currentPathElement); var bbox = currentPathElement.getBBox(); var centreX = bbox.x + bbox.width / 2; var centreY = bbox.y + bbox.height / 2; $this.css("transform-origin", centreX + 'px ' + centreY + 'px'); $this.css("transform", "scale(2)"); currentPathElement.parentElement.appendChild(currentPathElement); } function removePathZoomEffect(currentPathElement) { var $this = $(currentPathElement); $this.css("transform-origin", ""); $this.css("transform", ""); $this.css("stroke", ""); } $scope.setPopupPosition = function (event) { var modelHeight = $("#divCountyModel").height(); var offset = $(event.target).offset(); var targetHeight = event.target.getBoundingClientRect().height; //$(event.target).height(); if (modelHeight > offset.top) { $scope.modelTop = (offset.top + targetHeight) + "px"; } else { $scope.modelTop = (offset.top - modelHeight) + "px"; } $scope.modelLeft = offset.left + "px"; $rootScope.$broadcast("COUNTY_MOUSEOVER_POSITION", $scope.modelhidden, $scope.modelTop, $scope.modelLeft); } $scope.regionMouseOver = function (region, event, elmnt) { if ($rootScope.defaultOfficeSelection && enrModeManager.isOfficeMode()) { return; } var geometryObjProperties = region.properties $scope.pathData = region.pathData; var hoverRegionId = regionPropertiesParser.getRegionPrimaryId(geometryObjProperties); // Zoom if (isMouseInZoomTargetMode) { addPathZoomEffect(event.target); // Add Text element if (event.target) { var pathId = event.target.id; var textId = pathId.replace("path", "txt"); $("#" + textId).parent().append($("#" + textId)); } } else if (enrModeManager.isReferendumsMode()) { var refDataFile = "JurR_" + regionPropertiesParser.getRegionPrimaryId(geometryObjProperties, false, true); jsonDataFetcher.fetchSingleJson(refDataFile, true, false, function (referendumsJson) { detailsData.setRefData(referendumsJson); }); $rootScope.$broadcast("COUNTY_MOUSEOVER", geometryObjProperties, null); } $timeout(function () { $scope.modelhidden = false; $scope.setPopupPosition(event); }, 100); if (!enrModeManager.isReferendumsMode()) { $(".map-region." + hoverRegionId).tooltip("show"); $rootScope.$broadcast("COUNTY_MOUSEOVER", geometryObjProperties, hoverRegionId); } } $scope.makeUrl = function (idx) { return "#path" + idx; } $scope.regionMouseLeave = function (region, event) { if ($rootScope.defaultOfficeSelection && enrModeManager.isOfficeMode()) { return; } var geometryObjProperties = region.properties $scope.pathData = region.pathData; $scope.isMouseInZoomTargetModeE = isMouseInZoomTargetMode; if (isMouseInZoomTargetMode) { removePathZoomEffect(event.target); }; var hoverRegionId = regionPropertiesParser.getRegionPrimaryId(geometryObjProperties); $(".map-region." + hoverRegionId).tooltip("destroy"); $rootScope.$broadcast("COUNTY_MOUSEOVER", geometryObjProperties, hoverRegionId); //$scope.modelhidden = true; $timeout(modelHidden, 100); //$rootScope.$broadcast("COUNTY_MOUSEOVER_POSITION", $scope.modelhidden, $scope.modelTop, $scope.modelLeft); $scope.isMouserOver = false; } function modelHidden() { if (!primaryService.getIsMouseArrivedOnModel()) { $scope.modelhidden = true; $rootScope.$broadcast("COUNTY_MOUSEOVER_POSITION", $scope.modelhidden, $scope.modelTop, $scope.modelLeft); } } //Zoom Handling / Map Positioning var isMapZoomedIn; var isMouseInZoomTargetMode; var windowWidth = $window.innerWidth; var zoomInMouseX = 0; var zoomInMouseY = 0; $scope.$watch(function () { if ($window.innerWidth !== windowWidth) { windowWidth = $window.innerWidth; var officeJson = detailsData.getOfficeData(); mapSvgPathBuilder.injectNewPathInfoIntoMapGeoDataWithAreaLabel($scope.mapGeo, windowWidth, isMapZoomedIn ? [zoomInMouseX, zoomInMouseY] : undefined); } }); $scope.zoomOut = function () { isMapZoomedIn = false; isMouseInZoomTargetMode = false; $scope.zoomButtonClass = mapClassGenerator.generateZoomButtonClass(isMapZoomedIn, isMouseInZoomTargetMode, enrModeManager.isOfficeMode());; $scope.mapClass = mapClassGenerator.generateMapClass(isMapZoomedIn, isMouseInZoomTargetMode); var officeJson = detailsData.getOfficeData(); mapSvgPathBuilder.injectNewPathInfoIntoMapGeoDataWithAreaLabel($scope.mapGeo, windowWidth, undefined); }; $scope.zoomIn = function () { isMapZoomedIn = true; isMouseInZoomTargetMode = false; $scope.zoomButtonClass = mapClassGenerator.generateZoomButtonClass(isMapZoomedIn, isMouseInZoomTargetMode, enrModeManager.isOfficeMode()); $scope.mapClass = mapClassGenerator.generateMapClass(isMapZoomedIn, isMouseInZoomTargetMode); var height = $("#svgLayer").height(); var width = $("#svgLayer").width(); var officeJson = detailsData.getOfficeData(); mapSvgPathBuilder.injectNewPathInfoIntoMapGeoDataWithAreaLabel($scope.mapGeo, windowWidth, [zoomInMouseX, zoomInMouseY], [width, height]); }; $scope.onSVGClick = function (event) { console.log("mouse event X", event.offsetX); console.log("mouse event Y", event.offsetY); } $scope.handleZoomClicked = function () { // Zoom out happens immediately when you click the zoom out button. if (isMapZoomedIn) { $scope.zoomOut(); $scope.isMouseInZoomTargetModeE = !$scope.isMouseInZoomTargetModeE; } // Zoom in requires you to click the map to pick your zoom in spot. else { isMouseInZoomTargetMode = !isMouseInZoomTargetMode; $scope.zoomButtonClass = mapClassGenerator.generateZoomButtonClass(isMapZoomedIn, isMouseInZoomTargetMode, enrModeManager.isOfficeMode()); $scope.mapClass = mapClassGenerator.generateMapClass(isMapZoomedIn, isMouseInZoomTargetMode); } }; $scope.handleMouseEnter = function () { $("#ulDropdownMenu").show(); $scope.showModal = true; }; $scope.handleMouseLeave = function () { if (!clickHandle) { $("#ulDropdownMenu").hide(); $scope.showModal = false; } }; $scope.handleClick = function () { clickHandle = !clickHandle; if (clickHandle) { $("#ulDropdownMenu").show(); $scope.showModal = true; } else { hideLegend(); } }; function hideLegend() { $("#ulDropdownMenu").hide(); $scope.showModal = false; clickHandle = false; } $scope.$on("SLICK_OFFICE_SELECTION", clearSelectedRegion); $scope.$on("SLICK_REFERENDUM_SELECTION", clearSelectedRegion); function clearSelectedRegion() { // Clear existing selections if (mapSelectedRegion.isAnyRegionSelected()) { var curSelectedRegionId = mapSelectedRegion.getSelectedRegionId(); mapSelectedRegion.clearSelectedRegion(); $(".map-region." + curSelectedRegionId).attr("class", mapClassGenerator.generateRegionClass(curSelectedRegionId, false)); } } $scope.$on("SLICK_REFERENDUM_SELECTION", function(event, item) { if (item.FIPS != null) { var selectedRegion = findRegionPropertiesByFIPS(item.FIPS); if (selectedRegion != null) { localStorage.setItem("GeometryObjProperties", JSON.stringify(selectedRegion)); $scope.handleMapRegionClicked(selectedRegion, null); } } }); function findRegionPropertiesByFIPS(fipsId) { for (var x = 0; x < $scope.mapGeo.objects.collection.geometries.length; x++) { var y = $scope.mapGeo.objects.collection.geometries[x]; if (y.properties.GEOID === fipsId) { return y.properties; } } return null; } // Region Selection $scope.handleMapRegionClicked = function (geometryObjProperties, event) { if ($rootScope.defaultOfficeSelection && enrModeManager.isOfficeMode()) { return;} //close the popup $scope.modelhidden = true; $rootScope.$broadcast("COUNTY_MOUSEOVER_POSITION", $scope.modelhidden, $scope.modelTop, $scope.modelLeft); // Zoom if (isMouseInZoomTargetMode) { removePathZoomEffect(event.target); var browser = navigator.userAgent; if (browser.indexOf('Chrome') > -1) { zoomInMouseX = event.offsetX; zoomInMouseY = event.offsetY; $scope.zoomIn(); $scope.isMouseInZoomTargetModeE = true; } else if (browser.indexOf("Mozilla") > -1 && browser.indexOf("Firefox") > -1) { // Firefox Support setTimeout(function () { zoomInMouseX = event.originalEvent.layerX; zoomInMouseY = event.originalEvent.layerY; $scope.zoomIn(); $scope.isMouseInZoomTargetModeE = true; }, 100) } else { zoomInMouseX = event.offsetX; zoomInMouseY = event.offsetY; $scope.zoomIn(); $scope.isMouseInZoomTargetModeE = true; } } // Region Selection else { $scope.isMouseInZoomTargetModeE = false; var clickedRegionId = null; if (event != "undefined" && event != null) { if (event.target) { clickedRegionId = regionPropertiesParser.getRegionPrimaryId(geometryObjProperties); primaryService.setIsAnyRegionSelected(mapSelectedRegion.isAnyRegionSelected()); } } else { if (((localStorage.getItem("GeometryObjProperties") != "null" && localStorage.getItem("GeometryObjProperties") != null && localStorage.getItem("GeometryObjProperties") != "undefined")) && ((localStorage.getItem("GeometryObjProperties").length > 0))) { clickedRegionId = regionPropertiesParser.getRegionPrimaryId(JSON.parse(localStorage.getItem("GeometryObjProperties"))); } } var clickedRegionElement = $(".map-region." + clickedRegionId); var clickedRegionParent = clickedRegionElement.parent(); var isUserDeselectingRegion = clickedRegionId === mapSelectedRegion.getSelectedRegionId(); // Clear existing selections if (event != "undefined" && event != null) { if (mapSelectedRegion.isAnyRegionSelected()) { var curSelectedRegionId = mapSelectedRegion.getSelectedRegionId(); mapSelectedRegion.clearSelectedRegion(); $(".map-region." + curSelectedRegionId).attr("class", mapClassGenerator.generateRegionClass(curSelectedRegionId, false)); } } else { if (((localStorage.getItem("IsAnyRegionSelected") != "null" && localStorage.getItem("IsAnyRegionSelected") != null && localStorage.getItem("IsAnyRegionSelected") != "undefined")) && ((localStorage.getItem("IsAnyRegionSelected").length > 0))) { var curSelectedRegionId = clickedRegionId; mapSelectedRegion.clearSelectedRegion(); $(".map-region." + curSelectedRegionId).attr("class", mapClassGenerator.generateRegionClass(curSelectedRegionId, false)); } } // Make new selection (unless they deselected region by clicking on it a second time) if (!isUserDeselectingRegion) { mapSelectedRegion.setSelectedRegion(geometryObjProperties); clickedRegionElement.attr("class", mapClassGenerator.generateRegionClass(clickedRegionId, true)); // clickedRegionParent.append(clickedRegionElement); // Force region to be final SVG child to ensure it renders on top (so active effect is fully visible) } // Pull Referendums Data var shouldPullRefData = enrModeManager.isReferendumsMode() && !isUserDeselectingRegion; if (shouldPullRefData) { var refDataFile = "JurR_" + regionPropertiesParser.getRegionPrimaryId(geometryObjProperties, false, true); jsonDataFetcher.fetchSingleJson(refDataFile, true, false, function (referendumsJson) { detailsData.setData(referendumsJson); }); } // Clear Referendums Data if (enrModeManager.isReferendumsMode() && isUserDeselectingRegion) detailsData.clearData(); if (detailsData.isOffDataPresent() && detailsData.getOfficeData() !== detailsData.getData()) { detailsData.setData(detailsData.getOfficeData()); } } // Add Text element if (event != null) { if (event.target) { var pathId = event.target.id; var textId = pathId.replace("path", "txt"); $("#" + textId).parent().append($("#" + textId)); primaryService.setSelectedGeometryEventPath(JSON.stringify(pathId)); } } else { if (((localStorage.getItem("GeometryEventPath") != "null" && localStorage.getItem("GeometryEventPath") != "undefined")) && ((localStorage.getItem("GeometryEventPath").length > 0))) { var pathId = JSON.parse(localStorage.getItem("GeometryEventPath")); var textId = pathId.replace("path", "txt"); $("#" + textId).parent().append($("#" + textId)); primaryService.setSelectedGeometryEventPath(JSON.stringify(pathId)); } } primaryService.setSelectedGeometryObjProperties((geometryObjProperties)); }; // Handling Display of Office Candidates On Legend in Primary Mode // Forms flat list of candiates from office race data. function updateLegendCandidates() { // Early Out: Skip if not primary or if we aren't showing candidate gradients if (!$scope.isPrimary || $scope.disableGradients) return; var isOfficeMode = $scope.electionData && $scope.electionData.StatewideSummary; if (isOfficeMode) { var raceList = $scope.electionData.StatewideSummary.Race; if (!angular.isArray(raceList)) raceList = [raceList]; var raceList = raceList.slice(); // store copy of array var candidateList = []; raceList.forEach(function (race) { var raceCandidates = race.Candidates.Candidate; if (!angular.isArray(raceCandidates)) raceCandidates = [raceCandidates]; raceCandidates.forEach(function (candidate) { candidateList.push(candidate); }); }); gradientService.generateOfficeCandidateGradients(candidateList); // Get the list of candidates for the selected party, ordered by last name. var filteredCandidateList = $filter('filter')(candidateList, { PARTY: $scope.selectedPartyCode }); filteredCandidateList = $filter('orderByLastName')(filteredCandidateList.map(function (candidate) { return candidate.NAME_ON_BALLOT; })); var orderedCandidates = []; filteredCandidateList.forEach(function (name) { var candidate = $filter('filter')(candidateList, { NAME_ON_BALLOT: name })[0]; orderedCandidates.push(candidate); }); $scope.selectedPartyOfficeCandidates = filteredCandidateList; $scope.candidateColors = orderedCandidates.map(function (candidate) { return candidate.GRADIENT_COLOR; }); } } } ]); }());