{"version":3,"sources":["config/Constants.js","helpers/HeatmapHelpers.js","services/HeatmapService.js","services/classes/Notification.js","services/NotificationService.js","context/ResetContext.js","components/Notification.js","components/Notifications.js","components/MultiSelectBoxOption.js","components/MultiSelectBox.js","components/Nav.js","components/Menu.js","components/Footer.js","components/HeatmapHeader.js","components/HeatmapPeriodSection.js","components/HeatmapPeriodTableCell.js","components/HeatmapPeriod.js","components/Heatmap.js","components/Route.js","services/HeatmapDragService.js","components/Tooltip.js","components/JunctionTooltip.js","components/LinkTooltip.js","App.js","index.js"],"names":["CONSTANTS","DATA_SERVICE_RECONNECT_DELAY","TOAST_DISPLAY_TIME","NOTIFICATIONS","DISCONNECTION_MESSAGE","type","title","message","RECONNECTION_MESSAGE","CONNECTION_ERROR_MESSAGE","DELAY_DATA_ERROR_MESSAGE","ROUTE_DATA_ERROR_MESSAGE","HEATMAP_DATA_ERROR_MESSAGE","UPDATE_INTERVAL_MINS","ROUTE_SELECTION_STATE","deselected","partial","complete","timeframeGenerator","setPeriodVisibility","periodStart","routesPeriods","clonedRoutesPeriods","cloneObject","todaysRoutesPeriods","find","day","getDateStringFromDate","Date","date","carriageways","forEach","carriageway","period","periods","timeframe","convertTimeBinToTimeframe","isVisible","getVisibleJunctionsAndLinks","routeData","junctionFrom","junctionTo","visibleJunctions","visibleLinks","route","filteredJunctions","junctions","filter","junction","index","carriagewayJunctions","indexOf","label","map","id","getVisibleJunctions","startLinkId","endLinkId","visible","linkId","links","link","getVisibleLinks","setHeatmapColours","selectedDelays","delays","root","document","documentElement","delay","colour","style","setProperty","red","green","blue","generateEmptyPeriods","today","selectedDate","getHours","getMinutes","setMinutes","setHours","Math","floor","finalTimeframe","timeframeIterator","next","value","done","timeframeValue","unshift","addNewDatePeriods","newDatePeriods","existingDatesPeriods","Promise","resolve","addNewDate","then","datesPeriods","existingDatePeriods","carriagewayData","c","existingCarriageway","datePeriods","push","routePeriods","existingPeriod","carriagewayPeriod","sort","a","b","split","addNewPeriodToCarriageway","carriagewayLinks","journeyTimes","journeyTime","journeyTimeSeconds","delayBand","fillTimeBinGaps","getUTCFullYear","getUTCMonth","slice","getUTCDate","timestring","timeframeMins","startTime","formatTimeAsString","endTime","getCarriagewaySelections","routeOptions","name","isSelected","setCarriagewaySelections","selections","selectionRoute","selection","selectedCarriageway","selectionCarriageway","currentHour","currentInterval","dayLimit","updateIntervalMins","intervals","intervalValue","previousTime","length","hours","minutes","hoursFormatted","parseInt","minutesFormatted","existingArray","JSON","parse","stringify","HeatmapService","dataserviceHandlers","notificationService","this","dataServiceHandlers","onDataServiceMessage","onDataServiceOpen","currentRoute","currentDate","liveUpdateTimeouts","response","HeatmapHelpers","composed","roadName","directionId","timeBins","reverse","timeBin","timeBinMinutes","notificationDetails","isDisconnected","addNotification","action","createDataServiceConnection","bind","actionButtonText","notification","reconnectTimeout","setTimeout","heatmapWebSocketService","readyState","removeNotification","reject","clearTimeout","HubConnectionBuilder","withUrl","process","withAutomaticReconnect","configureLogging","LogLevel","Warning","build","on","handleDataServiceMessage","onclose","error","handleDataServiceClose","start","handleDataServiceOpen","catch","additionalInformation","fetch","ok","Error","statusText","json","data","displayOrder","startLinkJunction","endLinkJunction","direction","isJunction","existingJunction","parsedA","parsedB","toUpperCase","dateTodayString","currentDoute","queryDate","url","HeatmapNotification","onClose","timestamp","hasAction","NotificationService","onNotificationsUpdate","idIterator","idGenerator","notifications","getNotifications","console","log","getNotification","findIndex","splice","handleClose","handleAction","React","createContext","Notification","props","actionButton","createRef","closeButton","current","focus","className","ref","onClick","handleCloseClick","handleActionButtonClick","onCloseNotification","onClickNotificationAction","Component","defaultProps","Notifications","state","fadeIn","nextProps","nextState","prevProps","prevState","fadeTimeout","setState","key","MultiSelectBoxOption","onToggleOption","aria-label","unselected","MultiSelectBox","isExpanded","options","every","option","onToggleExpand","subOption","newState","closeList","closeSelectBox","addEventListener","event","target","closest","removeEventListener","Nav","routeSelections","toString","href","environmentName","onSubmit","handleFormSubmit","handleRouteToggle","onChange","handleDateChange","max","toISOString","handleResetClick","showMenu","Object","keys","splitId","routeName","selectedRoute","some","preventDefault","onChangeRouteOptions","onResetFilters","onToggleRoute","onChangeDate","toggleMenu","Menu","handleMenuButtonClick","handleDelayToggle","background","description","currentTarget","onChangeSelectedDelays","Footer","getFullYear","PureComponent","HeatmapHeader","renderLink","offsetPercent","width","lengthMetres","totalDistance","onMouseOver","handleMouseOverJunction","onMouseOut","closeTooltip","persist","openTooltip","HeatmapPeriodSection","showTooltip","timeout","documentClickCallback","transitionEnterTimeout","count","sectionStyle","transitionDelay","isLatest","classNames","handleButtonClick","setTooltipState","HeatmapPeriodTableCell","showTimeBins","currentLink","HeatmapPeriod","domContainer","visibleLink","Heatmap","heatmapContainer","simpleBarRef","isExpanding","heatmapHeight","heatmapDragService","minimumHeatmapHeightLive","zoomLevel","distance","isLive","resetHeight","context","recalcTimeout","recalculate","minimumHeatmapHeight","getScrollElement","tableStyle","simpleBarClass","heatmapStyle","height","forceVisible","autoHide","openJunctionTooltip","closeJunctionTooltip","onClickLink","onMouseDown","onDragHandleMouseDown","setButtonState","callback","setHeight","handleStartDragging","onStopDraggingHeatmap","contextType","ResetContext","Route","selectedRoutes","selectedCarriageways","resetMethods","resetComponent","setJunctionAndLinkVisibility","pdfUrl","Provider","manageResetMethods","handleJunctionFromChange","disabled","handleJunctionToChange","min","step","handleZoomLevelChange","getVisiblePeriodsForCarriageway","showLiveData","reduce","method","removeMethod","visibleJunctionsAndLinks","carriagewayId","parseFloat","methodName","rowHeight","HeatmapDragService","contentScrollbar","dragTimerActive","scrollSpacerHeight","domElements","content","querySelector","scrollSpacer","footer","currentDragHandle","setDragEvents","handleScroll","recalculateScrollSpacer","scrollbar","scrollbarContentWrapper","contentWrapperEl","scrollbarContent","contentEl","dragEvents","element","events","eventName","handler","handleHeatmapDrag","handleDragOverFooter","handleDragOutFooter","handleEndDragging","eventListener","initialHeight","numberOfRows","updateHeightCallback","endDragCallback","updateScrollbarDomElements","initialY","clientY","initialSpacerHeight","currentHeatmapHeight","addDragEventListeners","removeDragEventListeners","clearInterval","overFooterInterval","scrollbarScrollTop","scrollTop","onScroll","isAboveHandle","getBoundingClientRect","top","scrolledToBottom","clientHeight","offsetHeight","updateScrollSpacer","newHeight","maxHeightByRows","maxHeightByWindow","maxHeight","calculatedHeight","setInterval","currentScroll","startHeight","heightAdjustment","Tooltip","left","show","getTooltipPosition","getTooltipContent","elementBoundingBox","appContentBoundingBox","tooltipSize","clientWidth","adjustedElBoundingBox","bottom","JunctionTooltip","propTypes","PropTypes","object","isRequired","LinkTooltip","expectedTimeSeconds","App","routes","showLinkTooltip","linkTooltipData","showJunctionTooltip","junctionTooltipData","handleNotificationsUpdate","browserSupported","loadHeatmapService","scrollbarRef","unsupportedBrowserMessage","setEnvironmentIdentifier","handleSelectedDateChange","handleSelectedRouteDataChange","handleResetFilters","handleSelectedDelaysChange","closeNotification","handleNotificationActionClick","getRoutesPeriodsForSelectedDate","handleLinkTooltipOpen","handleLinkTooltipClose","handleJunctionTooltipOpen","handleJunctionTooltipClose","toLowerCase","heatmapService","updateLiveData","all","getRoutes","getDelayData","selectedRouteData","filteredRouteData","selectedRouteCarriageways","setPeriodVisibleInterval","currentTime","nextPeriod","setRecentPeriodVisible","checkDateChange","getHeatmapData","historicData","processHeatmapData","dateRoutesPeriods","closeLinkTooltip","setToolTipButtonState","once","triggerNotificationAction","supportedBrowserVersions","minimumBrowserSupport","Chrome","Chromium","Edge","Firefox","browserVersion","ua","window","navigator","userAgent","browser","version","chromeVersionIndex","chromiumVersionIndex","edgeVersionIndex","firefoxVersionIndex","substring","getBrowserVersion","isSupported","ReactDOM","render","getElementById"],"mappings":"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","file":"static/js/main.a8e587fc.chunk.js","sourcesContent":["/**\r\n * @description Constants module\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module config/Constants\r\n */\r\n\r\n/**\r\n * @constant\r\n * @type {object}\r\n * @description Exports an object containing constants that are used throughout the application.\r\n */\r\nconst CONSTANTS = {\r\n // Time in ms to attempt reconnection to data service.\r\n DATA_SERVICE_RECONNECT_DELAY: 20000,\r\n // Time in ms to display a toast notification.\r\n TOAST_DISPLAY_TIME: 2000,\r\n // Notification messages.\r\n NOTIFICATIONS: {\r\n // Notification message details after data service has disconnected.\r\n DISCONNECTION_MESSAGE: {\r\n type: 'error',\r\n title: 'Service Disconnected',\r\n message:\r\n 'The data service disconnected. Please wait to attempt an automatic reconnection, or click below to reconnect.'\r\n },\r\n // Notification message details after successful reconnection to data service.\r\n RECONNECTION_MESSAGE: {\r\n type: 'toast',\r\n title: 'Service Reconnected',\r\n message: 'The data service reconnected.'\r\n },\r\n // Notification message details after failed connection to data service.\r\n CONNECTION_ERROR_MESSAGE: {\r\n type: 'error',\r\n title: 'Service Connection Error',\r\n message: 'There was a problem connecting to the data service. Please try again later.'\r\n },\r\n // Notification message details after failure to get delay data.\r\n DELAY_DATA_ERROR_MESSAGE: {\r\n type: 'error',\r\n title: 'Data Error',\r\n message: 'There was a problem retrieving the delay data. Please try again later.'\r\n },\r\n // Notification message details after failure to get route data.\r\n ROUTE_DATA_ERROR_MESSAGE: {\r\n type: 'error',\r\n title: 'Data Error',\r\n message: 'There was a problem retrieving the route data. Please try again later.'\r\n },\r\n // Notification message details after failure to get heatmap data.\r\n HEATMAP_DATA_ERROR_MESSAGE: {\r\n type: 'error',\r\n title: 'Data Error',\r\n message: 'There was a problem retrieving the heatmap data. Please try again later.'\r\n }\r\n },\r\n // Default data update interval in minutes.\r\n UPDATE_INTERVAL_MINS: 15,\r\n ROUTE_SELECTION_STATE: {\r\n deselected: -1,\r\n partial: 0,\r\n complete: 1\r\n }\r\n};\r\n\r\nexport default CONSTANTS;\r\n","/**\r\n * @description HeatmapHelpers module\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module helpers/HeatmapHelpers\r\n */\r\n\r\nimport CONSTANTS from \"../config/Constants\";\r\n\r\n/**\r\n * @description Sets a specified period for the current day visible.\r\n * @export\r\n * @param {string} periodStart - A string representation of the start timeBin.\r\n * @param {array} routesPeriods - The routesPeriods to update.\r\n * @returns {array} The updated routesPeriods.\r\n */\r\nexport function setPeriodVisibility(periodStart, routesPeriods) {\r\n const clonedRoutesPeriods = cloneObject(routesPeriods),\r\n todaysRoutesPeriods = clonedRoutesPeriods.find(\r\n day =>\r\n getDateStringFromDate(new Date(day.date)) ===\r\n getDateStringFromDate(new Date())\r\n );\r\n\r\n todaysRoutesPeriods &&\r\n todaysRoutesPeriods.carriageways.forEach(carriageway => {\r\n const period = carriageway.periods.find(\r\n period =>\r\n period.timeframe === convertTimeBinToTimeframe(periodStart)\r\n );\r\n\r\n if (period) {\r\n period.isVisible = true;\r\n }\r\n });\r\n\r\n return clonedRoutesPeriods;\r\n}\r\n\r\n/**\r\n * @description Returns an object containing lists of all junctions and links from all routes.\r\n * @export\r\n * @param {array} routes - An array of routes.\r\n * @returns {Object} - The junctions and links data.\r\n */\r\nexport function getJunctionsAndLinksFromRoutes(routes) {\r\n const data = {\r\n junctions: [],\r\n links: []\r\n };\r\n\r\n routes.forEach(route =>\r\n route.carriageways.forEach(carriageway => {\r\n data.junctions = [\r\n ...data.junctions,\r\n ...carriageway.junctions.map(junction => {\r\n return {\r\n ...junction,\r\n carriageway: carriageway.id\r\n };\r\n })\r\n ];\r\n data.links = [\r\n ...data.links,\r\n ...carriageway.links.map(link => {\r\n return {\r\n ...link,\r\n carriageway: carriageway.id\r\n };\r\n })\r\n ];\r\n })\r\n );\r\n\r\n return data;\r\n}\r\n\r\n/**\r\n * @description Expands a heatmap by the specified carriageway id.\r\n * @export\r\n * @param {Object} expandedRoutes - The route data in which to find the carriageway.\r\n * @param {string} carriagewayId - The carriageway id.\r\n * @param {boolean} state - The specified state. Will toggle if unspecified.\r\n * @returns {object} The updated route data.\r\n */\r\nexport function expandHeatmapByCarriagewayId(\r\n expandedRoutes,\r\n carriagewayId,\r\n state\r\n) {\r\n const clonedExpandedRoutes = cloneObject(expandedRoutes),\r\n routeIndex = clonedExpandedRoutes.findIndex(id => id === carriagewayId);\r\n\r\n if (\r\n (routeIndex > -1 && state === true) ||\r\n (routeIndex === -1 && state === false)\r\n ) {\r\n // No change to expansion.\r\n return clonedExpandedRoutes;\r\n }\r\n\r\n if (routeIndex > -1) {\r\n // Remove from expanded routes\r\n clonedExpandedRoutes.splice(routeIndex, 1);\r\n } else {\r\n // Add to expanded routes\r\n clonedExpandedRoutes.push(carriagewayId);\r\n }\r\n\r\n return clonedExpandedRoutes;\r\n}\r\n\r\n/**\r\n * @description Sets the isSelected flag on selected routes.\r\n * @export\r\n * @param {array} routeData - The route data.\r\n * @param {array} [selectedCarriageways=[]] - The list of carriageways or entire routes to select.\r\n * @returns {array} - The modified data.\r\n */\r\nexport function setSelectedRoutes(routeData, selectedCarriageways = []) {\r\n const filteredRouteData = cloneObject(routeData),\r\n selectedRoutes = selectedCarriageways.filter(\r\n carriageway => carriageway.split(\"_\").length === 1\r\n );\r\n\r\n filteredRouteData.forEach(route => {\r\n route.carriageways = route.carriageways.map(carriageway => {\r\n return {\r\n ...carriageway,\r\n isSelected:\r\n selectedCarriageways.indexOf(carriageway.id) >= 0 ||\r\n selectedRoutes.indexOf(route.name) >= 0\r\n };\r\n });\r\n const selectedRouteCarriageways = route.carriageways.filter(\r\n carriageway => carriageway.isSelected\r\n ).length;\r\n\r\n route.isSelected =\r\n selectedRoutes.indexOf(route.name) >= 0 ||\r\n selectedRouteCarriageways === route.carriageways.length\r\n ? 1\r\n : selectedRouteCarriageways.length > 0\r\n ? 0\r\n : -1;\r\n });\r\n\r\n return filteredRouteData;\r\n}\r\n\r\n/**\r\n * @description Sets the junction and link visibility.\r\n * @export\r\n * @param {Object} routeData - The route data.\r\n * @param {string | number} junctionFrom - The junction to filter from.\r\n * @param {string | number} junctionTo - The junction to filter to.\r\n * @returns {object} The filtered route data.\r\n */\r\nexport function getVisibleJunctionsAndLinks(\r\n routeData,\r\n junctionFrom,\r\n junctionTo\r\n) {\r\n let visibleJunctions = [],\r\n visibleLinks = [];\r\n const route = cloneObject(routeData),\r\n filteredJunctions = route.junctions.filter(\r\n (junction, index) => index >= junctionFrom && index <= junctionTo\r\n );\r\n\r\n route.carriageways.forEach(carriageway => {\r\n const carriagewayJunctions = getVisibleJunctions(\r\n carriageway,\r\n filteredJunctions\r\n );\r\n\r\n visibleJunctions = [...visibleJunctions, ...carriagewayJunctions];\r\n visibleLinks = [\r\n ...visibleLinks,\r\n ...getVisibleLinks(carriageway, carriagewayJunctions)\r\n ];\r\n });\r\n\r\n return {\r\n junctions: visibleJunctions,\r\n links: visibleLinks\r\n };\r\n}\r\n\r\n/**\r\n * @description Sets the value of the CSS vars that relate to delay colours.\r\n * @export\r\n * @param {array} selectedDelays - The selected delays.\r\n * @param {array} delays - The delay values.\r\n */\r\nexport function setHeatmapColours(selectedDelays, delays) {\r\n const root = document.documentElement;\r\n\r\n delays.forEach((delay, index) => {\r\n const colour = delay.colour;\r\n\r\n root.style.setProperty(\r\n `--delay-colour-${index}`,\r\n selectedDelays[index]\r\n ? `rgb(${colour.red},${colour.green},${colour.blue})`\r\n : \"#ffffff\"\r\n );\r\n });\r\n}\r\n\r\n/**\r\n * @description Creates empty time bins for the specified date.\r\n * @param {string} date - A string representing the date.\r\n * @returns {array} - An array of empty periods.\r\n */\r\nfunction generateEmptyPeriods(date) {\r\n const today = new Date(),\r\n selectedDate = new Date(date),\r\n isToday =\r\n getDateStringFromDate(today) ===\r\n getDateStringFromDate(selectedDate);\r\n\r\n if (isToday) {\r\n if (today.getHours() === 0 && today.getMinutes() < CONSTANTS.UPDATE_INTERVAL_MINS) {\r\n selectedDate.setMinutes(0, 0, 0);\r\n } else {\r\n selectedDate.setHours(\r\n today.getHours(), // Current hour\r\n Math.floor(today.getMinutes() / CONSTANTS.UPDATE_INTERVAL_MINS) *\r\n CONSTANTS.UPDATE_INTERVAL_MINS -\r\n CONSTANTS.UPDATE_INTERVAL_MINS, // Round minutes down to start of previous timeBin\r\n 0,\r\n 0\r\n );\r\n } \r\n } else {\r\n selectedDate.setHours(23, 45, 0, 0);\r\n }\r\n\r\n const finalTimeframe = convertTimeBinToTimeframe(selectedDate),\r\n timeframeIterator = timeframeGenerator(),\r\n periods = [];\r\n let timeframe = timeframeIterator.next(),\r\n isVisible = finalTimeframe !== timeframe.value;\r\n\r\n while (!timeframe.done) {\r\n const timeframeValue = timeframe.value;\r\n\r\n isVisible = isVisible && finalTimeframe !== timeframe.value;\r\n\r\n periods.unshift({\r\n timeframe: timeframeValue,\r\n isVisible: isVisible\r\n });\r\n timeframe = timeframeIterator.next();\r\n }\r\n\r\n return periods;\r\n}\r\n\r\n/**\r\n * @description Adds a new period to the correct route within the selected routes.\r\n * @param {Object} period - The new period to add.\r\n * @param {array} selectedRoutes - The selected routes.\r\n * @returns {array} The selected routes with the added period.\r\n */\r\nfunction addNewPeriodToCarriageway(carriageway, existingDatePeriods) {\r\n let existingCarriageway = existingDatePeriods.carriageways.find(\r\n datePeriods => datePeriods.id === carriageway.id\r\n );\r\n\r\n if (!existingCarriageway) {\r\n existingCarriageway = {\r\n id: carriageway.id,\r\n periods: generateEmptyPeriods(existingDatePeriods.date)\r\n };\r\n existingDatePeriods.carriageways.push(existingCarriageway);\r\n }\r\n\r\n const routePeriods = existingCarriageway.periods.map(period => {\r\n const existingPeriod = carriageway.periods.find(\r\n carriagewayPeriod =>\r\n period.timeframe === carriagewayPeriod.timeframe\r\n );\r\n\r\n if (existingPeriod) {\r\n return {\r\n ...existingPeriod,\r\n isVisible: true\r\n };\r\n }\r\n\r\n return period;\r\n });\r\n\r\n existingCarriageway.periods = routePeriods.sort(\r\n (a, b) =>\r\n new Date(\"1970/01/01 \" + b.timeframe.split(\" \")[0]) -\r\n new Date(\"1970/01/01 \" + a.timeframe.split(\" \")[0])\r\n );\r\n}\r\n\r\n/**\r\n * @description Fills gaps within timeBin journeyTime data and ensures journeyTimes are in the correct order.\r\n * @param {array} carriagewayData - The new carriageway data to fill.\r\n * @param {array} carriagewayLinks - The links for the given carriageway.\r\n * @returns {array} - The filled data.\r\n */\r\nfunction fillTimeBinGaps(carriagewayData, carriagewayLinks) {\r\n carriagewayData.periods.forEach(period => {\r\n const journeyTimes = [];\r\n\r\n carriagewayLinks.forEach(link => {\r\n journeyTimes.push(\r\n period.journeyTimes.find(\r\n journeyTime => journeyTime.linkId === link\r\n ) || {\r\n linkId: link,\r\n journeyTimeSeconds: \"N/A\",\r\n delayBand: {\r\n id: 0\r\n }\r\n }\r\n );\r\n });\r\n period.journeyTimes = journeyTimes;\r\n });\r\n\r\n return carriagewayData;\r\n}\r\n\r\n/**\r\n * @description Adds new journeytimes to the existing ones.\r\n * @export\r\n * @param {Object} newDatePeriods - The new data.\r\n * @param {array} existingDatesPeriods - The existing data.\r\n * @param {array} carriageways - The existing carriageways.\r\n * @returns {void}\r\n */\r\nexport function addNewDatePeriods(\r\n newDatePeriods,\r\n existingDatesPeriods,\r\n carriageways\r\n) {\r\n return new Promise(resolve => {\r\n addNewDate(\r\n newDatePeriods.date || getDateStringFromDate(new Date()),\r\n existingDatesPeriods,\r\n carriageways\r\n ).then(datesPeriods => {\r\n let existingDatePeriods = datesPeriods.find(\r\n date => date.date === newDatePeriods.date\r\n );\r\n \r\n newDatePeriods.carriageways.forEach(carriageway => {\r\n const carriagewayData = carriageways.find(\r\n c => c.id === carriageway.id\r\n ),\r\n filledCarriageway = fillTimeBinGaps(\r\n carriageway,\r\n carriagewayData ? carriagewayData.links : []\r\n );\r\n \r\n addNewPeriodToCarriageway(filledCarriageway, existingDatePeriods);\r\n });\r\n resolve(datesPeriods);\r\n }); \r\n });\r\n}\r\n\r\n/**\r\n * @description Adds a new date of empty periods.\r\n * @export\r\n * @param {Date} date - The date for which to add period data.\r\n * @param {array} existingDatesPeriods - an array of existing datesPeriods\r\n * @param {array} carriageways - an array of carriageways\r\n * @returns {array} - The updated list of datesPeriods\r\n */\r\nexport function addNewDate(date, existingDatesPeriods, carriageways) {\r\n const datesPeriods = cloneObject(existingDatesPeriods);\r\n\r\n return new Promise(resolve => {\r\n let existingDatePeriods = datesPeriods.find(datePeriods => datePeriods.date === date);\r\n\r\n if (existingDatePeriods) {\r\n resolve(datesPeriods);\r\n\r\n return;\r\n }\r\n\r\n const periods = generateEmptyPeriods(date);\r\n\r\n datesPeriods.push({\r\n date: date,\r\n carriageways: carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n periods: periods\r\n };\r\n })\r\n });\r\n\r\n resolve(datesPeriods);\r\n });\r\n}\r\n\r\n/**\r\n * @description Returns a date in yyyy-mm-dd format\r\n * @export\r\n * @param {Object} date - The date to convert.\r\n * @returns {string} - The formatted date.\r\n */\r\nexport function getDateStringFromDate(date) {\r\n return `${date.getUTCFullYear()}-${(\"0\" + (date.getUTCMonth() + 1)).slice(\r\n -2\r\n )}-${(\"0\" + date.getUTCDate()).slice(-2)}`;\r\n}\r\n\r\n/**\r\n * @description Converts an ISO time string to a timeframe.\r\n * @export\r\n * @param {sring} timestring - An ISO time string.\r\n * @param {number} [timeframeMins=CONSTANTS.UPDATE_INTERVAL_MINS] - The number of minutes in the timeframe\r\n * @returns {string} - The timeframe.\r\n */\r\nexport function convertTimeBinToTimeframe(\r\n timestring,\r\n timeframeMins = CONSTANTS.UPDATE_INTERVAL_MINS\r\n) {\r\n const date = new Date(timestring),\r\n startTime = formatTimeAsString(date.getHours(), date.getMinutes());\r\n\r\n date.setMinutes(date.getMinutes() + timeframeMins);\r\n const endTime = formatTimeAsString(date.getHours(), date.getMinutes());\r\n\r\n return `${startTime} - ${endTime}`;\r\n}\r\n\r\n/**\r\n * @description Sets the junction visibility for a carriageway, using a list of filtered junctions.\r\n * @param {Object} carriageway - The carriageway.\r\n * @param {array} filteredJunctions - The list of filtered junctions.\r\n * @returns {array} A new list of filtered junctions.\r\n */\r\nfunction getVisibleJunctions(carriageway, filteredJunctions) {\r\n return carriageway.junctions\r\n .filter(junction => {\r\n return filteredJunctions.indexOf(junction.label) >= 0;\r\n })\r\n .map(junction => {\r\n return {\r\n ...junction,\r\n carriageway: carriageway.id\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * @description Sets the link visibility for a carriageway.\r\n * @param {Object} carriageway - The carriageway.\r\n * @returns {array} A new list of filtered links.\r\n */\r\nfunction getVisibleLinks(carriageway, visibleJunctions) {\r\n let startLinkId = null,\r\n endLinkId = null,\r\n visible = false;\r\n\r\n visibleJunctions.forEach(junction => {\r\n endLinkId = junction.linkId;\r\n startLinkId = !startLinkId ? junction.linkId : startLinkId;\r\n });\r\n const links = carriageway.links.map(link => {\r\n return {\r\n ...link,\r\n visible: link.id === startLinkId || link.id === endLinkId\r\n };\r\n });\r\n\r\n if (startLinkId !== endLinkId) {\r\n links.forEach(link => {\r\n if (link.visible) {\r\n visible = !visible;\r\n } else {\r\n link.visible = visible;\r\n }\r\n });\r\n }\r\n\r\n return links\r\n .filter(link => link.visible)\r\n .map(link => {\r\n return {\r\n ...link,\r\n carriageway: carriageway.id\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * @description Creates a selection list from a routeOptions object.\r\n * @export\r\n * @param {array} routeOptions - An array representing the routes.\r\n * @returns {Object} - An array of the selections.\r\n */\r\nexport function getCarriagewaySelections(routeOptions) {\r\n const routes = cloneObject(routeOptions);\r\n\r\n return routes.map(route => {\r\n return {\r\n name: route.name,\r\n isSelected: 1,\r\n carriageways: route.carriageways.map(carriageway => {\r\n return {\r\n name: carriageway.name,\r\n id: carriageway.id,\r\n isSelected: true\r\n };\r\n })\r\n };\r\n });\r\n}\r\n\r\n/**\r\n * @description Sets the carriageway visibility based on the specified selections.\r\n * @export\r\n * @param {array} selections - the selections.\r\n * @param {array} routeOptions - The route options.\r\n * @returns {array} The route options with updated visibility.\r\n */\r\nexport function setCarriagewaySelections(selections, routeOptions) {\r\n const route = cloneObject(routeOptions),\r\n selectionRoute = selections.find(\r\n selection => selection.name === route.name\r\n );\r\n\r\n return {\r\n ...route,\r\n isSelected: selectionRoute ? selectionRoute.isSelected : CONSTANTS.ROUTE_SELECTION_STATE.deselected,\r\n carriageways: route.carriageways.map(carriageway => {\r\n const selectedCarriageway = selectionRoute\r\n ? selectionRoute.carriageways.find(\r\n selectionCarriageway =>\r\n carriageway.id === selectionCarriageway.id\r\n )\r\n : null;\r\n\r\n return {\r\n ...carriageway,\r\n isSelected:\r\n selectedCarriageway && selectedCarriageway.isSelected\r\n };\r\n })\r\n };\r\n}\r\n\r\n/**\r\n * @description A generator function to generate a day of timeframes.\r\n * @export\r\n * @param {number} [currentHour=0] - (Optional) define the hour from which to start.\r\n * @param {number} [currentInterval=0] - (Optional) define the interval from which to start.\r\n * @param {boolean} [dayLimit=true] - (Optional) flag to state whether to end the generator at the end of a day.\r\n * @param {*} [updateIntervalMins=CONSTANTS.UPDATE_INTERVAL_MINS] - (Optional) the interval ength in mins.\r\n */\r\nexport function* timeframeGenerator(\r\n currentHour = 0,\r\n currentInterval = 0,\r\n dayLimit = true,\r\n updateIntervalMins = CONSTANTS.UPDATE_INTERVAL_MINS\r\n) {\r\n const intervals = [];\r\n\r\n for (\r\n let intervalValue = 0;\r\n intervalValue < 60;\r\n intervalValue += updateIntervalMins\r\n ) {\r\n intervals.push((\"0\" + intervalValue).slice(-2));\r\n }\r\n\r\n while (true) {\r\n let previousTime = formatTimeAsString(\r\n currentHour,\r\n intervals[currentInterval]\r\n );\r\n\r\n currentInterval++;\r\n\r\n if (currentInterval >= intervals.length) {\r\n currentInterval = 0;\r\n currentHour++;\r\n\r\n if (currentHour >= 24) {\r\n currentHour = 0;\r\n\r\n if (dayLimit) {\r\n yield `${previousTime} - ${formatTimeAsString(\r\n currentHour,\r\n intervals[currentInterval]\r\n )}`;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n yield `${previousTime} - ${formatTimeAsString(\r\n currentHour,\r\n intervals[currentInterval]\r\n )}`;\r\n }\r\n}\r\n\r\n/**\r\n * @description Formats time as a string.\r\n * @param {number | string} hours - The hours.\r\n * @param {number | string} minutes - The minutes.\r\n * @returns {string} The formatted time.\r\n */\r\nfunction formatTimeAsString(hours, minutes) {\r\n const hoursInt = parseInt(hours, 10),\r\n hoursFormatted = (\"0\" + hoursInt).slice(-2),\r\n minutesInt = parseInt(minutes, 10),\r\n minutesFormatted = (\"0\" + minutesInt).slice(-2);\r\n\r\n return `${hoursFormatted}:${minutesFormatted}`;\r\n}\r\n\r\n/**\r\n * @description Deep clones an array or object using JSON stringify/parse.\r\n * @param {array | Object} existingArray - The existing array\r\n * @returns {array | Object} - The cloned array/object.\r\n */\r\nfunction cloneObject(existingArray) {\r\n return JSON.parse(JSON.stringify(existingArray));\r\n}\r\n","/**\r\n * @description HeatmapService module.\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module services/HeatmapService\r\n */\r\n\r\nimport { HubConnectionBuilder, LogLevel } from '@microsoft/signalr';\r\nimport * as HeatmapHelpers from '../helpers/HeatmapHelpers';\r\nimport CONSTANTS from '../config/Constants';\r\n\r\n/**\r\n * @description Service to handle heatmap data.\r\n * @class\r\n */\r\nclass HeatmapService {\r\n\r\n /**\r\n * Creates an instance of HeatmapService.\r\n * @param {Object} dataserviceHandlers - an object containing hanlders for data service events.\r\n * @param {Object} notificationService - the global instance of teh notifications service.\r\n */\r\n constructor(dataserviceHandlers, notificationService) {\r\n this.dataServiceHandlers = {\r\n onDataServiceMessage: () => null,\r\n onDataServiceOpen: () => null,\r\n ...dataserviceHandlers\r\n };\r\n\r\n this.notificationService = notificationService;\r\n this.currentRoute = null;\r\n this.currentDate = null;\r\n this.liveUpdateTimeouts = {};\r\n }\r\n\r\n /**\r\n * @description Handles the receiving of a message from the dataService.\r\n * @param {Object} event - The event\r\n */\r\n handleDataServiceMessage(message) {\r\n const dateTodayString = HeatmapHelpers.getDateStringFromDate(\r\n new Date(message.composed)\r\n ),\r\n response = {\r\n date: dateTodayString,\r\n carriageways: message.carriageways.map(carriageway => {\r\n return {\r\n id: `${message.roadName}_${carriageway.directionId}`,\r\n periods: carriageway.timeBins.reverse().map(timeBin => {\r\n return {\r\n ...timeBin,\r\n timeframe: HeatmapHelpers.convertTimeBinToTimeframe(\r\n timeBin.timeBin,\r\n timeBin.timeBinMinutes\r\n )\r\n };\r\n })\r\n };\r\n })\r\n };\r\n\r\n this.dataServiceHandlers.onDataServiceMessage(response);\r\n }\r\n\r\n /**\r\n * @description Handles the opening of the dataService.\r\n */\r\n handleDataServiceOpen() {\r\n const notificationDetails = {\r\n ...CONSTANTS.NOTIFICATIONS.RECONNECTION_MESSAGE\r\n };\r\n\r\n this.isDisconnected &&\r\n this.notificationService.addNotification(notificationDetails);\r\n this.dataServiceHandlers.onDataServiceOpen &&\r\n this.dataServiceHandlers.onDataServiceOpen();\r\n this.isDisconnected = false;\r\n }\r\n\r\n /**\r\n * @description Handles the closing of the dataService.\r\n */\r\n handleDataServiceClose() {\r\n const action = this.createDataServiceConnection.bind(this),\r\n notificationDetails = {\r\n ...CONSTANTS.NOTIFICATIONS.DISCONNECTION_MESSAGE,\r\n action: action,\r\n actionButtonText: 'Reconnect'\r\n },\r\n notification = this.notificationService.addNotification(\r\n notificationDetails\r\n );\r\n\r\n this.isDisconnected = true;\r\n\r\n // Attempts to reopen connection after 20s if it has not been reopened in this time.\r\n this.reconnectTimeout = setTimeout(() => {\r\n if (this.heatmapWebSocketService.readyState === 3) {\r\n this.createDataServiceConnection();\r\n this.notificationService.removeNotification(notification.id);\r\n } else if (this.heatmapWebSocketService.readyState === 1) {\r\n this.notificationService.removeNotification(notification.id);\r\n }\r\n }, CONSTANTS.DATA_SERVICE_RECONNECT_DELAY);\r\n }\r\n\r\n /**\r\n * @description Creates a connection to the dataService.\r\n */\r\n createDataServiceConnection() {\r\n return new Promise((resolve, reject) => {\r\n clearTimeout(this.reconnectTimeout);\r\n\r\n this.heatmapWebSocketService = new HubConnectionBuilder()\r\n .withUrl(process.env.HEATMAP_WSS_API_URL)\r\n .withAutomaticReconnect()\r\n .configureLogging(LogLevel.Warning)\r\n .build();\r\n\r\n this.heatmapWebSocketService.on(\r\n 'Initialise Journey Times',\r\n message => {\r\n this.handleDataServiceMessage(message);\r\n }\r\n );\r\n this.heatmapWebSocketService.on('Update Journey Times', message => {\r\n this.handleDataServiceMessage(message);\r\n });\r\n this.heatmapWebSocketService.onclose(error =>\r\n this.handleDataServiceClose(error)\r\n );\r\n this.heatmapWebSocketService\r\n .start()\r\n .then(() => {\r\n this.handleDataServiceOpen();\r\n resolve();\r\n })\r\n .catch(error => {\r\n const notification = {\r\n ...CONSTANTS.NOTIFICATIONS.CONNECTION_ERROR_MESSAGE,\r\n additionalInformation: error\r\n };\r\n\r\n reject(notification);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * @description Gets the delay band data.\r\n * @returns {array} The delay bands.\r\n */\r\n getDelayData() {\r\n return new Promise((resolve, reject) =>\r\n fetch(\r\n process.env.API_ROOT_URL + process.env.DELAY_DATA_API_ENDPOINT\r\n )\r\n .then(response => {\r\n if (!response.ok) {\r\n throw new Error(response.statusText);\r\n }\r\n\r\n return response.json();\r\n })\r\n .then(data => resolve(data))\r\n .catch(error => {\r\n const notification = {\r\n ...CONSTANTS.NOTIFICATIONS.DELAY_DATA_ERROR_MESSAGE,\r\n additionalInformation: error\r\n };\r\n\r\n reject(notification);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * @description Gets the available routes.\r\n * @returns {array} The list of routes.\r\n */\r\n getRoutes() {\r\n return new Promise((resolve, reject) =>\r\n fetch(process.env.API_ROOT_URL + process.env.ROUTES_API_ENDPOINT)\r\n .then(response => {\r\n if (!response.ok) {\r\n throw new Error(response.statusText);\r\n }\r\n\r\n return response.json();\r\n })\r\n .then(data => {\r\n data.forEach(route => {\r\n let junctions = [];\r\n\r\n route.carriageways.forEach(carriageway => {\r\n carriageway.links.sort(\r\n (a, b) => a.displayOrder - b.displayOrder\r\n );\r\n carriageway.junctions.sort(\r\n (a, b) => a.displayOrder - b.displayOrder\r\n );\r\n\r\n const startLinkId = carriageway.links[0].id,\r\n endLinkId =\r\n carriageway.links[\r\n carriageway.links.length - 1\r\n ].id,\r\n startLinkJunction = carriageway.junctions.find(\r\n junction => junction.linkId === startLinkId\r\n ),\r\n endLinkJunction = carriageway.junctions.find(\r\n junction => junction.linkId === endLinkId\r\n );\r\n\r\n if (!startLinkJunction) {\r\n carriageway.junctions.push({\r\n directionId: carriageway.direction.id,\r\n displayOrder: 0,\r\n label: 'Start',\r\n linkId: startLinkId,\r\n name: 'Start'\r\n });\r\n }\r\n\r\n if (!endLinkJunction) {\r\n carriageway.junctions.push({\r\n directionId: carriageway.direction.id,\r\n displayOrder:\r\n carriageway.junctions[\r\n carriageway.junctions.length - 1\r\n ].displayOrder + 1,\r\n label: 'End',\r\n linkId: endLinkId,\r\n name: 'End'\r\n });\r\n }\r\n\r\n carriageway.junctions = carriageway.junctions.map(\r\n junction => {\r\n const isJunction = !!parseInt(\r\n junction.label\r\n );\r\n\r\n return {\r\n ...junction,\r\n label: junction.label,\r\n isJunction: isJunction\r\n };\r\n }\r\n );\r\n carriageway.id = `${route.name}_${carriageway.direction.id}`;\r\n let carriagewayJunctions = carriageway.junctions\r\n .filter(\r\n junction =>\r\n !junctions.find(\r\n existingJunction =>\r\n existingJunction ===\r\n junction.label ||\r\n junction.label === ''\r\n )\r\n )\r\n .map(junction => junction.label);\r\n\r\n junctions = [...junctions, ...carriagewayJunctions];\r\n });\r\n route.junctions = junctions.sort((a, b) => {\r\n const parsedA = parseInt(a) || a,\r\n parsedB = parseInt(b) || b;\r\n\r\n // Sort junctions in following order (where applicable):\r\n // 'Start',\r\n // ,\r\n // ,\r\n // 'End'\r\n if (parsedA === 'Start' || parsedB === 'End') {\r\n return -1;\r\n } else if (\r\n parsedA === 'End' ||\r\n parsedB === 'Start'\r\n ) {\r\n return 1;\r\n } else if (\r\n typeof parsedA === 'string' &&\r\n typeof parsedB === 'string' &&\r\n parsedA.toUpperCase() < parsedB.toUpperCase()\r\n ) {\r\n return -1;\r\n } else if (\r\n typeof parsedA === 'string' &&\r\n typeof parsedB === 'string' &&\r\n parsedA.toUpperCase() > parsedB.toUpperCase()\r\n ) {\r\n return 1;\r\n } else if (\r\n typeof parsedA === 'string' &&\r\n typeof parsedB === 'string'\r\n ) {\r\n return 0;\r\n } else if (typeof parsedA === 'string') {\r\n return 1;\r\n } else if (typeof parsedB === 'string') {\r\n return -1;\r\n }\r\n\r\n return parseInt(parsedA) - parseInt(parsedB);\r\n });\r\n\r\n resolve(data);\r\n })\r\n }).catch(error => {\r\n const notification = {\r\n ...CONSTANTS.NOTIFICATIONS.ROUTE_DATA_ERROR_MESSAGE,\r\n additionalInformation: error\r\n };\r\n\r\n reject(notification);\r\n })\r\n );\r\n }\r\n\r\n /**\r\n * @description Gets the data for a given route and date.\r\n * @param {string} route - The route name.\r\n * @param {Object} date - The date for which data should be obtained.\r\n * @returns {Object} - The returned data.\r\n */\r\n getHeatmapData(route, date) {\r\n const dateTodayString = HeatmapHelpers.getDateStringFromDate(\r\n new Date()\r\n );\r\n\r\n date = date === '' ? dateTodayString : date;\r\n\r\n this.currentDoute = route;\r\n this.currentDate = date;\r\n\r\n const queryDate = date ? date : dateTodayString,\r\n url = `${process.env.API_ROOT_URL +\r\n process.env\r\n .JOURNEY_TIME_API_ENDPOINT}/${route}?date=${queryDate}&download=false`;\r\n\r\n return new Promise((resolve, reject) =>\r\n fetch(url)\r\n .then(response => {\r\n if (!response.ok) {\r\n throw new Error(response.statusText);\r\n }\r\n\r\n return response.json();\r\n })\r\n .then(json => {\r\n const data = {\r\n date: queryDate,\r\n carriageways: json.map(carriageway => {\r\n return {\r\n id: `${route}_${carriageway.directionId}`,\r\n periods: carriageway.timeBins\r\n .reverse()\r\n .map(timeBin => {\r\n return {\r\n ...timeBin,\r\n timeframe: HeatmapHelpers.convertTimeBinToTimeframe(\r\n timeBin.timeBin,\r\n timeBin.timeBinMinutes\r\n )\r\n };\r\n })\r\n };\r\n })\r\n };\r\n\r\n resolve(data);\r\n })\r\n .catch(error => {\r\n const notification = {\r\n ...CONSTANTS.NOTIFICATIONS.HEATMAP_DATA_ERROR_MESSAGE,\r\n additionalInformation: error\r\n };\r\n\r\n reject(notification);\r\n })\r\n );\r\n }\r\n}\r\n\r\nexport default HeatmapService;\r\n","/**\r\n * @description HeatmapNotification module.\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module services/classes/HeatmapNotification\r\n */\r\n\r\n/**\r\n * @description A class that handles an individual notification.\r\n * @class\r\n */\r\nclass HeatmapNotification {\r\n\r\n /**\r\n * Creates an instance of Notification.\r\n * @param {string | number} id - a unique Id for the notification.\r\n * @param {Object} notification - an object containing the notification information.\r\n */\r\n constructor(id, notification) {\r\n this.id = id;\r\n this.type = notification.type || 'info';\r\n this.title = notification.title;\r\n this.message = notification.message;\r\n this.actionButtonText = notification.actionButtonText || 'OK';\r\n this.action = notification.action;\r\n this.onClose = notification.onClose;\r\n this.timestamp = new Date();\r\n }\r\n\r\n /**\r\n * @description Returns the notification details.\r\n * @returns The notification details.\r\n */\r\n getNotification() {\r\n return {\r\n id: this.id,\r\n type: this.type,\r\n title: this.title,\r\n message: this.message,\r\n hasAction: !!this.action,\r\n actionButtonText: this.actionButtonText\r\n };\r\n }\r\n\r\n /**\r\n * @description triggers the action.\r\n */\r\n handleAction() {\r\n this.action && this.action();\r\n }\r\n\r\n /**\r\n * @description Triggers the close action.\r\n */\r\n handleClose() {\r\n this.onClose && this.onClose();\r\n }\r\n}\r\n\r\nexport default HeatmapNotification;","/**\r\n * @description NotificationService module.\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module services/NotificationService\r\n */\r\n\r\nimport HeatmapNotification from './classes/Notification';\r\nimport CONSTANTS from '../config/Constants';\r\n\r\n/**\r\n * @description A class that handles notifications.\r\n * @class\r\n */\r\nclass NotificationService {\r\n\r\n /**\r\n * Creates an instance of NotificationService.\r\n * @param {Function} [onNotificationsUpdate=() => null] - (Optional) handler for when notifications are updated.\r\n */\r\n constructor(onNotificationsUpdate = () => null) {\r\n this.idIterator = this.idGenerator();\r\n this.onNotificationsUpdate = onNotificationsUpdate;\r\n }\r\n\r\n /**\r\n * @description Adds a notification.\r\n * @param {Object} notificationDetails - The notification details.\r\n * @returns The created notification.\r\n */\r\n addNotification(notificationDetails) {\r\n const notification = new HeatmapNotification(\r\n this.idIterator.next().value,\r\n notificationDetails\r\n );\r\n\r\n notifications.push(notification);\r\n this.onNotificationsUpdate(this.getNotifications());\r\n\r\n if (notificationDetails.type === 'toast') {\r\n // setTimeout to only show a toast notification for a set time.\r\n setTimeout(\r\n () => this.removeNotification(notification.id),\r\n CONSTANTS.TOAST_DISPLAY_TIME\r\n );\r\n }\r\n\r\n notificationDetails.additionalInformation &&\r\n console.log(notificationDetails.additionalInformation);\r\n\r\n return notification.getNotification();\r\n }\r\n\r\n /**\r\n * @description Removes a notification by id.\r\n * @param {number} id - The notification id.\r\n * @returns The deleted notification.\r\n */\r\n removeNotification(id) {\r\n const index = notifications.findIndex(\r\n notification => notification.id === id\r\n );\r\n\r\n if (index < 0) {\r\n return null;\r\n }\r\n\r\n const notification = notifications.splice(index, 1)[0];\r\n\r\n notification.handleClose();\r\n this.onNotificationsUpdate(this.getNotifications());\r\n\r\n return notification.getNotification();\r\n }\r\n\r\n /**\r\n * @description Gets all notifications.\r\n * @returns A list of all notifications.\r\n */\r\n getNotifications() {\r\n return notifications.map(notification =>\r\n notification.getNotification()\r\n );\r\n }\r\n\r\n /**\r\n * @description Gets a notification by id.\r\n * @param {number} id - The notification id.\r\n * @returns The notification.\r\n */\r\n getNotificationById(id) {\r\n return notifications\r\n .find(notification => notification.id === id)\r\n .getNotification();\r\n }\r\n\r\n /**\r\n * @description Triggers the action of a notification.\r\n * @param {number} id - The id of the notification.\r\n */\r\n triggerNotificationAction(id) {\r\n const notification = notifications.find(\r\n notification => notification.id === id\r\n );\r\n\r\n notification.handleAction();\r\n this.removeNotification(notification.id);\r\n }\r\n\r\n /**\r\n * @description A generator function to create unique ids.\r\n */\r\n *idGenerator() {\r\n let id = 0;\r\n\r\n while (true) {\r\n yield id++;\r\n }\r\n }\r\n}\r\n\r\nconst notifications = [];\r\n\r\nexport default NotificationService;\r\n","/**\r\n * @description Reset Context\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module context/ResetContext\r\n */\r\n\r\nimport React from 'react';\r\n\r\nexport default React.createContext(() => null);","/**\r\n * @description Notification component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Notification\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\n/**\r\n * @description The Notification component.\r\n * @class \r\n * @extends {React.Component}\r\n */\r\nclass Notification extends React.Component {\r\n constructor(props) {\r\n super(props);\r\n this.actionButton = React.createRef();\r\n this.closeButton = React.createRef();\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidMount() {\r\n this.props.notification.hasAction\r\n ? this.actionButton.current.focus()\r\n : this.closeButton.current.focus();\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n return (\r\n
\r\n
\r\n
\r\n {this.props.notification.title}\r\n
\r\n \r\n close\r\n \r\n
\r\n
\r\n
\r\n
\r\n {this.props.notification.message}\r\n
\r\n {this.props.notification.hasAction && (\r\n \r\n {this.props.notification.actionButtonText}\r\n \r\n )}\r\n
\r\n
\r\n );\r\n }\r\n\r\n /**\r\n * @description Handles the click of the close button.\r\n */\r\n handleCloseClick() {\r\n this.props.onCloseNotification(this.props.notification.id);\r\n }\r\n\r\n /**\r\n * @description Handles the click of the action button.\r\n */\r\n handleActionButtonClick() {\r\n this.props.onClickNotificationAction(this.props.notification.id);\r\n }\r\n}\r\n\r\nNotification.defaultProps = {\r\n notification: {\r\n id: 0,\r\n title: '',\r\n message: '',\r\n actionButtonText: ''\r\n },\r\n onCloseNotification: () => null,\r\n onClickNotificationAction: () => null\r\n};\r\n\r\nNotification.propTypes = {\r\n notification: PropTypes.object.isRequired,\r\n onCloseNotification: PropTypes.func.isRequired,\r\n onClickNotificationAction: PropTypes.func.isRequired\r\n};\r\n\r\nexport default Notification;\r\n","/**\r\n * @description Notifications component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Notifications\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport Notification from './Notification';\r\n\r\n/**\r\n * @description The Notifications component.\r\n * @class \r\n * @extends {React.Component}\r\n */\r\nclass Notifications extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n this.state = {\r\n fadeIn: false,\r\n isVisible: false\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n return (\r\n this.props.notifications !== nextProps.notifications ||\r\n this.state.fadeIn !== nextState.fadeIn ||\r\n this.state.isVisible !== nextState.isVisible\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidUpdate(prevProps, prevState) {\r\n if (\r\n prevProps.notifications.length !==\r\n this.props.notifications.length &&\r\n prevProps.notifications.length === 0\r\n ) {\r\n clearTimeout(this.fadeTimeout);\r\n this.setState({\r\n isVisible: true\r\n });\r\n // Timeout needed to allow for render before fade in.\r\n setTimeout(() =>\r\n this.setState({\r\n fadeIn: true\r\n }), 0\r\n );\r\n } else if (prevState.fadeIn === true && this.state.fadeIn === false) { \r\n // Timeout needed to allow for fade out before hiding notifications.\r\n this.fadeTimeout = setTimeout(() =>\r\n this.setState({\r\n isVisible: false\r\n }), 200\r\n );\r\n } else if (this.props.notifications.length === 0) {\r\n this.setState({\r\n fadeIn: false\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentWillUnmount() {\r\n clearTimeout(this.fadeTimeout);\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const className = `Notifications${this.state.fadeIn ? ' active' : ''}`;\r\n\r\n return (\r\n this.state.isVisible && (\r\n
\r\n {this.props.notifications.map(notification => (\r\n \r\n ))}\r\n
\r\n )\r\n );\r\n }\r\n}\r\n\r\nNotifications.defaultProps = {\r\n notifications: [],\r\n onCloseNotification: () => null,\r\n onClickNotificationAction: () => null\r\n};\r\n\r\nNotifications.propTypes = {\r\n notifications: PropTypes.array.isRequired,\r\n onCloseNotification: PropTypes.func.isRequired,\r\n onClickNotificationAction: PropTypes.func.isRequired\r\n};\r\n\r\nexport default Notifications;\r\n","/**\r\n * @description MultiSelectBoxOption component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/MultiSelectBoxOption\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport CONSTANTS from \"../config/Constants\";\r\n\r\n/**\r\n * @description The MultiSelectBoxOption component.\r\n * @param {Object} props - The props object.\r\n * @returns The JSX representation of the React elemnts to render.\r\n */\r\nfunction MultiSelectBoxOption(props) {\r\n \r\n return (\r\n \r\n );\r\n\r\n /**\r\n * @description handles the toggling of the option.\r\n */\r\n function handleToggleOption() {\r\n props.onToggleOption(props.value);\r\n }\r\n}\r\n\r\nMultiSelectBoxOption.defaultProps = {\r\n name: '',\r\n id: '',\r\n isSelected: CONSTANTS.ROUTE_SELECTION_STATE.unselected,\r\n onToggleOption: () => null\r\n};\r\n\r\nMultiSelectBoxOption.propTypes = {\r\n name: PropTypes.string.isRequired,\r\n value: PropTypes.oneOfType([\r\n PropTypes.string,\r\n PropTypes.number\r\n ]),\r\n isSelected: PropTypes.oneOfType([\r\n PropTypes.bool,\r\n PropTypes.number\r\n ]),\r\n onToggleOption: PropTypes.func.isRequired\r\n};\r\n\r\nexport default MultiSelectBoxOption;\r\n","/**\r\n * @description MultiSelectBox component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/MultiSelectBox\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport MultiSelectBoxOption from './MultiSelectBoxOption';\r\n\r\nimport CONSTANTS from \"../config/Constants\";\r\n\r\n/**\r\n * @description The MultiSelectBox component.\r\n * @class MultiSelectBox\r\n * @extends {React.Component}\r\n */\r\nclass MultiSelectBox extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n this.state = {\r\n isExpanded: false\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n return this.props.options !== nextProps.options || this.state.isExpanded !== nextState.isExpanded;\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n return (\r\n
\r\n \r\n {this.state.isExpanded && (\r\n
    \r\n {this.props.options &&\r\n this.props.options.map(option => (\r\n
  • \r\n \r\n {option.options && (\r\n
      \r\n {option.options.map(subOption => (\r\n
    • \r\n \r\n
    • \r\n ))}\r\n
    \r\n )}\r\n
  • \r\n ))}\r\n
\r\n )}\r\n
\r\n );\r\n }\r\n\r\n /**\r\n * @description Handles the toggling of the multiselectbox.\r\n */\r\n onToggleExpand() {\r\n const newState = !this.state.isExpanded;\r\n \r\n this.closeList = this.closeSelectBox.bind(this);\r\n newState && document.addEventListener('click', this.closeList); \r\n this.setState({\r\n isExpanded: newState\r\n });\r\n }\r\n\r\n /**\r\n * @description Closes the multiselectbox.\r\n * @param {Object} event - the click event.\r\n * @returns {void}\r\n */\r\n closeSelectBox(event) {\r\n if (event && event.target.closest('.MultiSelectBox')) {\r\n return;\r\n }\r\n\r\n document.removeEventListener('click', this.closeList);\r\n this.setState({\r\n isExpanded: false\r\n });\r\n }\r\n}\r\n\r\nMultiSelectBox.defaultProps = {\r\n options: [],\r\n onToggleOption: () => null\r\n};\r\n\r\nMultiSelectBox.propTypes = {\r\n options: PropTypes.array.isRequired,\r\n onToggleOption: PropTypes.func.isRequired\r\n};\r\n\r\nexport default MultiSelectBox;\r\n","/**\r\n * @description Nav component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Nav\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport MultiSelectBox from './MultiSelectBox';\r\nimport CONSTANTS from '../config/Constants';\r\n\r\n/**\r\n * @description The Nav component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass Nav extends React.Component {\r\n \r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n\r\n this.state = {\r\n routeSelections: JSON.parse(\r\n JSON.stringify(this.props.routeSelections)\r\n ),\r\n selectedDate: this.props.selectedDate.toString()\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const date = new Date();\r\n\r\n return (\r\n
\r\n \r\n
\r\n
\r\n

\r\n Journey Time Heatmap{' '}\r\n {this.props.environmentName &&\r\n this.props.environmentName !== 'Production' && (\r\n
\r\n ({this.props.environmentName})\r\n
\r\n )}\r\n

\r\n \r\n {\r\n return {\r\n ...rest,\r\n options: carriageways\r\n };\r\n }\r\n )}\r\n onToggleOption={this.handleRouteToggle.bind(this)}\r\n >\r\n \r\n \r\n \r\n \r\n Apply\r\n \r\n \r\n Reset\r\n \r\n \r\n
\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n return (\r\n this.props.showMenu !== nextProps.showMenu ||\r\n this.props.routeSelections !== nextProps.routeSelections ||\r\n this.state.routeSelections !== nextState.routeSelections ||\r\n this.props.selectedDate !== nextProps.selectedDate ||\r\n this.state.selectedDate !== nextState.selectedDate\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidUpdate(prevProps) {\r\n const state = {};\r\n\r\n if (prevProps.routeSelections !== this.props.routeSelections) {\r\n state.routeSelections = JSON.parse(\r\n JSON.stringify(this.props.routeSelections)\r\n );\r\n }\r\n\r\n if (prevProps.selectedDate !== this.props.selectedDate) {\r\n state.selectedDate = this.props.selectedDate.toString();\r\n }\r\n\r\n Object.keys(state).length > 0 && this.setState(state);\r\n }\r\n\r\n /**\r\n * @description Handles the toggling of a route.\r\n * @param {string} id - The route ID.\r\n */\r\n handleRouteToggle(id) {\r\n const splitId = id.toString().split('_'),\r\n routeName = splitId[0],\r\n directionId = splitId[1] && parseInt(splitId[1], 10),\r\n routeSelections = JSON.parse(\r\n JSON.stringify(this.state.routeSelections)\r\n ),\r\n selectedRoute = routeSelections.find(\r\n route => route.name === routeName\r\n );\r\n\r\n if (selectedRoute && directionId) {\r\n const selectedCarriageway = selectedRoute.carriageways.find(\r\n carriageway => carriageway.id === id\r\n );\r\n\r\n if (selectedCarriageway) {\r\n selectedCarriageway.isSelected = !selectedCarriageway.isSelected;\r\n\r\n selectedRoute.isSelected = selectedRoute.carriageways.every(\r\n carriageway => carriageway.isSelected\r\n )\r\n ? CONSTANTS.ROUTE_SELECTION_STATE.complete\r\n : selectedRoute.carriageways.some(\r\n carriageway => carriageway.isSelected\r\n )\r\n ? CONSTANTS.ROUTE_SELECTION_STATE.partial\r\n : CONSTANTS.ROUTE_SELECTION_STATE.deselected;\r\n }\r\n } else if (selectedRoute) {\r\n selectedRoute.isSelected =\r\n selectedRoute.isSelected ===\r\n CONSTANTS.ROUTE_SELECTION_STATE.complete\r\n ? CONSTANTS.ROUTE_SELECTION_STATE.deselected\r\n : CONSTANTS.ROUTE_SELECTION_STATE.complete;\r\n\r\n selectedRoute.carriageways = selectedRoute.carriageways.map(\r\n carriageway => {\r\n return {\r\n ...carriageway,\r\n isSelected:\r\n selectedRoute.isSelected ===\r\n CONSTANTS.ROUTE_SELECTION_STATE.complete\r\n };\r\n }\r\n );\r\n }\r\n\r\n this.setState({\r\n routeSelections: routeSelections\r\n });\r\n }\r\n\r\n /**\r\n * @description Sets the selectedDate state parameter on changing the input value.\r\n * @param {Object} event - The event.\r\n */\r\n handleDateChange(event) {\r\n this.setState({ selectedDate: event.target.value });\r\n }\r\n\r\n /**\r\n * @description Submits the form.\r\n * @param {Object} event - The submit event.\r\n */\r\n handleFormSubmit(event) {\r\n event.preventDefault();\r\n this.props.onChangeRouteOptions(\r\n this.state.routeSelections,\r\n this.state.selectedDate\r\n );\r\n }\r\n\r\n /**\r\n * @description Handles the reset click event.\r\n */\r\n handleResetClick() {\r\n this.props.onResetFilters();\r\n }\r\n}\r\n\r\nNav.defaultProps = {\r\n routeOptions: [],\r\n onToggleRoute: () => null,\r\n selectedDate: 'yyyy-mm-dd',\r\n onChangeDate: () => null,\r\n showMenu: false,\r\n toggleMenu: () => null,\r\n onChangeRouteOptions: () => null,\r\n onResetFilters: () => null\r\n};\r\n\r\nNav.propTypes = {\r\n environmentName: PropTypes.string,\r\n routeSelections: PropTypes.array.isRequired,\r\n onToggleRoute: PropTypes.func.isRequired,\r\n selectedDate: PropTypes.string.isRequired,\r\n onChangeDate: PropTypes.func.isRequired,\r\n showMenu: PropTypes.bool.isRequired,\r\n toggleMenu: PropTypes.func.isRequired,\r\n onChangeRouteOptions: PropTypes.func.isRequired,\r\n onResetFilters: PropTypes.func.isRequired\r\n};\r\n\r\nexport default Nav;\r\n","/**\r\n * @description Menu component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Menu\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\n/**\r\n * @description The Menu component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass Menu extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n\r\n this.state = {\r\n showMenu: false\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n return (\r\n this.state.showMenu !== nextState.showMenu ||\r\n this.props.delays !== nextProps.delays ||\r\n this.props.selectedDelays !== nextProps.selectedDelays\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const className = this.state.showMenu ? 'Menu-container expanded' : 'Menu-container';\r\n\r\n return (\r\n this.props.delays.length > 0 && (\r\n
\r\n
\r\n
\r\n \r\n

Delays

\r\n \r\n {this.state.showMenu ? 'keyboard_arrow_down' : 'keyboard_arrow_up'}\r\n \r\n \r\n
\r\n
\r\n {this.props.delays.map((delay, index) => (\r\n
\r\n \r\n
\r\n ))}\r\n
\r\n
\r\n
\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @description Handles the toggling of delay bands.\r\n * @param {Object} event - The event.\r\n */\r\n handleDelayToggle(event) {\r\n const selectedDelays = [...this.props.selectedDelays],\r\n index = parseInt(event.currentTarget.value, 10);\r\n\r\n selectedDelays[index] = !selectedDelays[index];\r\n this.props.onChangeSelectedDelays(selectedDelays);\r\n }\r\n\r\n /**\r\n * @description Handles the click event of the menu button.\r\n */\r\n handleMenuButtonClick() {\r\n this.setState({\r\n showMenu: !this.state.showMenu\r\n });\r\n }\r\n}\r\n\r\nMenu.defaultProps = {\r\n delays: [],\r\n selectedDelays: [],\r\n\r\n onChangeSelectedDelays: () => null\r\n};\r\n\r\nMenu.propTypes = {\r\n delays: PropTypes.array.isRequired,\r\n selectedDelays: PropTypes.array.isRequired,\r\n\r\n onChangeSelectedDelays: PropTypes.func.isRequired\r\n};\r\n\r\nexport default Menu;\r\n","/**\r\n * @description Footer component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Footer\r\n */\r\n\r\nimport React from 'react';\r\n\r\n/**\r\n * @description The Footer component.\r\n * @class\r\n * @extends {React.PureComponent}\r\n */\r\nclass Footer extends React.PureComponent {\r\n \r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n return (\r\n
\r\n
\r\n © IBI Group {new Date().getFullYear()}\r\n
\r\n \r\n
\r\n );\r\n }\r\n}\r\n\r\nexport default Footer;\r\n","/**\r\n * @description HeatmapHeader component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/HeatmapHeader\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\n/**\r\n * @description The HeatmapHeader component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass HeatmapHeader extends React.Component {\r\n \r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps) {\r\n return (\r\n JSON.stringify(this.props.junctions) !==\r\n JSON.stringify(nextProps.junctions) ||\r\n JSON.stringify(this.props.links) !== JSON.stringify(nextProps.links)\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n return (\r\n
\r\n
\r\n {this.props.direction}\r\n
\r\n {this.props.links.map(link => this.renderLink(link))}\r\n
\r\n );\r\n }\r\n\r\n renderLink(link) {\r\n const junctions = this.props.junctions.filter(\r\n junction => junction.linkId === link.id\r\n ),\r\n className = `HeatmapHeader-text-container${\r\n junctions.length === 1 && junctions[0].offsetPercent > 50\r\n ? ' justify-end'\r\n : junctions.length === 1 &&\r\n junctions[0].offsetPercent === 50\r\n ? ' justify-center'\r\n : ''\r\n }`;\r\n\r\n return (\r\n \r\n {junctions.map(junction => (\r\n
\r\n \r\n \r\n {junction.label}\r\n
\r\n \r\n \r\n ))}\r\n \r\n );\r\n }\r\n\r\n /**\r\n * @description Handles the hovering of the mouse over a junction.\r\n * @param {Object} junction - The junction.\r\n * @param {Object} event - The mouseover event.\r\n */\r\n handleMouseOverJunction(junction, event) {\r\n event.persist();\r\n this.props.openTooltip(event, junction);\r\n }\r\n}\r\n\r\nHeatmapHeader.defaultProps = {\r\n totalDistance: 0,\r\n direction: '',\r\n links: [],\r\n junctions: [],\r\n openTooltip: () => null,\r\n closeTooltip: () => null\r\n};\r\n\r\nHeatmapHeader.propTypes = {\r\n totalDistance: PropTypes.number.isRequired,\r\n direction: PropTypes.string.isRequired,\r\n links: PropTypes.array.isRequired,\r\n junctions: PropTypes.array.isRequired,\r\n openTooltip: PropTypes.func.isRequired,\r\n closeTooltip: PropTypes.func.isRequired\r\n};\r\n\r\nexport default HeatmapHeader;\r\n","/**\r\n * @description HeatmapPeriodSection component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/HeatmapPeriodSection\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\n/**\r\n * @description The HeatmapPeriodSection component.\r\n * @class \r\n * @extends {React.Component}\r\n */\r\nclass HeatmapPeriodSection extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n\r\n this.state = {\r\n showTooltip: false,\r\n fadeIn: false\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n return (\r\n this.state.showTooltip !== nextState.showTooltip ||\r\n this.state.fadeIn !== nextState.fadeIn ||\r\n this.props.link !== nextProps.link\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidMount() {\r\n // Timeout allows fadeIn transition to work correctly\r\n this.timeout = setTimeout(\r\n () =>\r\n this.setState({\r\n fadeIn: true\r\n }),\r\n 0\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentWillUnmount() {\r\n document.removeEventListener('click', this.documentClickCallback);\r\n clearTimeout(this.timeout);\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const transitionEnterTimeout = 1500 * (this.props.index / this.props.count),\r\n sectionStyle = {\r\n transitionDelay: this.props.isLatest\r\n ? `${transitionEnterTimeout}ms`\r\n : 0\r\n },\r\n classNames = `HeatmapPeriodSection-button delay-colour-${this.props.link.delayBand && this.props.link.delayBand.id ? this.props.link.delayBand.id : 0}${\r\n this.state.fadeIn ? ' fadeIn-visible' : ''\r\n }${this.props.isLatest ? ' fadeIn' : ''}${\r\n this.state.showTooltip ? ' active' : ''\r\n }`;\r\n\r\n return (\r\n \r\n );\r\n }\r\n\r\n /**\r\n * @description Sets the tooltip state.\r\n * @param {bool} newState - The state of the tooltip\r\n */\r\n setTooltipState(newState) {\r\n this.setState({\r\n showTooltip: newState\r\n });\r\n }\r\n\r\n /**\r\n * @description Handles the clicking of the button.\r\n * @param {Object} event - The click event.\r\n */\r\n handleButtonClick(event) {\r\n event.persist();\r\n this.setTooltipState(true);\r\n this.props.openTooltip(\r\n event,\r\n this.props.link,\r\n this.setTooltipState.bind(this)\r\n );\r\n }\r\n}\r\n\r\nHeatmapPeriodSection.defaultProps = {\r\n index: 0,\r\n count: 0,\r\n link: {},\r\n isLatest: false,\r\n openTooltip: () => {}\r\n};\r\n\r\nHeatmapPeriodSection.propTypes = {\r\n index: PropTypes.number.isRequired,\r\n count: PropTypes.number.isRequired,\r\n link: PropTypes.object.isRequired,\r\n isLatest: PropTypes.bool.isRequired,\r\n openTooltip: PropTypes.func.isRequired\r\n};\r\n\r\nexport default HeatmapPeriodSection;\r\n","/**\r\n * @description HeatmapPeriodTableCell component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/HeatmapPeriodTableCell\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport HeatmapPeriodSection from './HeatmapPeriodSection';\r\n\r\n/**\r\n * @description The HeatmapPeriodTableCell component.\r\n * @class \r\n * @extends {React.Component}\r\n */\r\nclass HeatmapPeriodTableCell extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps) {\r\n return (\r\n JSON.stringify(this.props.links) !==\r\n JSON.stringify(nextProps.links) ||\r\n JSON.stringify(this.props.journeyTime) !==\r\n JSON.stringify(nextProps.journeyTime) ||\r\n this.props.totalDistance !== nextProps.totalDistance ||\r\n this.props.showTimeBins !== nextProps.showTimeBins\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const currentLink = this.props.links.find(\r\n link => link.id === this.props.journeyTime.linkId\r\n ),\r\n style = {\r\n width: currentLink ? `${(currentLink.lengthMetres / this.props.totalDistance) *\r\n 100}%` : 0\r\n };\r\n\r\n return (\r\n this.props.showTimeBins &&
\r\n \r\n
\r\n );\r\n }\r\n}\r\n\r\nHeatmapPeriodTableCell.defaultProps = {\r\n index: 0,\r\n count: 0,\r\n totalDistance: 0,\r\n isLatest: false,\r\n showTimeBins: false,\r\n journeyTime: {},\r\n links: [],\r\n openTooltip: () => null\r\n};\r\n\r\nHeatmapPeriodTableCell.propTypes = {\r\n index: PropTypes.number.isRequired,\r\n count: PropTypes.number.isRequired,\r\n totalDistance: PropTypes.number.isRequired,\r\n isLatest: PropTypes.bool.isRequired,\r\n showTimeBins: PropTypes.bool.isRequired,\r\n journeyTime: PropTypes.object.isRequired,\r\n timeframe: PropTypes.string.isRequired,\r\n links: PropTypes.array.isRequired,\r\n openTooltip: PropTypes.func.isRequired\r\n};\r\n\r\nexport default HeatmapPeriodTableCell;\r\n","/**\r\n * @description HeatmapPeriod component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/HeatmapPeriod\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport HeatmapPeriodTableCell from './HeatmapPeriodTableCell';\r\n\r\n/**\r\n * @description The HeatmapPeriod component.\r\n * @class \r\n * @extends {React.Component}\r\n */\r\nclass HeatmapPeriod extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps) {\r\n return (\r\n JSON.stringify(this.props.links) !==\r\n JSON.stringify(nextProps.links) ||\r\n JSON.stringify(this.props.period) !==\r\n JSON.stringify(nextProps.period) ||\r\n this.props.totalDistance !== nextProps.totalDistance ||\r\n this.props.showTimeBins !== nextProps.showTimeBins\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n return this.props.period.isVisible && (\r\n
\r\n
\r\n {this.props.period.timeframe}\r\n
\r\n {this.props.period.journeyTimes ? (\r\n this.props.period.journeyTimes\r\n .filter(journeyTime =>\r\n this.props.links.find(\r\n visibleLink => visibleLink.id === journeyTime.linkId\r\n )\r\n )\r\n .map((journeyTime, index) => (\r\n \r\n ))\r\n ) : (\r\n
\r\n )}\r\n
\r\n );\r\n }\r\n}\r\n\r\nHeatmapPeriod.defaultProps = {\r\n isLatest: false,\r\n period: {},\r\n totalDistance: 0,\r\n showTimeBins: true,\r\n links: [],\r\n openTooltip: () => null,\r\n closeTooltip: () => null\r\n};\r\n\r\nHeatmapPeriod.propTypes = {\r\n period: PropTypes.object.isRequired,\r\n totalDistance: PropTypes.number.isRequired,\r\n isLatest: PropTypes.bool.isRequired,\r\n showTimeBins: PropTypes.bool.isRequired,\r\n links: PropTypes.array.isRequired,\r\n openTooltip: PropTypes.func.isRequired,\r\n closeTooltip: PropTypes.func.isRequired\r\n};\r\n\r\nexport default HeatmapPeriod;\r\n","/**\r\n * @description Heatmap component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Heatmap\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\nimport SimpleBar from 'simplebar-react';\r\n\r\nimport 'simplebar/dist/simplebar.min.css';\r\n\r\nimport ResetContext from '../context/ResetContext';\r\n\r\nimport HeatmapHeader from './HeatmapHeader';\r\nimport HeatmapPeriod from './HeatmapPeriod';\r\n\r\n/**\r\n * @description The Heatmap component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass Heatmap extends React.Component {\r\n \r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n this.heatmapContainer = React.createRef();\r\n this.simpleBarRef = React.createRef();\r\n\r\n this.state = {\r\n isExpanded: false,\r\n isExpanding: false,\r\n showTimeBins: false,\r\n heatmapHeight: props.heatmapDragService.minimumHeatmapHeightLive\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n return (\r\n JSON.stringify(this.props.periods) !==\r\n JSON.stringify(nextProps.periods) ||\r\n JSON.stringify(this.props.links) !==\r\n JSON.stringify(nextProps.links) ||\r\n this.props.zoomLevel !== nextProps.zoomLevel ||\r\n this.props.distance !== nextProps.distance ||\r\n this.state.isExpanded !== nextState.isExpanded ||\r\n this.props.isLive !== nextProps.isLive ||\r\n this.state.heatmapHeight !== nextState.heatmapHeight ||\r\n this.state.showTimeBins !== nextState.showTimeBins\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidMount() {\r\n this.resetHeight();\r\n this.context(this.props.id, this.resetHeight.bind(this));\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidUpdate(prevProps) {\r\n // Below code is to recalculate SimpleBar after changing zoom level.\r\n // Needs setTimeout in order to allow for setting state and rendering changes before recalculating.\r\n if (prevProps.zoomLevel !== this.props.zoomLevel) {\r\n this.recalcTimeout = setTimeout(\r\n () =>\r\n this.simpleBarRef.current &&\r\n this.simpleBarRef.current.recalculate(),\r\n 0\r\n );\r\n }\r\n\r\n if (prevProps.isLive !== this.props.isLive) {\r\n this.setState({\r\n heatmapHeight: this.props.isLive\r\n ? this.props.heatmapDragService.minimumHeatmapHeightLive\r\n : this.props.heatmapDragService.minimumHeatmapHeight\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentWillUnmount() {\r\n this.recalcTimeout && clearTimeout(this.recalcTimeout);\r\n this.simpleBarRef.current &&\r\n this.simpleBarRef.current\r\n .getScrollElement()\r\n .removeEventListener('scroll', this.props.closeTooltip);\r\n this.context(this.props.id, this.resetHeight.bind(this), true);\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const tableStyle = { width: `${100 * this.props.zoomLevel}%` },\r\n simpleBarClass = `Heatmap${\r\n this.state.isExpanded ? ' expanded' : ''\r\n }`,\r\n heatmapStyle = {\r\n height: `${this.state.heatmapHeight}px`\r\n };\r\n\r\n return (\r\n
\r\n \r\n
\r\n
\r\n {this.props.direction}\r\n
\r\n {this.props.isLive && this.props.periods[0] && (\r\n
\r\n {this.props.periods[0].timeframe}\r\n
\r\n )}\r\n
Latest
\r\n
\r\n \r\n
\r\n
\r\n \r\n {this.props.isLive && this.props.periods[0] && (\r\n \r\n )}\r\n {this.props.isLive &&\r\n this.props.periods.length === 0 && (\r\n
\r\n
\r\n
\r\n Waiting for heatmap data\r\n
\r\n
\r\n )}\r\n
\r\n
\r\n {this.props.periods.map(\r\n (period, index) =>\r\n (!this.props.isLive || index !== 0) && (\r\n \r\n )\r\n )}\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n drag_indicator\r\n \r\n
\r\n \r\n );\r\n }\r\n\r\n /**\r\n * @description Handles the opening of a link tooltip on click.\r\n * @param {Object} event - the click event.\r\n * @param {Object} link - the link.\r\n * @param {Function} setButtonState - callback to set button state.\r\n */\r\n onClickLink(event, link, setButtonState) {\r\n this.simpleBarRef.current &&\r\n this.simpleBarRef.current\r\n .getScrollElement()\r\n .addEventListener('scroll', this.props.closeTooltip);\r\n this.props.openTooltip(event, link, setButtonState);\r\n }\r\n\r\n /**\r\n * @description Handles the closing of the tooltip.\r\n */\r\n closeTooltip() {\r\n this.simpleBarRef.current &&\r\n this.simpleBarRef.current\r\n .getScrollElement()\r\n .removeEventListener('scroll', this.props.closeTooltip);\r\n this.props.closeTooltip();\r\n }\r\n\r\n /**\r\n * @description Sets the new height of the heatmap.\r\n * @param {number} height - The new height.\r\n * @param {function} callback - A callback to run once the state has been updated.\r\n */\r\n setHeight(height, callback) {\r\n this.heatmapContainer.current &&\r\n this.heatmapContainer.current.style.setProperty(\r\n '--junction-line-height',\r\n `${height - 34}px`\r\n );\r\n this.setState(\r\n {\r\n heatmapHeight: height\r\n },\r\n callback && callback()\r\n );\r\n }\r\n\r\n /**\r\n * @description Resets the height of the heatmap.\r\n */\r\n resetHeight() {\r\n this.setHeight(\r\n this.props.isLive\r\n ? this.props.heatmapDragService.minimumHeatmapHeightLive\r\n : this.props.heatmapDragService.minimumHeatmapHeight\r\n );\r\n this.setState({\r\n isExpanded: false,\r\n isExpanding: false\r\n });\r\n }\r\n\r\n /**\r\n * @description Handles the mouse down event on the drag handle.\r\n * @param {Object} event - The mousedown event.\r\n */\r\n onDragHandleMouseDown(event) {\r\n this.setState({\r\n isExpanding: true,\r\n isExpanded: true\r\n });\r\n\r\n this.props.heatmapDragService.handleStartDragging(\r\n event,\r\n this.state.heatmapHeight,\r\n this.props.periods.length - 1,\r\n this.setHeight.bind(this),\r\n this.onStopDraggingHeatmap.bind(this),\r\n this.props.isLive\r\n );\r\n }\r\n\r\n /**\r\n * @description The callback to call when the drag has completed.\r\n * @param {boolean} isExpanded\r\n */\r\n onStopDraggingHeatmap(isExpanded) {\r\n this.setState({\r\n isExpanding: false,\r\n isExpanded: isExpanded,\r\n showTimeBins: isExpanded\r\n });\r\n }\r\n}\r\n\r\nHeatmap.contextType = ResetContext;\r\n\r\nHeatmap.defaultProps = {\r\n id: '',\r\n periods: [],\r\n isLive: true,\r\n distance: 0,\r\n direction: '',\r\n title: '',\r\n links: [],\r\n junctions: [],\r\n zoomLevel: 1,\r\n openTooltip: () => null,\r\n closeTooltip: () => null,\r\n openJunctionTooltip: () => null,\r\n closeJunctionTooltip: () => null,\r\n heatmapDragService: {}\r\n};\r\n\r\nHeatmap.propTypes = {\r\n id: PropTypes.string.isRequired,\r\n periods: PropTypes.array.isRequired,\r\n isLive: PropTypes.bool.isRequired,\r\n distance: PropTypes.number.isRequired,\r\n direction: PropTypes.string.isRequired,\r\n title: PropTypes.string.isRequired,\r\n links: PropTypes.array.isRequired,\r\n junctions: PropTypes.array.isRequired,\r\n zoomLevel: PropTypes.number.isRequired,\r\n openTooltip: PropTypes.func.isRequired,\r\n closeTooltip: PropTypes.func.isRequired,\r\n openJunctionTooltip: PropTypes.func.isRequired,\r\n closeJunctionTooltip: PropTypes.func.isRequired,\r\n heatmapDragService: PropTypes.object.isRequired\r\n};\r\n\r\nexport default Heatmap;\r\n","/**\r\n * @description Route component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Route\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport * as HeatmapHelpers from '../helpers/HeatmapHelpers';\r\n\r\nimport ResetContext from '../context/ResetContext';\r\n\r\nimport Heatmap from './Heatmap';\r\n\r\n/**\r\n * @description The App component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass Route extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n const junctions = this.props.junctions.filter(\r\n junction =>\r\n junction.carriageway.split('_')[0] === this.props.route.name\r\n ),\r\n links = this.props.links.filter(\r\n link => link.carriageway.split('_')[0] === this.props.route.name\r\n ),\r\n selectedRoutes = HeatmapHelpers.setCarriagewaySelections(\r\n this.props.selectedCarriageways.filter(\r\n route => route.name === this.props.route.name\r\n ),\r\n this.props.route\r\n );\r\n\r\n this.state = {\r\n route: selectedRoutes,\r\n junctions: junctions,\r\n links: links,\r\n junctionFrom: 0,\r\n junctionTo: this.props.route.junctions.length - 1,\r\n zoomLevel: 1\r\n };\r\n\r\n this.resetMethods = {};\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidMount() {\r\n this.context(this.props.route.id, this.resetComponent.bind(this));\r\n this.setJunctionAndLinkVisibility();\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidUpdate(prevProps, prevState) {\r\n if (\r\n prevState.junctionFrom !== this.state.junctionFrom ||\r\n prevState.junctionTo !== this.state.junctionTo\r\n ) {\r\n this.setJunctionAndLinkVisibility();\r\n }\r\n\r\n if (\r\n prevProps.selectedCarriageways !== this.props.selectedCarriageways\r\n ) {\r\n const selectedRoutes = HeatmapHelpers.setCarriagewaySelections(\r\n this.props.selectedCarriageways.filter(\r\n route => route.name === this.state.route.name\r\n ),\r\n this.state.route\r\n );\r\n\r\n this.setState({\r\n route: selectedRoutes\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentWillUnmount() {\r\n this.context(this.props.route.id, this.resetComponent.bind(this), true);\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n const pdfUrl = `${process.env.API_ROOT_URL +\r\n process.env.JOURNEY_TIME_API_ENDPOINT}/${\r\n this.state.route.name\r\n }?date=${this.props.routesPeriods.date}&download=true`;\r\n\r\n return (\r\n this.state.route.isSelected >= 0 && (\r\n \r\n
\r\n
\r\n

{this.state.route.name}

\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n \r\n
\r\n {this.props.routesPeriods.date !==\r\n HeatmapHelpers.getDateStringFromDate(\r\n new Date()\r\n ) && (\r\n \r\n \r\n cloud_download\r\n \r\n \r\n )}\r\n {this.props.routesPeriods.date ===\r\n HeatmapHelpers.getDateStringFromDate(\r\n new Date()\r\n ) && (\r\n \r\n \r\n cloud_download\r\n \r\n \r\n )}\r\n\r\n \r\n Reset\r\n \r\n
\r\n
\r\n {this.state.route.carriageways &&\r\n this.state.route.carriageways.map(\r\n carriageway =>\r\n carriageway.isSelected && (\r\n \r\n link.carriageway ===\r\n carriageway.id\r\n )\r\n .sort(\r\n (a, b) =>\r\n a.displayOrder -\r\n b.displayOrder\r\n )}\r\n junctions={this.state.junctions\r\n .filter(\r\n junction =>\r\n junction.carriageway ===\r\n carriageway.id\r\n )\r\n .sort(\r\n (a, b) =>\r\n a.displayOrder -\r\n b.displayOrder\r\n )}\r\n distance={this.state.links\r\n .filter(\r\n link =>\r\n link.carriageway ===\r\n carriageway.id\r\n )\r\n .reduce(\r\n (a, b) =>\r\n b.visible\r\n ? a + b.lengthMetres\r\n : a,\r\n 0\r\n )}\r\n zoomLevel={this.state.zoomLevel}\r\n openTooltip={this.props.openTooltip}\r\n closeTooltip={\r\n this.props.closeTooltip\r\n }\r\n openJunctionTooltip={\r\n this.props.openJunctionTooltip\r\n }\r\n closeJunctionTooltip={\r\n this.props.closeJunctionTooltip\r\n }\r\n heatmapDragService={\r\n this.props.heatmapDragService\r\n }\r\n >\r\n )\r\n )}\r\n
\r\n \r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @description Adds a reset method into the this.resetMethods array.\r\n * @param {string} id - The id.\r\n * @param {Function} method - The method to add.\r\n * @param {boolean} removeMethod - A flag to indicate removing of method.\r\n */\r\n manageResetMethods(id, method, removeMethod) {\r\n this.resetMethods[id] = null;\r\n\r\n if (!removeMethod) {\r\n this.resetMethods[id] = method;\r\n }\r\n }\r\n\r\n /**\r\n * @description Sets the visibility of junctions and links for this route.\r\n */\r\n setJunctionAndLinkVisibility() {\r\n const visibleJunctionsAndLinks = HeatmapHelpers.getVisibleJunctionsAndLinks(\r\n this.state.route,\r\n this.state.junctionFrom,\r\n this.state.junctionTo\r\n );\r\n\r\n this.setState({\r\n ...visibleJunctionsAndLinks\r\n });\r\n }\r\n\r\n /**\r\n * @description Gets all available periods for a particular carriageway, for the selected date.\r\n * @param {string} carriagewayId - The carriageway Id.\r\n * @returns {array} - An array of periods.\r\n */\r\n getVisiblePeriodsForCarriageway(carriagewayId) {\r\n if (!this.props.routesPeriods) {\r\n return [];\r\n }\r\n\r\n const carriageway = this.props.routesPeriods.carriageways\r\n ? this.props.routesPeriods.carriageways.find(\r\n carriageway => carriageway.id === carriagewayId\r\n )\r\n : null;\r\n\r\n return carriageway\r\n ? carriageway.periods.filter(period => period.isVisible)\r\n : [];\r\n }\r\n\r\n /**\r\n * @description Handles the changing of the from junction.\r\n * @param {object} event - The event.\r\n */\r\n handleJunctionFromChange(event) {\r\n this.setState({ junctionFrom: parseInt(event.target.value, 10) });\r\n }\r\n\r\n /**\r\n * @description Handles the changing of the to junction.\r\n * @param {object} event - The event.\r\n */\r\n handleJunctionToChange(event) {\r\n this.setState({ junctionTo: parseInt(event.target.value, 10) });\r\n }\r\n\r\n /**\r\n * @description Handles the changing of the zoom level.\r\n * @param {string} zoomLevel - The new value.\r\n */\r\n handleZoomLevelChange(event) {\r\n this.setState({ zoomLevel: parseFloat(event.target.value, 10) });\r\n }\r\n\r\n /**\r\n * @description Resets the component to it's default state.\r\n */\r\n resetComponent() {\r\n const state = {\r\n zoomLevel: 1,\r\n junctionFrom: 0,\r\n junctionTo: this.state.route.junctions.length - 1\r\n };\r\n\r\n this.setState(state);\r\n Object.keys(this.resetMethods).forEach(methodName => {\r\n const method = this.resetMethods[methodName];\r\n\r\n method && method();\r\n });\r\n }\r\n}\r\n\r\nRoute.contextType = ResetContext;\r\n\r\nRoute.defaultProps = {\r\n route: {},\r\n showLiveData: true,\r\n junctions: [],\r\n links: [],\r\n selectedCarriageways: [],\r\n openTooltip: () => null,\r\n closeTooltip: () => null,\r\n closeJunctionTooltip: () => null,\r\n heatmapDragService: {}\r\n};\r\n\r\nRoute.propTypes = {\r\n route: PropTypes.object.isRequired,\r\n showLiveData: PropTypes.bool.isRequired,\r\n junctions: PropTypes.array.isRequired,\r\n links: PropTypes.array.isRequired,\r\n routesPeriods: PropTypes.object.isRequired,\r\n selectedCarriageways: PropTypes.array.isRequired,\r\n openTooltip: PropTypes.func.isRequired,\r\n closeTooltip: PropTypes.func.isRequired,\r\n openJunctionTooltip: PropTypes.func.isRequired,\r\n closeJunctionTooltip: PropTypes.func.isRequired,\r\n heatmapDragService: PropTypes.object.isRequired\r\n};\r\n\r\nexport default Route;\r\n","/**\r\n * @description HeatmapDragService module.\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module services/HeatmapDragService\r\n */\r\n\r\nconst rowHeight = 32;\r\n\r\n/**\r\n * @description Service to handle heatmap dragging, expanding and scrolling.\r\n * @class\r\n */\r\nclass HeatmapDragService {\r\n\r\n /**\r\n * Creates an instance of HeatmapDragService.\r\n * @param {Object} contentScrollbar - The main content scrollbar.\r\n */\r\n constructor(contentScrollbar) {\r\n this.contentScrollbar = contentScrollbar;\r\n this.dragTimerActive = false;\r\n this.scrollSpacerHeight = 0;\r\n\r\n this.domElements = {\r\n content: document.querySelector('.content'),\r\n scrollSpacer: document.querySelector('.scroll-space'),\r\n footer: document.querySelector('.Footer'),\r\n currentDragHandle: null\r\n };\r\n\r\n this.setDragEvents();\r\n this.handleScroll = this.recalculateScrollSpacer.bind(this);\r\n\r\n this.minimumHeatmapHeight = 50 + (rowHeight * 8);\r\n this.minimumHeatmapHeightLive = this.minimumHeatmapHeight;\r\n }\r\n\r\n /**\r\n * @description Updates local references to DOM elements.\r\n * @returns {void}\r\n */\r\n updateScrollbarDomElements() {\r\n if (this.domElements.scrollbar) {\r\n return;\r\n }\r\n\r\n this.domElements = {\r\n ...this.domElements,\r\n scrollbar: this.contentScrollbar.current.getScrollElement(),\r\n scrollbarContentWrapper: this.contentScrollbar.current.contentWrapperEl,\r\n scrollbarContent: this.contentScrollbar.current.contentEl\r\n };\r\n }\r\n\r\n /**\r\n * @description Sets the drag events handler objects. \r\n * These contain handlers for specific events.\r\n */\r\n setDragEvents() {\r\n this.dragEvents = [\r\n {\r\n element: this.domElements.content,\r\n events: [\r\n {\r\n eventName: 'mousemove',\r\n handler: this.handleHeatmapDrag.bind(this)\r\n }\r\n ]\r\n },\r\n {\r\n element: this.domElements.footer,\r\n events: [\r\n {\r\n eventName: 'mouseover',\r\n handler: this.handleDragOverFooter.bind(this)\r\n },\r\n {\r\n eventName: 'mouseout',\r\n handler: this.handleDragOutFooter.bind(this)\r\n }\r\n ]\r\n },\r\n {\r\n element: document,\r\n events: [\r\n {\r\n eventName: 'mouseup',\r\n handler: this.handleEndDragging.bind(this)\r\n }\r\n ]\r\n }\r\n ];\r\n }\r\n\r\n /**\r\n * @description Adds the event listeners as specified in the this.dragEvents object.\r\n */\r\n addDragEventListeners() {\r\n this.dragEvents.forEach(element =>\r\n element.events.forEach(eventListener =>\r\n element.element.addEventListener(eventListener.eventName, eventListener.handler)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @description Removes the event listeners in the this.dragEvents object.\r\n */\r\n removeDragEventListeners() {\r\n this.dragEvents.forEach(element =>\r\n element.events.forEach(eventListener =>\r\n element.element.removeEventListener(eventListener.eventName, eventListener.handler)\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @description Sets the initial variables required when dragging starts.\r\n * @param {Object} event - The drag event.\r\n * @param {number} initialHeight - The initial height of the heatmap.\r\n * @param {number} numberOfRows - The number of rows displayed in the heatmap.\r\n * @param {Function} updateHeightCallback - The callback in which to return the calculated height.\r\n * @param {Function} endDragCallback - The callback to call at the end of the drag.\r\n */\r\n handleStartDragging(event, initialHeight, numberOfRows, updateHeightCallback, endDragCallback, isLive) {\r\n this.updateScrollbarDomElements();\r\n this.minimumHeatmapHeight = isLive ? this.minimumHeatmapHeightLive : this.minimumHeatmapHeight;\r\n this.isExpanding = true;\r\n this.initialY = event.clientY;\r\n this.initialSpacerHeight = this.scrollSpacerHeight;\r\n this.domElements.currentDragHandle = event.currentTarget;\r\n this.initialHeight = initialHeight;\r\n this.currentHeatmapHeight = initialHeight;\r\n this.numberOfRows = numberOfRows;\r\n this.updateHeightCallback = updateHeightCallback;\r\n this.endDragCallback = endDragCallback;\r\n this.addDragEventListeners();\r\n }\r\n\r\n /**\r\n * @description Handles the end of the drag.\r\n */\r\n handleEndDragging() {\r\n this.removeDragEventListeners();\r\n clearInterval(this.overFooterInterval);\r\n\r\n if (this.scrollSpacerHeight !== 'auto') {\r\n this.scrollbarScrollTop = this.domElements.scrollbar.scrollTop;\r\n this.onScroll = this.recalculateScrollSpacer.bind(this);\r\n this.domElements.scrollbar.addEventListener('scroll', this.handleScroll);\r\n }\r\n\r\n this.endDragCallback && this.endDragCallback(this.currentHeatmapHeight !== this.minimumHeatmapHeight);\r\n this.isExpanding = false;\r\n }\r\n\r\n /**\r\n * @description Handles the drag move events.\r\n * @param {Object} event - The move event.\r\n * @returns {void}\r\n */\r\n handleHeatmapDrag(event) {\r\n const isAboveHandle = this.domElements.currentDragHandle.getBoundingClientRect().top >= event.clientY;\r\n\r\n if (\r\n !this.isExpanding ||\r\n this.dragTimerActive ||\r\n (this.currentHeatmapHeight === this.minimumHeatmapHeight && isAboveHandle)\r\n ) {\r\n return;\r\n }\r\n\r\n const scrolledToBottom =\r\n this.domElements.scrollbar.clientHeight + this.domElements.scrollbar.scrollTop >=\r\n this.domElements.scrollbarContent.offsetHeight;\r\n\r\n if (isAboveHandle && scrolledToBottom) {\r\n this.updateScrollSpacer(this.initialSpacerHeight + (this.initialY - event.clientY));\r\n } else if (this.scrollSpacerHeight) {\r\n this.updateScrollSpacer(this.initialSpacerHeight - (event.clientY - this.initialY));\r\n }\r\n\r\n // Timer used for debouncing - increases performance of drag.\r\n this.dragTimerActive = true;\r\n setTimeout(() => (this.dragTimerActive = false), 10);\r\n\r\n const newHeight = event.clientY - this.initialY + this.initialHeight,\r\n maxHeightByRows = this.numberOfRows * 32 + this.minimumHeatmapHeight,\r\n maxHeightByWindow = this.domElements.content.clientHeight - 64,\r\n maxHeight = Math.min(maxHeightByRows, maxHeightByWindow),\r\n calculatedHeight = Math.min(Math.max(newHeight, this.minimumHeatmapHeight), maxHeight);\r\n\r\n this.currentHeatmapHeight = calculatedHeight;\r\n this.updateHeightCallback && this.updateHeightCallback(this.currentHeatmapHeight);\r\n }\r\n\r\n /**\r\n * @description Handles the case where a drag handle is pulled over the footer.\r\n */\r\n handleDragOverFooter() {\r\n const stepHeight = 20,\r\n maxHeightByRows = this.numberOfRows * 32 + this.minimumHeatmapHeight,\r\n maxHeightByWindow = this.domElements.content.clientHeight - 64,\r\n maxHeight = Math.min(maxHeightByRows, maxHeightByWindow);\r\n\r\n // Interval used for continuation fo scroll when over footer.\r\n this.overFooterInterval = setInterval(() => {\r\n const currentScroll = this.domElements.scrollbarContentWrapper.scrollTop,\r\n newHeight = Math.min(Math.max(this.currentHeatmapHeight + stepHeight, this.minimumHeatmapHeight), maxHeight);\r\n\r\n if (newHeight === this.currentHeatmapHeight) {\r\n return;\r\n }\r\n \r\n this.currentHeatmapHeight = newHeight;\r\n this.startHeight += stepHeight;\r\n this.updateHeightCallback(newHeight, () => {\r\n this.domElements.scrollbarContentWrapper.scrollTop = currentScroll + stepHeight;\r\n });\r\n }, 100);\r\n }\r\n\r\n /**\r\n * @description Handles dragging out of the footer.\r\n * @param {Object} event - The mouseout event.\r\n */\r\n handleDragOutFooter(event) {\r\n this.initialY = event.clientY;\r\n this.initialHeight = this.currentHeatmapHeight\r\n clearInterval(this.overFooterInterval);\r\n }\r\n\r\n /**\r\n * @description Updates the scroll spacer height.\r\n * @param {number} [newHeight=0] - The new height of the spacer.\r\n */\r\n updateScrollSpacer(newHeight = 0) {\r\n if (newHeight < 0) {\r\n newHeight = 0;\r\n }\r\n\r\n this.scrollSpacerHeight = newHeight;\r\n this.domElements.scrollSpacer.style.height = newHeight > 0 ? `${newHeight}px` : 'auto';\r\n }\r\n\r\n /**\r\n * @description Recalculates the scroll spacer height.\r\n * @returns {void}\r\n */\r\n recalculateScrollSpacer() {\r\n const heightAdjustment = this.scrollbarScrollTop - this.domElements.scrollbar.scrollTop,\r\n newHeight = this.scrollSpacerHeight - heightAdjustment;\r\n\r\n this.scrollbarScrollTop = this.domElements.scrollbar.scrollTop;\r\n\r\n if (newHeight <= 0) {\r\n // scrollSpacer now effectively 0\r\n this.domElements.scrollbar.removeEventListener('scroll', this.handleScroll);\r\n this.updateScrollSpacer();\r\n\r\n return;\r\n } else if (heightAdjustment <= 0) {\r\n // Scrolling down - don't change scrollSpacer height.\r\n return;\r\n }\r\n\r\n this.updateScrollSpacer(newHeight);\r\n }\r\n}\r\n\r\nexport default HeatmapDragService;\r\n","/**\r\n * @description Tooltip component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/Tooltip\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\n/**\r\n * @description The Tooltip component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass Tooltip extends React.Component {\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n this.ref = React.createRef();\r\n this.state = {\r\n top: 0,\r\n left: 0\r\n };\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n shouldComponentUpdate(nextProps, nextState) {\r\n const hasNewEventTarget =\r\n (nextProps.event && !this.props.event) ||\r\n (nextProps.event &&\r\n nextProps.event.target !== this.props.event.target);\r\n \r\n return (\r\n hasNewEventTarget ||\r\n this.props.show !== nextProps.show ||\r\n this.state.left !== nextState.left ||\r\n this.state.top !== nextState.top\r\n );\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidUpdate(prevProps) {\r\n const hasNewEventTarget =\r\n (!prevProps.event && this.props.event) ||\r\n (this.props.event &&\r\n prevProps.event.target !== this.props.event.target);\r\n\r\n if (hasNewEventTarget && this.props.show) {\r\n this.setState(this.getTooltipPosition());\r\n }\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n return (\r\n this.props.show && (\r\n
\r\n {this.getTooltipContent()}\r\n
\r\n )\r\n );\r\n }\r\n\r\n /**\r\n * @description Returns the default content for the tooltip - an empty string.\r\n * @returns {string} - an empty string.\r\n */\r\n getTooltipContent() {\r\n return '';\r\n }\r\n\r\n /**\r\n * @description Gets the position that the tooltip should be located in.\r\n * @returns {object} The position.\r\n */\r\n getTooltipPosition() {\r\n const elementBoundingBox = this.props.event.target.getBoundingClientRect(),\r\n appContentBoundingBox = document\r\n .querySelector('.App .content')\r\n .getBoundingClientRect(),\r\n tooltipSize = {\r\n width: this.ref.current.clientWidth + 10,\r\n height: this.ref.current.clientHeight + 20\r\n },\r\n adjustedElBoundingBox = {\r\n top: elementBoundingBox.top - appContentBoundingBox.top,\r\n bottom: elementBoundingBox.bottom - appContentBoundingBox.top,\r\n left: elementBoundingBox.left - appContentBoundingBox.left\r\n };\r\n\r\n let top = Math.min(\r\n appContentBoundingBox.height - tooltipSize.height,\r\n adjustedElBoundingBox.bottom\r\n ),\r\n left = Math.min(\r\n appContentBoundingBox.width - tooltipSize.width,\r\n adjustedElBoundingBox.left\r\n ),\r\n bottom = top + tooltipSize.height;\r\n\r\n // Calculate if tooltip will cover bounding box.\r\n if (\r\n (bottom > adjustedElBoundingBox.top &&\r\n bottom < adjustedElBoundingBox.bottom) ||\r\n (top > adjustedElBoundingBox.top &&\r\n top < adjustedElBoundingBox.bottom) ||\r\n (top < adjustedElBoundingBox.top &&\r\n bottom > adjustedElBoundingBox.bottom)\r\n ) {\r\n top = adjustedElBoundingBox.top - tooltipSize.height;\r\n }\r\n\r\n return {\r\n top: top + 10,\r\n left: left\r\n };\r\n }\r\n}\r\n\r\nTooltip.defaultProps = {\r\n show: false,\r\n event: {}\r\n};\r\n\r\nTooltip.propTypes = {\r\n show: PropTypes.bool.isRequired,\r\n event: PropTypes.object.isRequired\r\n};\r\n\r\nexport default Tooltip;\r\n","/**\r\n * @description JunctionTooltip component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/JunctionTooltip\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport Tooltip from './Tooltip';\r\n\r\n/**\r\n * @description The JunctionTooltip component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass JunctionTooltip extends Tooltip {\r\n \r\n /**\r\n * @description Returns the content to be inserted into the Tooltip.\r\n * @returns {Object} A JSX object representing the DOM structure.\r\n */\r\n getTooltipContent() {\r\n return (\r\n
\r\n
\r\n {this.props.junction.isJunction && (\r\n
\r\n
{this.props.junction.label}
\r\n
\r\n )}\r\n
{this.props.junction.name}
\r\n
\r\n
\r\n );\r\n }\r\n}\r\n\r\nJunctionTooltip.defaultProps = {\r\n junction: {}\r\n};\r\n\r\nJunctionTooltip.propTypes = {\r\n junction: PropTypes.object.isRequired\r\n};\r\n\r\nexport default JunctionTooltip;\r\n","/**\r\n * @description LinkTooltip component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module components/LinkTooltip\r\n */\r\n\r\nimport React from 'react';\r\nimport PropTypes from 'prop-types';\r\n\r\nimport Tooltip from './Tooltip';\r\n\r\n/**\r\n * @description The LinkTooltip component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass LinkTooltip extends Tooltip {\r\n\r\n /**\r\n * @description Returns the content to be inserted into the Tooltip.\r\n * @returns {Object} A JSX object representing the DOM structure.\r\n */\r\n getTooltipContent() {\r\n return (\r\n
\r\n
\r\n Location:\r\n {this.props.link.name}\r\n
\r\n
\r\n Timeframe:\r\n {this.props.link.timeframe}\r\n
\r\n
\r\n Actual Journey Time:\r\n {this.props.link.journeyTimeSeconds}s\r\n
\r\n
\r\n Expected Journey Time:\r\n {this.props.link.expectedTimeSeconds}s\r\n
\r\n
\r\n );\r\n }\r\n}\r\n\r\nLinkTooltip.defaultProps = {\r\n link: {}\r\n};\r\n\r\nLinkTooltip.propTypes = {\r\n link: PropTypes.object.isRequired\r\n};\r\n\r\nexport default LinkTooltip;\r\n","/**\r\n * @description App component\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module App\r\n */\r\n\r\nimport React from \"react\";\r\nimport PropTypes from \"prop-types\";\r\n\r\nimport SimpleBar from \"simplebar-react\";\r\n\r\nimport \"./styles/App.css\";\r\nimport \"simplebar/dist/simplebar.min.css\";\r\n\r\nimport HeatmapService from \"./services/HeatmapService\";\r\nimport * as HeatmapHelpers from \"./helpers/HeatmapHelpers\";\r\nimport NotificationService from \"./services/NotificationService\";\r\n\r\nimport ResetContext from \"./context/ResetContext\";\r\n\r\nimport Notifications from \"./components/Notifications\";\r\nimport Nav from \"./components/Nav\";\r\nimport Menu from \"./components/Menu\";\r\nimport Footer from \"./components/Footer\";\r\nimport Route from \"./components/Route\";\r\nimport HeatmapDragService from './services/HeatmapDragService';\r\nimport JunctionTooltip from './components/JunctionTooltip';\r\nimport LinkTooltip from './components/LinkTooltip';\r\nimport CONSTANTS from \"./config/Constants\";\r\n\r\n/**\r\n * @description The App component.\r\n * @class\r\n * @extends {React.Component}\r\n */\r\nclass App extends React.Component {\r\n \r\n /**\r\n * @inheritdoc\r\n */\r\n constructor(props) {\r\n super(props);\r\n this.state = {\r\n notifications: [],\r\n selectedCarriageways: [],\r\n routes: [],\r\n delays: [],\r\n junctions: [],\r\n links: [],\r\n routesPeriods: [],\r\n showMenu: false,\r\n selectedDate: HeatmapHelpers.getDateStringFromDate(new Date()),\r\n showLiveData: true,\r\n zoomLevel: 1,\r\n selectedDelays: [true, true, true, true, true, true, true],\r\n showLinkTooltip: false,\r\n linkTooltipData: {},\r\n showJunctionTooltip: false,\r\n junctionTooltipData: {}\r\n };\r\n this.notificationService = new NotificationService(\r\n this.handleNotificationsUpdate.bind(this)\r\n );\r\n this.resetMethods = {};\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidMount() {\r\n if (!this.props.browserSupported) {\r\n const unsupportedBrowserMessage = [\r\n
\r\n \r\n The browser version that you are using is currently\r\n unsupported and may result in a degraded experience.\r\n
\r\n This app is currently supported in the latest versions of Google Chrome, Mozilla Firefox and\r\n Microsoft Edge.\r\n
\r\n
\r\n ];\r\n\r\n this.addNotification({\r\n title: \"Unsupported Browser\",\r\n message: unsupportedBrowserMessage,\r\n action: () => null, // onclose handler is called too\r\n onClose: this.loadHeatmapService.bind(this)\r\n });\r\n } else {\r\n this.loadHeatmapService();\r\n this.heatmapDragService = new HeatmapDragService(this.scrollbarRef);\r\n }\r\n\r\n this.setEnvironmentIdentifier(process.env.ENVIRONMENT_NAME);\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n componentDidUpdate(prevProps, prevState) {\r\n if (prevState.selectedDelays !== this.state.selectedDelays) {\r\n HeatmapHelpers.setHeatmapColours(\r\n this.state.selectedDelays,\r\n this.state.delays\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * @inheritdoc\r\n */\r\n render() {\r\n this.scrollbarRef = React.createRef();\r\n\r\n return (\r\n \r\n
\r\n \r\n \r\n \r\n
\r\n {/* SimpleBar used here for app level scrolling/consistent scrollbar styling */}\r\n \r\n {this.state.routes.length > 0 &&\r\n this.state.routes.map(route => (\r\n \r\n ))}\r\n
\r\n \r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n }\r\n\r\n /**\r\n * @description Adds a reset method into the this.resetMethods array.\r\n * @param {string} id - The id.\r\n * @param {Function} method - The method to add.\r\n * @param {boolean} removeMethod - A flag to indicate removing of method.\r\n */\r\n manageResetMethods(id, method, removeMethod) {\r\n this.resetMethods[id] = null;\r\n \r\n if (!removeMethod) {\r\n this.resetMethods[id] = method;\r\n } \r\n }\r\n\r\n /**\r\n * @description Adds the environment name to the document title in all enviornments except production.\r\n * @param {string} environmentName - The environment name.\r\n */\r\n setEnvironmentIdentifier(environmentName) {\r\n if (environmentName && environmentName.toLowerCase() === \"production\") {\r\n return;\r\n }\r\n\r\n document.title = `${document.title} (${environmentName})`;\r\n }\r\n\r\n /**\r\n * @description Gets all available periods for the selected date.\r\n * @returns {Object} - An object containing details of carriageways and their periods.\r\n */\r\n getRoutesPeriodsForSelectedDate() {\r\n const selectedDate = this.state.selectedDate\r\n ? this.state.selectedDate\r\n : HeatmapHelpers.getDateStringFromDate(new Date());\r\n\r\n return (\r\n this.state.routesPeriods.find(\r\n currentDate => currentDate.date === selectedDate\r\n ) || {}\r\n );\r\n }\r\n\r\n /**\r\n * @description Loads the heatmap service.\r\n */\r\n loadHeatmapService() {\r\n this.heatmapService = new HeatmapService(\r\n {\r\n onDataServiceMessage: this.updateLiveData.bind(this)\r\n },\r\n this.notificationService\r\n );\r\n\r\n Promise.all([\r\n this.heatmapService.getRoutes(),\r\n this.heatmapService.getDelayData()\r\n ])\r\n .then(data => {\r\n const routeData = HeatmapHelpers.getJunctionsAndLinksFromRoutes(\r\n data[0]\r\n ),\r\n selectedRouteData = HeatmapHelpers.setSelectedRoutes(\r\n data[0]\r\n );\r\n\r\n this.setState({\r\n ...routeData,\r\n routes: data[0],\r\n selectedCarriageways: HeatmapHelpers.getCarriagewaySelections(\r\n selectedRouteData\r\n ),\r\n delays: data[1]\r\n });\r\n HeatmapHelpers.setHeatmapColours(\r\n this.state.selectedDelays,\r\n data[1]\r\n );\r\n\r\n this.setPeriodVisibleInterval();\r\n\r\n let carriageways = [];\r\n\r\n this.state.routes.forEach(route => {\r\n carriageways = [\r\n ...carriageways,\r\n ...route.carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n links: carriageway.links.map(link => link.id)\r\n };\r\n })\r\n ];\r\n });\r\n\r\n HeatmapHelpers.addNewDate(\r\n this.state.selectedDate,\r\n this.state.routesPeriods,\r\n carriageways\r\n ).then(routesPeriods => {\r\n this.setState(\r\n {\r\n routesPeriods: routesPeriods\r\n },\r\n () => this.heatmapService.createDataServiceConnection()\r\n );\r\n }).catch(error => {\r\n this.addNotification(error);\r\n });\r\n })\r\n .catch(error => {\r\n this.addNotification(error);\r\n });\r\n }\r\n\r\n /**\r\n * @description Starts the interval from which to update period visibility.\r\n */\r\n setPeriodVisibleInterval() {\r\n const currentTime = new Date(),\r\n nextPeriod = new Date();\r\n\r\n nextPeriod.setMinutes(\r\n Math.floor(\r\n currentTime.getMinutes() / CONSTANTS.UPDATE_INTERVAL_MINS\r\n ) *\r\n CONSTANTS.UPDATE_INTERVAL_MINS +\r\n CONSTANTS.UPDATE_INTERVAL_MINS,\r\n 0,\r\n 0\r\n );\r\n\r\n // Start the interval at the start of the next time bin.\r\n setTimeout(() => {\r\n this.setRecentPeriodVisible();\r\n // Start the interval\r\n setInterval(() => {\r\n this.setRecentPeriodVisible();\r\n }, CONSTANTS.UPDATE_INTERVAL_MINS * 60000);\r\n }, nextPeriod - currentTime);\r\n }\r\n\r\n /**\r\n * @description Sets the latest timebin for which we have received no data in the alloted time as visible.\r\n */\r\n setRecentPeriodVisible() {\r\n const currentTime = new Date(),\r\n periodStart = new Date().setMinutes(\r\n Math.floor(\r\n currentTime.getMinutes() / CONSTANTS.UPDATE_INTERVAL_MINS\r\n ) *\r\n CONSTANTS.UPDATE_INTERVAL_MINS -\r\n 2 * CONSTANTS.UPDATE_INTERVAL_MINS,\r\n 0,\r\n 0\r\n );\r\n\r\n if (this.checkDateChange()) {\r\n let carriageways = [];\r\n\r\n this.state.routes.forEach(route => {\r\n carriageways = [\r\n ...carriageways,\r\n ...route.carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n links: carriageway.links.map(link => link.id)\r\n };\r\n })\r\n ];\r\n });\r\n\r\n HeatmapHelpers.addNewDatePeriods(\r\n {\r\n date: currentTime,\r\n carriageways: carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n periods: []\r\n };\r\n })\r\n },\r\n this.state.routesPeriods,\r\n carriageways\r\n ).then(data => {\r\n const state = {\r\n routesPeriods: HeatmapHelpers.setPeriodVisibility(\r\n periodStart,\r\n data\r\n ),\r\n selectedDate: HeatmapHelpers.getDateStringFromDate(currentTime)\r\n };\r\n\r\n this.setState(state);\r\n });\r\n } else {\r\n const routesPeriods = HeatmapHelpers.setPeriodVisibility(\r\n periodStart,\r\n this.state.routesPeriods\r\n );\r\n\r\n this.setState({\r\n routesPeriods: routesPeriods\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * @description Processes the 'live' data as received from the API.\r\n * @param {Object} data - The received data.\r\n */\r\n updateLiveData(data) {\r\n const period = {\r\n ...data\r\n };\r\n let carriageways = [];\r\n\r\n this.state.routes.forEach(route => {\r\n carriageways = [\r\n ...carriageways,\r\n ...route.carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n links: carriageway.links.map(link => link.id)\r\n };\r\n })\r\n ];\r\n });\r\n\r\n HeatmapHelpers.addNewDatePeriods(\r\n period,\r\n this.state.routesPeriods,\r\n carriageways\r\n ).then(data => {\r\n const state = {\r\n routesPeriods: data\r\n };\r\n\r\n if (this.checkDateChange()) {\r\n state.selectedDate = new Date().toISOString().split(\"T\")[0];\r\n }\r\n\r\n this.setState(state);\r\n });\r\n\r\n this.handleLinkTooltipClose();\r\n }\r\n\r\n /**\r\n * @description Updates the HeatmapService with new route details.\r\n * @param {array} [selectedCarriageways=this.state.selectedCarriageways] - The selected carriageways, or all.\r\n * @param {string} [selectedDate=this.state.selectedDate] - The selected date.\r\n */\r\n handleSelectedRouteDataChange(\r\n selectedCarriageways = this.state.selectedCarriageways,\r\n selectedDate = this.state.selectedDate\r\n ) {\r\n const showLiveData =\r\n selectedDate === new Date().toISOString().split(\"T\")[0];\r\n\r\n this.setState({\r\n selectedCarriageways: selectedCarriageways,\r\n selectedDate: selectedDate,\r\n showLiveData: showLiveData\r\n });\r\n !showLiveData &&\r\n selectedCarriageways\r\n .filter(route => route.isSelected >= 0)\r\n .forEach(route => {\r\n this.heatmapService\r\n .getHeatmapData(route.name, selectedDate)\r\n .then(historicData =>\r\n this.processHeatmapData(historicData)\r\n )\r\n .catch(error => {\r\n this.addNotification(error);\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * @description Processes the historic heatmap data and sets the state.\r\n * @param {Array} historicData - An array of historic data.\r\n */\r\n processHeatmapData(historicData) {\r\n let carriageways = [];\r\n\r\n this.state.routes.forEach(route => {\r\n carriageways = [\r\n ...carriageways,\r\n ...route.carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n links: carriageway.links.map(link => link.id)\r\n };\r\n })\r\n ];\r\n carriageways = [\r\n ...carriageways,\r\n ...route.carriageways.map(carriageway => {\r\n return {\r\n id: carriageway.id,\r\n links: carriageway.links.map(link => link.id)\r\n };\r\n })\r\n ];\r\n });\r\n\r\n HeatmapHelpers.addNewDatePeriods(\r\n historicData,\r\n this.state.routesPeriods,\r\n carriageways\r\n ).then(dateRoutesPeriods => {\r\n this.setState({\r\n routesPeriods: dateRoutesPeriods\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * @description Handles the opening of the junction tooltip.\r\n * @param {Object} event - The hover event, passed through.\r\n * @param {Object} junction - The junction for which to display information.\r\n */\r\n handleJunctionTooltipOpen(event, junction) {\r\n this.closeJunctionTooltip && this.closeJunctionTooltip();\r\n\r\n this.setState({\r\n showJunctionTooltip: true,\r\n junctionTooltipData: {\r\n event: event,\r\n junction: junction\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * @description Handles the opening of the junction tooltip.\r\n */\r\n handleJunctionTooltipClose() {\r\n this.closeJunctionTooltip && this.closeJunctionTooltip();\r\n\r\n this.setState({\r\n showJunctionTooltip: false,\r\n junctionTooltipData: {\r\n event: null,\r\n junction: {}\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * @param {Object} event - The click event, passed through.\r\n * @param {Object} link - The link for which to display information.\r\n * @param {Function} setButtonState - A callback for setting the active state of the clicked button.\r\n */\r\n handleLinkTooltipOpen(event, link, setButtonState) {\r\n this.closeLinkTooltip && this.closeLinkTooltip();\r\n\r\n if (!this.state.selectedDelays[link.delayBand.id]) {\r\n setButtonState(false);\r\n\r\n return;\r\n }\r\n\r\n this.setState({\r\n showLinkTooltip: true,\r\n linkTooltipData: {\r\n event: event,\r\n link: link\r\n }\r\n });\r\n this.setToolTipButtonState = setButtonState;\r\n this.closeLinkTooltip = this.handleLinkTooltipClose.bind(this);\r\n document.addEventListener('click', this.closeLinkTooltip, {\r\n once: true\r\n });\r\n this.scrollbarRef.current &&\r\n this.scrollbarRef.current\r\n .getScrollElement()\r\n .addEventListener('scroll', this.closeLinkTooltip);\r\n }\r\n\r\n /**\r\n * @description Closes the tooltip and sets the button active state to false.\r\n */\r\n handleLinkTooltipClose() {\r\n document.removeEventListener('click', this.closeLinkTooltip);\r\n this.scrollbarRef.current && this.scrollbarRef.current.getScrollElement().removeEventListener('scroll', this.closeLinkTooltip);\r\n this.setToolTipButtonState && this.setToolTipButtonState(false);\r\n this.setState({\r\n showLinkTooltip: false\r\n });\r\n this.setToolTipButtonState = null;\r\n }\r\n\r\n /**\r\n * @description Handles the changing of the selected date.\r\n * @param {string} date - The new value.\r\n */\r\n handleSelectedDateChange(date) {\r\n this.setState({ selectedDate: date });\r\n }\r\n\r\n /**\r\n * @description Handles the changing of the selected delays.\r\n * @param {array} selectedDelays - The list of selected delays.\r\n */\r\n handleSelectedDelaysChange(selectedDelays) {\r\n this.setState({ selectedDelays: selectedDelays });\r\n }\r\n\r\n /**\r\n * @description Resets the filters back to default.\r\n */\r\n handleResetFilters() {\r\n this.setState(\r\n {\r\n selectedDate: new Date().toISOString().split(\"T\")[0],\r\n zoomLevel: 1,\r\n selectedDelays: [true, true, true, true, true, true, true],\r\n selectedCarriageways: HeatmapHelpers.getCarriagewaySelections(\r\n this.state.routes\r\n )\r\n },\r\n this.handleSelectedRouteDataChange\r\n );\r\n Object.keys(this.resetMethods).forEach(methodName => {\r\n const method = this.resetMethods[methodName];\r\n\r\n method && method()\r\n });\r\n }\r\n\r\n /**\r\n * @description Checks whether the date has changed.\r\n * @returns {Boolean} Whether date has changed.\r\n */\r\n checkDateChange() {\r\n return (\r\n this.state.showLiveData &&\r\n HeatmapHelpers.getDateStringFromDate(new Date()) !==\r\n HeatmapHelpers.getDateStringFromDate(\r\n new Date(this.state.selectedDate)\r\n )\r\n );\r\n }\r\n\r\n // Notification related events\r\n\r\n /**\r\n * @description Adds a notification to the NotificationService.\r\n * @param {Object} notification - The notification details.\r\n * @returns {Object} The notification.\r\n */\r\n addNotification(notification) {\r\n return this.notificationService.addNotification(notification);\r\n }\r\n\r\n /**\r\n * @description Closes a notification via the NotificationService.\r\n * @param {number} id - The notification id.\r\n */\r\n closeNotification(id) {\r\n this.notificationService.removeNotification(id);\r\n }\r\n\r\n /**\r\n * @description Handles a click event of a notification action.\r\n * @param {number} id - The notification id.\r\n */\r\n handleNotificationActionClick(id) {\r\n this.notificationService.triggerNotificationAction(id);\r\n }\r\n\r\n /**\r\n * @description Handles a change to the notifications list.\r\n * @param {array} notifications - The list of notifications from the NotificationService.\r\n */\r\n handleNotificationsUpdate(notifications) {\r\n this.setState({\r\n notifications: notifications\r\n });\r\n }\r\n}\r\n\r\nApp.defaultProps = {\r\n browserSupported: false,\r\n supportedBrowserVersions: {}\r\n};\r\n\r\nApp.propTypes = {\r\n browserSupported: PropTypes.bool.isRequired,\r\n supportedBrowserVersions: PropTypes.object.isRequired\r\n};\r\n\r\nexport default App;\r\n","/**\r\n * @description Index\r\n * @file\r\n * @copyright © Copyright IBI Group., created 2019. All use, disclosure, and/or reproduction of this material is prohibited unless authorized in writing. All rights reserved.\r\n * @module index\r\n */\r\n\r\nimport React from 'react';\r\nimport ReactDOM from 'react-dom';\r\n\r\nimport App from './App';\r\n\r\nconst minimumBrowserSupport = {\r\n Chrome: process.env.SUPPORTED_VERSION_CHROME,\r\n Chromium: process.env.SUPPORTED_VERSION_CHROMIUM,\r\n Edge: process.env.SUPPORTED_VERSION_EDGE,\r\n Firefox: process.env.SUPPORTED_VERSION_FIREFOX\r\n },\r\n browserVersion = getBrowserVersion(),\r\n isSupported =\r\n minimumBrowserSupport[browserVersion.browser] &&\r\n browserVersion.version >= minimumBrowserSupport[browserVersion.browser];\r\n\r\nReactDOM.render(\r\n ,\r\n document.getElementById('root')\r\n);\r\n\r\n/**\r\n * @description Gets the browser name and version.\r\n * @returns An object with the browser name and version.\r\n */\r\nfunction getBrowserVersion() {\r\n const ua = window.navigator.userAgent,\r\n browserVersion = {\r\n browser: null,\r\n version: null\r\n },\r\n chromeVersionIndex = ua.indexOf('Chrome'),\r\n chromiumVersionIndex = ua.indexOf('Chromium'),\r\n edgeVersionIndex = ua.indexOf('Edge/'),\r\n firefoxVersionIndex = ua.indexOf('Firefox');\r\n\r\n if (chromiumVersionIndex !== -1) {\r\n browserVersion.browser = 'Chromium';\r\n browserVersion.version = parseInt(ua.substring(chromiumVersionIndex + 9).split('.')[0], 10);\r\n } else if (chromeVersionIndex !== -1 && edgeVersionIndex === -1) {\r\n browserVersion.browser = 'Chrome';\r\n browserVersion.version = parseInt(ua.substring(chromeVersionIndex + 7).split('.')[0], 10);\r\n } else if (edgeVersionIndex !== -1) {\r\n browserVersion.browser = 'Edge';\r\n browserVersion.version = parseInt(ua.substring(edgeVersionIndex + 5).split('.')[0], 10);\r\n } else if (firefoxVersionIndex !== -1) {\r\n browserVersion.browser = 'Firefox';\r\n browserVersion.version = parseInt(ua.substring(firefoxVersionIndex + 8).split('.')[0], 10);\r\n }\r\n\r\n return browserVersion;\r\n}\r\n"],"sourceRoot":""}