{"version":3,"sources":["webpack:///./ScriptsV2/Core/Util/Mouse/heatmap.js","webpack:///./ScriptsV2/Core/Util/Mouse/api.js","webpack:///./ScriptsV2/Core/Util/Mouse/tracking.js","webpack:///./ScriptsV2/Core/Util/Mouse/initMouse.js"],"names":["_container","_map","_heatmap","_interopRequireDefault","__webpack_require__","_api","_show","_loading","createMap","createHeatMapElement","h337","create","container","getMapData","style","position","showMap","setVisible","hideMap","reloadMap","body","document","html","documentElement","height","Math","max","scrollHeight","offsetHeight","clientHeight","heatmap","createElement","setAttribute","concat","width","scrollWidth","getElementsByTagName","appendChild","getElementById","map","api","getHeatmapData","window","location","href","then","data","setData","min","visible","display","initMap","addEventListener","event","keyCode","ctrlKey","shiftKey","setTimeout","postHistory","pages","Promise","resolve","reject","fetch","method","headers","Content-Type","JSON","stringify","response","ok","url","json","eventType","state","_lastTrackedEvent","mouseX","mouseY","eventDate","Date","seconds","_currentState","_idleTime","_history","startPostInterval","intervalHandler","length","historyToPost","clearHistory","setInterval","_postIntervalInSeconds","startIdleInterval","addEvent","track","trackIntervalTimePassed","axisMoved","xUpperLimit","xLowerLimit","yUpperLimit","yLowerLimit","pageX","toCheck","now","_trackIntervalInSeconds","date","trackedEvent","floor","_minimumDeviation","pageY","getDifferenceInSeconds","snowplowId","addEventToPage","page","find","events","push","getHistory","stringHistory","localStorage","getItem","setItem","parse","saveHistory","date1","date2","difference","getTime","abs","getSnowPlowId","cookies","decodeURIComponent","cookie","split","i","indexOf","splitCookie","lenght","splitValue","initTracker","currentPosition","x","y","tracking"],"mappings":"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","file":"vendor-dnx-mouse-tracking.js","sourcesContent":["//https://www.patrick-wied.at/static/heatmapjs/\nimport h337 from 'heatmap.js';\n//api methods.\nimport api from './api';\n\n//container element \nlet _container;\n//The map element\nlet _map;\n//Boolean to iodicate if map is currently shown.\nlet _show = false;\n//Boolean to indicate wether a map load is already in progress.\nlet _loading = false;\n\n//Id for the wrapper element.\nconst elementId = 'dnx-heatmap';\n//maximum for number of seconds idle that is shown on map.\nconst maxShownValue = 10;\n\n//Adds event listener to document keyup.\n//Ctrl + Shift + H/h will toggle the heat map.\n//Ctrl + Shift + L/l will reload the data from api.\nconst initMap = () => {\n document.addEventListener('keyup', (event) => {\n if(event.keyCode === 72 && event.ctrlKey && event.shiftKey) {\n _show = !_show;\n\n if (_show) {\n // reload data if the map was previously shown\n if (_container) setTimeout(reloadMap, 0);\n showMap();\n \n } else {\n hideMap();\n }\n } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey) {\n reloadMap();\n }\n });\n};\n\n//Create a new instance of heat map and get map data.\nconst createMap = () => {\n\n //lazy private with our container.\n _container = _container || createHeatMapElement();\n\n //Create map\n _map = h337.create({\n container: _container\n });\n\n //Fill map with data\n getMapData(_map);\n\n // The default initialization in heat map.js will set the wrapper to relative.\n // For our purposes we want it to be absolute (fill the screen)\n _container.style.position = 'absolute';\n};\n\n//Set map display to initial.\nconst showMap = () => {\n setVisible(true);\n};\n\n//Set map display to none.\nconst hideMap = () => {\n setVisible(false);\n};\n\n//Refill the map with data from api.\nconst reloadMap = () => {\n getMapData(_map);\n};\n\n// Creates a wrapper for heat map and sets the style.\n// Height will be the maximum height we can find for the page so we have roomm for all the data.\nconst createHeatMapElement = () => {\n const body = document.body,\n html = document.documentElement;\n\n //Get the absolute max height we can find for the page.\n const height = Math.max( body.scrollHeight, body.offsetHeight, \n html.clientHeight, html.scrollHeight, html.offsetHeight );\n\n //Styling to make sure heat map covers the entire page and is on top of everything.\n const heatmap = document.createElement('div');\n heatmap.setAttribute('id', elementId);\n heatmap.style.height = `${height}px`;\n heatmap.style.width = `${body.scrollWidth}px`;\n\n //Attach heatmap to body and return the newly created element.\n document.getElementsByTagName('body')[0].appendChild(heatmap);\n return document.getElementById(elementId);\n};\n\n//Get map data from api\nconst getMapData = (map) => {\n\n //create map if it doesn't yet exist.\n if(!_container) {\n createMap();\n }\n\n if(!_loading) {\n _loading = true;\n\n api.getHeatmapData(window.location.href).then(\n (data) => {\n\n //User can't see a map without data.\n if(data === []) {\n hideMap();\n }\n \n // Max-min is the amount of seconds the mouse pointer has been idle.\n map.setData({\n max: maxShownValue,\n min: 0,\n data: data\n });\n\n _loading = false; // Indicate we are done loading.\n },\n () => {\n hideMap(); //User can't see a map without data.\n _loading = false; //done loading.\n }\n );\n }\n \n};\n\n//Toggle visibility of the map container.\nconst setVisible = (visible) => {\n \n if(!_container) {\n createMap();\n }\n\n if(visible) {\n _container.style.display = 'initial';\n } else {\n _container.style.display = 'none';\n }\n};\n\nexport default {\n initMap,\n showMap,\n hideMap,\n reloadMap\n};","const _endpoint = '/api/MouseTracking/'; //Api endpoint\n\n//Post mouse event history to api endpoint as a collection of pages.\nconst postHistory = (pages) => {\n return new Promise((resolve, reject) => {\n fetch(_endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(pages)\n }).then((response) => {\n if(response.ok) {\n resolve();\n } else {\n reject();\n }\n });\n });\n};\n\nconst getHeatmapData = (url) => {\n return new Promise((resolve, reject) => {\n fetch(`${_endpoint}HeatMap?url=${url}`).then((response) => {\n response.json().then((data) => {\n resolve(data);\n },\n reject);\n },\n reject);\n });\n};\n\nexport default {\n postHistory,\n getHeatmapData\n};","import api from './api';\n\n//The minimum amount of pixels (x or y) that the cursor has to move before it will be tracked as an event.\nconst _minimumDeviation = 30;\n//The amount of time in seconds before state will be set to idle.\nconst _maxIdleTimeInSeconds = 30;\n//The minumum amount of time in seconds between each tracked event.\nconst _trackIntervalInSeconds = 1;\n//The amount of time in seconds between each post of history to api endpoint.\nconst _postIntervalInSeconds = 30;\n//A second in miliseconds.\nconst _secondInMs = 1000;\n\n//Types of mouse events.\nconst eventType = {\n idle: 0,\n move: 1\n};\n\n//Tracking states.\nconst state = {\n idle: 0,\n active: 1\n};\n\n//Initial event.\nlet _lastTrackedEvent = {\n mouseX: 0,\n mouseY: 0,\n eventDate: new Date(),\n eventType: eventType.idle,\n seconds: 0\n};\n\n//current tracking state\nlet _currentState = state.idle;\n //current idle time\nlet _idleTime = 0;\n//history of tracked events\nlet _history = [];\n\nconst initTracker = () => {\n \n //Get any history saved in local storage.\n _history = getHistory();\n\n //Get snowplowId\n _lastTrackedEvent.snowplowId = getSnowPlowId();\n\n //Event listener for mousemove to track mouse movement.\n document.addEventListener('mousemove', (event) => {\n track(event);\n });\n\n //Event listener for window unload. Saves currently unsent history to local storage.\n window.addEventListener('unload', saveHistory);\n\n //Start posting\n startPostInterval(); \n\n //Start timing idle time\n startIdleInterval();\n};\n\n//Post history to endpoint every x seconds.\n//Clear history whether successful or not to avoid flooding local storage.\nconst startPostInterval = () => {\n const intervalHandler = () => {\n if (_history.length !== 0) {\n var historyToPost = _history;\n clearHistory();\n // TODO: re-add historyToPost to _history on failure?\n api.postHistory(historyToPost).then(\n () => { },\n () => { }\n );\n }\n };\n // send saved history asap\n if (_history && _history.length !== 0) { setTimeout(intervalHandler, 500); }\n\n // register send interval\n setInterval(intervalHandler, _postIntervalInSeconds * _secondInMs);\n};\n\n//Increments idleTime every second\n//If idle time reaches max idle time, adds an idle event to history.\nconst startIdleInterval = () => {\n setInterval(() => {\n _idleTime ++; //Increase idle time.\n\n //When we reach max threshhold and we're not already idle, set state to idle. \n if(_currentState != state.idle && _idleTime >= _maxIdleTimeInSeconds) {\n _currentState = state.idle;\n addEvent(); //for now, no included event means idle event.\n }\n }, _secondInMs);\n};\n\n//Event handler for document.onmousemove\n//Resets idle time to 0.\n//Sets state to active.\n//Tracks movement if conditions are met.\nconst track = (event) => {\n _idleTime = 0;\n _currentState = state.active;\n\n //If mouse moved enough and track interval timer has passed, add event.\n if(trackIntervalTimePassed() && axisMoved(event)) {\n addEvent(event);\n }\n};\n\n//Checks if x axis has moved more than the minimum pixels since last event.\nconst axisMoved = (event) => {\n const xUpperLimit = _lastTrackedEvent.mouseX + _minimumDeviation;\n const xLowerLimit = _lastTrackedEvent.mouseX - _minimumDeviation;\n const yUpperLimit = _lastTrackedEvent.mouseY + _minimumDeviation;\n const yLowerLimit = _lastTrackedEvent.mouseY - _minimumDeviation;\n\n return (event.pageX > (xUpperLimit) || event.pageX < (xLowerLimit)) ||\n (event.pageX > (yUpperLimit) || event.pageX < (yLowerLimit));\n};\n\n//Checks if the minumum interval between events has passed.\nconst trackIntervalTimePassed = () => {\n const toCheck = new Date(Date.now() - (_trackIntervalInSeconds * _secondInMs));\n return _lastTrackedEvent.eventDate < toCheck;\n};\n\n//Return the last tracked coordinates\nconst currentPosition = () => {\n return {\n x: _lastTrackedEvent.mouseX,\n y: _lastTrackedEvent.mouseY\n };\n};\n\n//Adds an event to history and updates lastTrackedEvent.\n//For now, no mouse event means idle, mouse event means move.\nconst addEvent = (event) => {\n\n const date = new Date();\n const trackedEvent = {};\n\n if(event != null) {\n //Move event. Update position.\n //Round x and y based on minimum deviation.\n trackedEvent.mouseX = Math.floor(event.pageX / _minimumDeviation) * _minimumDeviation + (_minimumDeviation / 0.5);\n trackedEvent.mouseY = Math.floor(event.pageY / _minimumDeviation) * _minimumDeviation + (_minimumDeviation / 0.5);\n trackedEvent.eventType = eventType.move;\n } else {\n //Idle event. position is same as last tracked.\n trackedEvent.mouseX = _lastTrackedEvent.mouseX;\n trackedEvent.mouseY = _lastTrackedEvent.mouseY;\n trackedEvent.eventType = eventType.idle;\n }\n\n //Properties shared by all events.\n trackedEvent.eventDate = date;\n trackedEvent.seconds = getDifferenceInSeconds(_lastTrackedEvent.eventDate, date);\n trackedEvent.snowplowId = _lastTrackedEvent.snowplowId;\n\n addEventToPage(trackedEvent);\n _lastTrackedEvent = trackedEvent;\n};\n\nconst addEventToPage = (trackedEvent) => {\n \n //Try to find page in existing history\n var page = _history.find((page) => {\n return page.url === window.location.href;\n });\n\n //If no page exists, create one.\n if(page == null) {\n \n //Add event to page events.\n const events = [];\n events.push(trackedEvent);\n \n _history.push({\n url: window.location.href,\n events: events\n });\n } else {\n //Add event to page events.\n page.events.push(trackedEvent);\n }\n};\n\n//Gets any stored history from local storage and clears localStorage.\nconst getHistory = () => {\n const stringHistory = localStorage.getItem('mouseHistory') || '[]';\n localStorage.setItem('mouseHistory', null);\n\n const history = JSON.parse(stringHistory);\n return history || [];\n};\n\n//Clears client side tracked history.\nconst clearHistory = () => {\n _history = [];\n};\n\n//Save history to local storage.\nconst saveHistory = () => {\n localStorage.setItem('mouseHistory', JSON.stringify(_history));\n};\n\n//Calculates the difference in seconds between two dates.\nconst getDifferenceInSeconds = (date1, date2) => {\n const difference = (date1.getTime() - date2.getTime()) / 1000;\n\n //return rounded down to integer and do not differentiate between negative and positive.\n return Math.floor(Math.abs(difference));\n};\n\n//Gets snowplow id from cookies\nconst getSnowPlowId = () => {\n const identifier = '_sp_id';\n\n //Get cookies string\n const decodedCookie = decodeURIComponent(document.cookie);\n \n //Split into separate cookies\n const cookies = decodedCookie.split(';');\n\n //Find right cookie\n for(let i = 0 ; i < cookies.length ; i++) {\n \n if(cookies[i].indexOf(identifier) != -1) {\n \n //Get value\n const splitCookie = cookies[i].split('=');\n \n //If no value, return null\n if(splitCookie.lenght < 2) {\n return null;\n }\n\n //Value is multiple id's split by full stops, we want the first one\n const splitValue = splitCookie[1].split('.');\n\n //If empty return null otherwise return value.\n return splitValue[0] != '' ? splitValue[0] : null;\n }\n }\n\n //if no cookies return null.\n return null;\n };\n\nexport default {\n initTracker,\n currentPosition\n};","// This script is meant to be included in the script bundle so that heatmap and tracker will always run on all pages without them having to call themselves.\n// You'll still be able to include the mouse scripts in other files, without re-initializing it immediatly on your current page.\n// For example if you want to create a widget that shows a heat map.\n\nimport heatmap from './heatmap';\nimport tracking from './tracking';\n\ntracking.initTracker();\nheatmap.initMap();"],"sourceRoot":""}