diff --git a/build/UserALEWebExtension/background.js b/build/UserALEWebExtension/background.js index 24fc4e34..73fb7ec8 100644 --- a/build/UserALEWebExtension/background.js +++ b/build/UserALEWebExtension/background.js @@ -72,34 +72,36 @@ var ADD_LOG = prefix + 'ADD_LOG'; * @return {timeStampScale~tsScaler} The timestamp normalizing function. */ function timeStampScale(e) { - if (e.timeStamp && e.timeStamp > 0) { - var delta = Date.now() - e.timeStamp; - /** - * Returns a timestamp depending on various browser quirks. - * @param {?Number} ts A timestamp to use for normalization. - * @return {Number} A normalized timestamp. - */ - var tsScaler; - - if (delta < 0) { - tsScaler = function () { - return e.timeStamp / 1000; - }; - } else if (delta > e.timeStamp) { - var navStart = performance.timing.navigationStart; - tsScaler = function (ts) { - return ts + navStart; - }; + let tsScaler; + if (e.timeStamp && e.timeStamp > 0) { + const delta = Date.now() - e.timeStamp; + /** + * Returns a timestamp depending on various browser quirks. + * @param {?Number} ts A timestamp to use for normalization. + * @return {Number} A normalized timestamp. + */ + + if (delta < 0) { + tsScaler = function () { + return e.timeStamp / 1000; + }; + } else if (delta > e.timeStamp) { + const navStart = performance.timing.navigationStart; + tsScaler = function (ts) { + return ts + navStart; + }; + } else { + tsScaler = function (ts) { + return ts; + }; + } } else { - tsScaler = function (ts) { - return ts; - }; + tsScaler = function () { + return Date.now(); + }; } - } else { - tsScaler = function () { return Date.now(); }; - } - return tsScaler; + return tsScaler; } var __spreadArrays = (undefined && undefined.__spreadArrays) || function () { @@ -348,7 +350,7 @@ function extractTimeFields(timeStamp) { * limitations under the License. */ -var sendIntervalId = null; +let sendIntervalId = null; /** * Initializes the log queue processors. @@ -417,10 +419,10 @@ function sendOnClose(logs, config) { // @todo expose config object to sendLogs replate url with config.url function sendLogs(logs, config, retries) { - var req = new XMLHttpRequest(); + const req = new XMLHttpRequest(); // @todo setRequestHeader for Auth - var data = JSON.stringify(logs); + const data = JSON.stringify(logs); req.open('POST', config.url); if (config.authHeader) { diff --git a/build/UserALEWebExtension/content.js b/build/UserALEWebExtension/content.js index 0c6e317f..411fe1e2 100644 --- a/build/UserALEWebExtension/content.js +++ b/build/UserALEWebExtension/content.js @@ -67,7 +67,7 @@ var version = "2.1.1"; * limitations under the License. */ - var sessionId = null; +let sessionId = null; /** * Extracts the initial configuration settings from the @@ -75,33 +75,35 @@ var version = "2.1.1"; * @return {Object} The extracted configuration object */ function getInitialSettings() { - var settings = {}; + const settings = {}; - if (sessionId === null) { - sessionId = getSessionId('userAleSessionId', 'session_' + String(Date.now())); - } + if (sessionId === null) { + sessionId = getSessionId('userAleSessionId', 'session_' + String(Date.now())); + } + + const script = document.currentScript || (function () { + const scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; + })(); - var script = document.currentScript || (function () { - var scripts = document.getElementsByTagName('script'); - return scripts[scripts.length - 1]; - })(); - - var get = script ? script.getAttribute.bind(script) : function() { return null; }; - settings.autostart = get('data-autostart') === 'false' ? false : true; - settings.url = get('data-url') || 'http://localhost:8000'; - settings.transmitInterval = +get('data-interval') || 5000; - settings.logCountThreshold = +get('data-threshold') || 5; - settings.userId = get('data-user') || null; - settings.version = get('data-version') || null; - settings.logDetails = get('data-log-details') === 'true' ? true : false; - settings.resolution = +get('data-resolution') || 500; - settings.toolName = get('data-tool') || null; - settings.userFromParams = get('data-user-from-params') || null; - settings.time = timeStampScale(document.createEvent('CustomEvent')); - settings.sessionID = get('data-session') || sessionId; - settings.authHeader = get('data-auth') || null; - settings.custIndex = get('data-index') || null; - return settings; + const get = script ? script.getAttribute.bind(script) : function () { + return null; + }; + settings.autostart = get('data-autostart') === 'false' ? false : true; + settings.url = get('data-url') || 'http://localhost:8000'; + settings.transmitInterval = +get('data-interval') || 5000; + settings.logCountThreshold = +get('data-threshold') || 5; + settings.userId = get('data-user') || null; + settings.version = get('data-version') || null; + settings.logDetails = get('data-log-details') === 'true' ? true : false; + settings.resolution = +get('data-resolution') || 500; + settings.toolName = get('data-tool') || null; + settings.userFromParams = get('data-user-from-params') || null; + settings.time = timeStampScale(document.createEvent('CustomEvent')); + settings.sessionID = get('data-session') || sessionId; + settings.authHeader = get('data-auth') || null; + settings.custIndex = get('data-index') || null; + return settings; } /** @@ -110,13 +112,13 @@ function getInitialSettings() { * from refreshing the current user session * */ -function getSessionId(sessionKey, value){ - if (window.sessionStorage.getItem(sessionKey) === null) { - window.sessionStorage.setItem(sessionKey, JSON.stringify(value)); - return value; - } +function getSessionId(sessionKey, value) { + if (window.sessionStorage.getItem(sessionKey) === null) { + window.sessionStorage.setItem(sessionKey, JSON.stringify(value)); + return value; + } - return JSON.parse(window.sessionStorage.getItem(sessionKey)); + return JSON.parse(window.sessionStorage.getItem(sessionKey)); } @@ -126,34 +128,36 @@ function getSessionId(sessionKey, value){ * @return {timeStampScale~tsScaler} The timestamp normalizing function. */ function timeStampScale(e) { - if (e.timeStamp && e.timeStamp > 0) { - var delta = Date.now() - e.timeStamp; - /** - * Returns a timestamp depending on various browser quirks. - * @param {?Number} ts A timestamp to use for normalization. - * @return {Number} A normalized timestamp. - */ - var tsScaler; - - if (delta < 0) { - tsScaler = function () { - return e.timeStamp / 1000; - }; - } else if (delta > e.timeStamp) { - var navStart = performance.timing.navigationStart; - tsScaler = function (ts) { - return ts + navStart; - }; + let tsScaler; + if (e.timeStamp && e.timeStamp > 0) { + const delta = Date.now() - e.timeStamp; + /** + * Returns a timestamp depending on various browser quirks. + * @param {?Number} ts A timestamp to use for normalization. + * @return {Number} A normalized timestamp. + */ + + if (delta < 0) { + tsScaler = function () { + return e.timeStamp / 1000; + }; + } else if (delta > e.timeStamp) { + const navStart = performance.timing.navigationStart; + tsScaler = function (ts) { + return ts + navStart; + }; + } else { + tsScaler = function (ts) { + return ts; + }; + } } else { - tsScaler = function (ts) { - return ts; - }; + tsScaler = function () { + return Date.now(); + }; } - } else { - tsScaler = function () { return Date.now(); }; - } - return tsScaler; + return tsScaler; } /* @@ -182,7 +186,7 @@ function timeStampScale(e) { function configure(config, newConfig) { Object.keys(newConfig).forEach(function(option) { if (option === 'userFromParams') { - var userId = getUserIdFromParams(newConfig[option]); + const userId = getUserIdFromParams(newConfig[option]); if (userId) { config.userId = userId; } @@ -197,9 +201,9 @@ function configure(config, newConfig) { * @return {string|null} The extracted/decoded userid, or null if none is found. */ function getUserIdFromParams(param) { - var userField = param; - var regex = new RegExp('[?&]' + userField + '(=([^&#]*)|&|#|$)'); - var results = window.location.href.match(regex); + const userField = param; + const regex = new RegExp('[?&]' + userField + '(=([^&#]*)|&|#|$)'); + const results = window.location.href.match(regex); if (results && results[2]) { return decodeURIComponent(results[2].replace(/\+/g, ' ')); @@ -422,21 +426,21 @@ function createVersionParts(count) { * See the License for the specific language governing permissions and * limitations under the License. */ -var browser$1 = detect(); +const browser$1 = detect(); -var logs$1; -var config$1; +let logs$1; +let config$1; // Interval Logging Globals -var intervalID; -var intervalType; -var intervalPath; -var intervalTimer; -var intervalCounter; -var intervalLog; +let intervalID; +let intervalType; +let intervalPath; +let intervalTimer; +let intervalCounter; +let intervalLog; -var filterHandler = null; -var mapHandler = null; +let filterHandler = null; +let mapHandler = null; /** * Assigns a handler to filter logs out of the queue. @@ -476,16 +480,16 @@ function packageLog(e, detailFcn) { return false; } - var details = null; + let details = null; if (detailFcn) { details = detailFcn(e); } - var timeFields = extractTimeFields( + const timeFields = extractTimeFields( (e.timeStamp && e.timeStamp > 0) ? config$1.time(e.timeStamp) : Date.now() ); - var log = { + let log = { 'target' : getSelector(e.target), 'path' : buildPath(e), 'pageUrl': window.location.href, @@ -532,12 +536,12 @@ function packageCustomLog(customLog, detailFcn, userAction) { return false; } - var details = null; + let details = null; if (detailFcn) { details = detailFcn(); } - var metaData = { + const metaData = { 'pageUrl': window.location.href, 'pageTitle': document.title, 'pageReferrer': document.referrer, @@ -554,7 +558,7 @@ function packageCustomLog(customLog, detailFcn, userAction) { 'sessionID': config$1.sessionID }; - var log = Object.assign(metaData, customLog); + let log = Object.assign(metaData, customLog); if ((typeof filterHandler === 'function') && !filterHandler(log)) { return false; @@ -588,10 +592,10 @@ function extractTimeFields(timeStamp) { * @return boolean */ function packageIntervalLog(e) { - var target = getSelector(e.target); - var path = buildPath(e); - var type = e.type; - var timestamp = Math.floor((e.timeStamp && e.timeStamp > 0) ? config$1.time(e.timeStamp) : Date.now()); + const target = getSelector(e.target); + const path = buildPath(e); + const type = e.type; + const timestamp = Math.floor((e.timeStamp && e.timeStamp > 0) ? config$1.time(e.timeStamp) : Date.now()); // Init - this should only happen once on initialization if (intervalID == null) { @@ -702,11 +706,11 @@ function getSelector(ele) { * @return {HTMLElement[]} Array of elements, starting at the event target, ending at the root element. */ function buildPath(e) { - var path = []; + let path = []; if (e.path) { path = e.path; } else { - var ele = e.target; + let ele = e.target; while(ele) { path.push(ele); ele = ele.parentElement; @@ -722,9 +726,9 @@ function buildPath(e) { * @return {string[]} Array of string CSS selectors. */ function selectorizePath(path) { - var i = 0; - var pathEle; - var pathSelectors = []; + let i = 0; + let pathEle; + const pathSelectors = []; while (pathEle = path[i]) { pathSelectors.push(getSelector(pathEle)); ++i; @@ -756,7 +760,7 @@ function detectBrowser() { * limitations under the License. */ -var sendIntervalId = null; +let sendIntervalId = null; /** * Initializes the log queue processors. @@ -843,10 +847,10 @@ function sendOnClose(logs, config) { // @todo expose config object to sendLogs replate url with config.url function sendLogs(logs, config, retries) { - var req = new XMLHttpRequest(); + const req = new XMLHttpRequest(); // @todo setRequestHeader for Auth - var data = JSON.stringify(logs); + const data = JSON.stringify(logs); req.open('POST', config.url); if (config.authHeader) { @@ -883,14 +887,13 @@ function sendLogs(logs, config, retries) { * limitations under the License. */ -// @todo var>let -var events; -var bufferBools; -var bufferedEvents; +let events; +let bufferBools; +let bufferedEvents; //@todo: Investigate drag events and their behavior -var intervalEvents = ['click', 'focus', 'blur', 'input', 'change', 'mouseover', 'submit']; -var refreshEvents; -var windowEvents = ['load', 'blur', 'focus']; +const intervalEvents = ['click', 'focus', 'blur', 'input', 'change', 'mouseover', 'submit']; +let refreshEvents; +const windowEvents = ['load', 'blur', 'focus']; /** * Maps an event to an object containing useful information. @@ -1010,15 +1013,15 @@ function attachHandlers(config) { * limitations under the License. */ -var config = {}; -var logs = []; -var startLoadTimestamp = Date.now(); -var endLoadTimestamp; +const config = {}; +const logs = []; +const startLoadTimestamp = Date.now(); +let endLoadTimestamp; window.onload = function () { endLoadTimestamp = Date.now(); }; -var started = false; +let started = false; // Start up Userale @@ -1040,7 +1043,7 @@ if (config.autostart) { function setup(config) { if (!started) { setTimeout(function () { - var state = document.readyState; + const state = document.readyState; if (state === 'interactive' || state === 'complete') { attachHandlers(config); diff --git a/build/userale-2.1.1.js b/build/userale-2.1.1.js index ea5de7d9..90c80f9e 100644 --- a/build/userale-2.1.1.js +++ b/build/userale-2.1.1.js @@ -22,6 +22,22 @@ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.userale = {})); }(this, (function (exports) { 'use strict'; + function _typeof(obj) { + "@babel/helpers - typeof"; + + if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { + _typeof = function _typeof(obj) { + return typeof obj; + }; + } else { + _typeof = function _typeof(obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; + }; + } + + return _typeof(obj); + } + var version$1 = "2.1.1"; /* @@ -40,14 +56,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - var sessionId = null; - + var sessionId = null; /** * Extracts the initial configuration settings from the * currently executing script tag. * @return {Object} The extracted configuration object */ + function getInitialSettings() { var settings = {}; @@ -55,12 +70,14 @@ sessionId = getSessionId('userAleSessionId', 'session_' + String(Date.now())); } - var script = document.currentScript || (function () { + var script = document.currentScript || function () { var scripts = document.getElementsByTagName('script'); return scripts[scripts.length - 1]; - })(); + }(); - var get = script ? script.getAttribute.bind(script) : function() { return null; }; + var get = script ? script.getAttribute.bind(script) : function () { + return null; + }; settings.autostart = get('data-autostart') === 'false' ? false : true; settings.url = get('data-url') || 'http://localhost:8000'; settings.transmitInterval = +get('data-interval') || 5000; @@ -77,14 +94,14 @@ settings.custIndex = get('data-index') || null; return settings; } - /** * defines sessionId, stores it in sessionStorage, checks to see if there is a sessionId in * storage when script is started. This prevents events like 'submit', which refresh page data * from refreshing the current user session * */ - function getSessionId(sessionKey, value){ + + function getSessionId(sessionKey, value) { if (window.sessionStorage.getItem(sessionKey) === null) { window.sessionStorage.setItem(sessionKey, JSON.stringify(value)); return value; @@ -92,14 +109,15 @@ return JSON.parse(window.sessionStorage.getItem(sessionKey)); } - - /** * Creates a function to normalize the timestamp of the provided event. * @param {Object} e An event containing a timeStamp property. * @return {timeStampScale~tsScaler} The timestamp normalizing function. */ + function timeStampScale(e) { + var tsScaler; + if (e.timeStamp && e.timeStamp > 0) { var delta = Date.now() - e.timeStamp; /** @@ -107,24 +125,26 @@ * @param {?Number} ts A timestamp to use for normalization. * @return {Number} A normalized timestamp. */ - var tsScaler; if (delta < 0) { - tsScaler = function () { + tsScaler = function tsScaler() { return e.timeStamp / 1000; }; } else if (delta > e.timeStamp) { var navStart = performance.timing.navigationStart; - tsScaler = function (ts) { + + tsScaler = function tsScaler(ts) { return ts + navStart; }; } else { - tsScaler = function (ts) { + tsScaler = function tsScaler(ts) { return ts; }; } } else { - tsScaler = function () { return Date.now(); }; + tsScaler = function tsScaler() { + return Date.now(); + }; } return tsScaler; @@ -154,22 +174,24 @@ * @param {Object} newConfig Configuration object to merge into the current config. */ function configure(config, newConfig) { - Object.keys(newConfig).forEach(function(option) { + Object.keys(newConfig).forEach(function (option) { if (option === 'userFromParams') { var userId = getUserIdFromParams(newConfig[option]); + if (userId) { config.userId = userId; } } + config[option] = newConfig[option]; }); } - /** * Attempts to extract the userid from the query parameters of the URL. * @param {string} param The name of the query parameter containing the userid. * @return {string|null} The extracted/decoded userid, or null if none is found. */ + function getUserIdFromParams(param) { var userField = param; var regex = new RegExp('[?&]' + userField + '(=([^&#]*)|&|#|$)'); @@ -397,43 +419,39 @@ * limitations under the License. */ var browser = detect(); - var logs$1; - var config$1; + var config$1; // Interval Logging Globals - // Interval Logging Globals var intervalID; var intervalType; var intervalPath; var intervalTimer; var intervalCounter; var intervalLog; - var filterHandler = null; var mapHandler = null; - /** * Assigns a handler to filter logs out of the queue. * @param {Function} callback The handler to invoke when logging. */ + function setLogFilter(callback) { filterHandler = callback; } - /** * Assigns a handler to transform logs from their default structure. * @param {Function} callback The handler to invoke when logging. */ + function setLogMapper(callback) { mapHandler = callback; } - - /** * Assigns the config and log container to be used by the logging functions. * @param {Array} newLogs Log container. * @param {Object} newConfig Configuration to use while logging. */ + function initPackager(newLogs, newConfig) { logs$1 = newLogs; config$1 = newConfig; @@ -446,50 +464,48 @@ intervalCounter = 0; intervalLog = null; } - /** * Transforms the provided HTML event into a log and appends it to the log queue. * @param {Object} e The event to be logged. * @param {Function} detailFcn The function to extract additional log parameters from the event. * @return {boolean} Whether the event was logged. */ + function packageLog(e, detailFcn) { if (!config$1.on) { return false; } var details = null; + if (detailFcn) { details = detailFcn(e); } - var timeFields = extractTimeFields( - (e.timeStamp && e.timeStamp > 0) ? config$1.time(e.timeStamp) : Date.now() - ); - + var timeFields = extractTimeFields(e.timeStamp && e.timeStamp > 0 ? config$1.time(e.timeStamp) : Date.now()); var log = { - 'target' : getSelector(e.target), - 'path' : buildPath(e), + 'target': getSelector(e.target), + 'path': buildPath(e), 'pageUrl': window.location.href, 'pageTitle': document.title, 'pageReferrer': document.referrer, 'browser': detectBrowser(), - 'clientTime' : timeFields.milli, - 'microTime' : timeFields.micro, - 'location' : getLocation(e), - 'scrnRes' : getSreenRes(), - 'type' : e.type, + 'clientTime': timeFields.milli, + 'microTime': timeFields.micro, + 'location': getLocation(e), + 'scrnRes': getSreenRes(), + 'type': e.type, 'logType': 'raw', - 'userAction' : true, - 'details' : details, - 'userId' : config$1.userId, - 'toolVersion' : config$1.version, - 'toolName' : config$1.toolName, + 'userAction': true, + 'details': details, + 'userId': config$1.userId, + 'toolVersion': config$1.version, + 'toolName': config$1.toolName, 'useraleVersion': config$1.useraleVersion, - 'sessionID': config$1.sessionID, + 'sessionID': config$1.sessionID }; - if ((typeof filterHandler === 'function') && !filterHandler(log)) { + if (typeof filterHandler === 'function' && !filterHandler(log)) { return false; } @@ -498,10 +514,8 @@ } logs$1.push(log); - return true; } - /** * Packages the provided customLog to include standard meta data and appends it to the log queue. * @param {Object} customLog The behavior to be logged. @@ -509,187 +523,199 @@ * @param {boolean} userAction Indicates user behavior (true) or system behavior (false) * @return {boolean} Whether the event was logged. */ - function packageCustomLog(customLog, detailFcn, userAction) { - if (!config$1.on) { - return false; - } - var details = null; - if (detailFcn) { - details = detailFcn(); - } + function packageCustomLog(customLog, detailFcn, userAction) { + if (!config$1.on) { + return false; + } - var metaData = { - 'pageUrl': window.location.href, - 'pageTitle': document.title, - 'pageReferrer': document.referrer, - 'browser': detectBrowser(), - 'clientTime' : Date.now(), - 'scrnRes' : getSreenRes(), - 'logType': 'custom', - 'userAction' : userAction, - 'details' : details, - 'userId' : config$1.userId, - 'toolVersion' : config$1.version, - 'toolName' : config$1.toolName, - 'useraleVersion': config$1.useraleVersion, - 'sessionID': config$1.sessionID - }; + var details = null; - var log = Object.assign(metaData, customLog); + if (detailFcn) { + details = detailFcn(); + } - if ((typeof filterHandler === 'function') && !filterHandler(log)) { - return false; - } + var metaData = { + 'pageUrl': window.location.href, + 'pageTitle': document.title, + 'pageReferrer': document.referrer, + 'browser': detectBrowser(), + 'clientTime': Date.now(), + 'scrnRes': getSreenRes(), + 'logType': 'custom', + 'userAction': userAction, + 'details': details, + 'userId': config$1.userId, + 'toolVersion': config$1.version, + 'toolName': config$1.toolName, + 'useraleVersion': config$1.useraleVersion, + 'sessionID': config$1.sessionID + }; + var log = Object.assign(metaData, customLog); - if (typeof mapHandler === 'function') { - log = mapHandler(log); - } + if (typeof filterHandler === 'function' && !filterHandler(log)) { + return false; + } - logs$1.push(log); + if (typeof mapHandler === 'function') { + log = mapHandler(log); + } - return true; + logs$1.push(log); + return true; } - /** * Extract the millisecond and microsecond portions of a timestamp. * @param {Number} timeStamp The timestamp to split into millisecond and microsecond fields. * @return {Object} An object containing the millisecond * and microsecond portions of the timestamp. */ + function extractTimeFields(timeStamp) { return { milli: Math.floor(timeStamp), - micro: Number((timeStamp % 1).toFixed(3)), + micro: Number((timeStamp % 1).toFixed(3)) }; } - /** * Track intervals and gather details about it. * @param {Object} e * @return boolean */ + function packageIntervalLog(e) { - var target = getSelector(e.target); - var path = buildPath(e); - var type = e.type; - var timestamp = Math.floor((e.timeStamp && e.timeStamp > 0) ? config$1.time(e.timeStamp) : Date.now()); - - // Init - this should only happen once on initialization - if (intervalID == null) { - intervalID = target; - intervalType = type; - intervalPath = path; - intervalTimer = timestamp; - intervalCounter = 0; - } + var target = getSelector(e.target); + var path = buildPath(e); + var type = e.type; + var timestamp = Math.floor(e.timeStamp && e.timeStamp > 0 ? config$1.time(e.timeStamp) : Date.now()); // Init - this should only happen once on initialization + + if (intervalID == null) { + intervalID = target; + intervalType = type; + intervalPath = path; + intervalTimer = timestamp; + intervalCounter = 0; + } - if (intervalID !== target || intervalType !== type) { - // When to create log? On transition end - // @todo Possible for intervalLog to not be pushed in the event the interval never ends... - - intervalLog = { - 'target': intervalID, - 'path': intervalPath, - 'pageUrl': window.location.href, - 'pageTitle': document.title, - 'pageReferrer': document.referrer, - 'browser': detectBrowser(), - 'count': intervalCounter, - 'duration': timestamp - intervalTimer, // microseconds - 'startTime': intervalTimer, - 'endTime': timestamp, - 'type': intervalType, - 'logType': 'interval', - 'targetChange': intervalID !== target, - 'typeChange': intervalType !== type, - 'userAction': false, - 'userId': config$1.userId, - 'toolVersion': config$1.version, - 'toolName': config$1.toolName, - 'useraleVersion': config$1.useraleVersion, - 'sessionID': config$1.sessionID - }; + if (intervalID !== target || intervalType !== type) { + // When to create log? On transition end + // @todo Possible for intervalLog to not be pushed in the event the interval never ends... + intervalLog = { + 'target': intervalID, + 'path': intervalPath, + 'pageUrl': window.location.href, + 'pageTitle': document.title, + 'pageReferrer': document.referrer, + 'browser': detectBrowser(), + 'count': intervalCounter, + 'duration': timestamp - intervalTimer, + // microseconds + 'startTime': intervalTimer, + 'endTime': timestamp, + 'type': intervalType, + 'logType': 'interval', + 'targetChange': intervalID !== target, + 'typeChange': intervalType !== type, + 'userAction': false, + 'userId': config$1.userId, + 'toolVersion': config$1.version, + 'toolName': config$1.toolName, + 'useraleVersion': config$1.useraleVersion, + 'sessionID': config$1.sessionID + }; - if (typeof filterHandler === 'function' && !filterHandler(intervalLog)) { - return false; - } + if (typeof filterHandler === 'function' && !filterHandler(intervalLog)) { + return false; + } - if (typeof mapHandler === 'function') { - intervalLog = mapHandler(intervalLog, e); - } + if (typeof mapHandler === 'function') { + intervalLog = mapHandler(intervalLog, e); + } - logs$1.push(intervalLog); + logs$1.push(intervalLog); // Reset - // Reset - intervalID = target; - intervalType = type; - intervalPath = path; - intervalTimer = timestamp; - intervalCounter = 0; - } + intervalID = target; + intervalType = type; + intervalPath = path; + intervalTimer = timestamp; + intervalCounter = 0; + } // Interval is still occuring, just update counter - // Interval is still occuring, just update counter - if (intervalID == target && intervalType == type) { - intervalCounter = intervalCounter + 1; - } - return true; - } + if (intervalID == target && intervalType == type) { + intervalCounter = intervalCounter + 1; + } + return true; + } /** * Extracts coordinate information from the event * depending on a few browser quirks. * @param {Object} e The event to extract coordinate information from. * @return {Object} An object containing nullable x and y coordinates for the event. */ + function getLocation(e) { if (e.pageX != null) { - return { 'x' : e.pageX, 'y' : e.pageY }; + return { + 'x': e.pageX, + 'y': e.pageY + }; } else if (e.clientX != null) { - return { 'x' : document.documentElement.scrollLeft + e.clientX, 'y' : document.documentElement.scrollTop + e.clientY }; + return { + 'x': document.documentElement.scrollLeft + e.clientX, + 'y': document.documentElement.scrollTop + e.clientY + }; } else { - return { 'x' : null, 'y' : null }; + return { + 'x': null, + 'y': null + }; } } - /** * Extracts innerWidth and innerHeight to provide estimates of screen resolution * @return {Object} An object containing the innerWidth and InnerHeight */ + function getSreenRes() { - return { 'width': window.innerWidth, 'height': window.innerHeight}; + return { + 'width': window.innerWidth, + 'height': window.innerHeight + }; } - /** * Builds a string CSS selector from the provided element * @param {HTMLElement} ele The element from which the selector is built. * @return {string} The CSS selector for the element, or Unknown if it can't be determined. */ + function getSelector(ele) { if (ele.localName) { - return ele.localName + (ele.id ? ('#' + ele.id) : '') + (ele.className ? ('.' + ele.className) : ''); + return ele.localName + (ele.id ? '#' + ele.id : '') + (ele.className ? '.' + ele.className : ''); } else if (ele.nodeName) { - return ele.nodeName + (ele.id ? ('#' + ele.id) : '') + (ele.className ? ('.' + ele.className) : ''); + return ele.nodeName + (ele.id ? '#' + ele.id : '') + (ele.className ? '.' + ele.className : ''); } else if (ele && ele.document && ele.location && ele.alert && ele.setInterval) { return "Window"; } else { return "Unknown"; } } - /** * Builds an array of elements from the provided event target, to the root element. * @param {Object} e Event from which the path should be built. * @return {HTMLElement[]} Array of elements, starting at the event target, ending at the root element. */ + function buildPath(e) { var path = []; + if (e.path) { path = e.path; } else { var ele = e.target; - while(ele) { + + while (ele) { path.push(ele); ele = ele.parentElement; } @@ -697,28 +723,29 @@ return selectorizePath(path); } - /** * Builds a CSS selector path from the provided list of elements. * @param {HTMLElement[]} path Array of HTMLElements from which the path should be built. * @return {string[]} Array of string CSS selectors. */ + function selectorizePath(path) { var i = 0; var pathEle; var pathSelectors = []; + while (pathEle = path[i]) { pathSelectors.push(getSelector(pathEle)); ++i; } + return pathSelectors; } - function detectBrowser() { - return { - 'browser': browser ? browser.name : '', - 'version': browser ? browser.version : '' - }; + return { + 'browser': browser ? browser.name : '', + 'version': browser ? browser.version : '' + }; } /* @@ -737,14 +764,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - var sendIntervalId = null; - /** * Initializes the log queue processors. * @param {Array} logs Array of logs to append to. * @param {Object} config Configuration object to use when logging. */ + function initSender(logs, config) { if (sendIntervalId !== null) { clearInterval(sendIntervalId); @@ -753,7 +779,6 @@ sendIntervalId = sendOnInterval(logs, config); sendOnClose(logs, config); } - /** * Checks the provided log array on an interval, flushing the logs * if the queue has reached the threshold specified by the provided config. @@ -761,19 +786,20 @@ * @param {Object} config Configuration object to be read from. * @return {Number} The newly created interval id. */ + function sendOnInterval(logs, config) { - return setInterval(function() { + return setInterval(function () { if (!config.on) { return; } if (logs.length >= config.logCountThreshold) { sendLogs(logs.slice(0), config, 0); // Send a copy + logs.splice(0); // Clear array reference (no reassignment) } }, config.transmitInterval); } - /** * Provides a simplified send function that can be called before events that would * refresh page can resolve so that log queue ('logs) can be shipped immediately. This @@ -783,38 +809,39 @@ * @param {Array} logs Array of logs to read from. * @param {Object} config Configuration object to be read from. */ + function sendOnRefresh(logs, config) { if (!config.on) { return; } + if (logs.length > 0) { sendLogs(logs, config, 1); } } - /** * Attempts to flush the remaining logs when the window is closed. * @param {Array} logs Array of logs to be flushed. * @param {Object} config Configuration object to be read from. */ + function sendOnClose(logs, config) { if (!config.on) { return; } if (navigator.sendBeacon) { - window.addEventListener('unload', function() { + window.addEventListener('unload', function () { navigator.sendBeacon(config.url, JSON.stringify(logs)); }); } else { - window.addEventListener('beforeunload', function() { + window.addEventListener('beforeunload', function () { if (logs.length > 0) { sendLogs(logs, config, 1); } }); } } - /** * Sends the provided array of logs to the specified url, * retrying the request up to the specified number of retries. @@ -822,22 +849,21 @@ * @param {string} config configuration parameters (e.g., to extract URL from & send the POST request to). * @param {Number} retries Maximum number of attempts to send the logs. */ - // @todo expose config object to sendLogs replate url with config.url + function sendLogs(logs, config, retries) { - var req = new XMLHttpRequest(); + var req = new XMLHttpRequest(); // @todo setRequestHeader for Auth - // @todo setRequestHeader for Auth var data = JSON.stringify(logs); - req.open('POST', config.url); + if (config.authHeader) { req.setRequestHeader('Authorization', config.authHeader); } req.setRequestHeader('Content-type', 'application/json;charset=UTF-8'); - req.onreadystatechange = function() { + req.onreadystatechange = function () { if (req.readyState === 4 && req.status !== 200) { if (retries > 0) { sendLogs(logs, config, retries--); @@ -864,264 +890,304 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - - // @todo var>let var events; var bufferBools; - var bufferedEvents; - //@todo: Investigate drag events and their behavior + var bufferedEvents; //@todo: Investigate drag events and their behavior + var intervalEvents = ['click', 'focus', 'blur', 'input', 'change', 'mouseover', 'submit']; var refreshEvents; var windowEvents = ['load', 'blur', 'focus']; - /** * Maps an event to an object containing useful information. * @param {Object} e Event to extract data from */ + function extractMouseEvent(e) { return { - 'clicks' : e.detail, - 'ctrl' : e.ctrlKey, - 'alt' : e.altKey, - 'shift' : e.shiftKey, - 'meta' : e.metaKey, - // 'text' : e.target.innerHTML + 'clicks': e.detail, + 'ctrl': e.ctrlKey, + 'alt': e.altKey, + 'shift': e.shiftKey, + 'meta': e.metaKey // 'text' : e.target.innerHTML + }; } - /** * Defines the way information is extracted from various events. * Also defines which events we will listen to. * @param {Object} config Configuration object to read from. */ + function defineDetails(config) { // Events list // Keys are event types // Values are functions that return details object if applicable events = { - 'click' : extractMouseEvent, - 'dblclick' : extractMouseEvent, - 'mousedown' : extractMouseEvent, - 'mouseup' : extractMouseEvent, - 'focus' : null, - 'blur' : null, - 'input' : config.logDetails ? function(e) { return { 'value' : e.target.value }; } : null, - 'change' : config.logDetails ? function(e) { return { 'value' : e.target.value }; } : null, - 'dragstart' : null, - 'dragend' : null, - 'drag' : null, - 'drop' : null, - 'keydown' : config.logDetails ? function(e) { return { 'key' : e.keyCode, 'ctrl' : e.ctrlKey, 'alt' : e.altKey, 'shift' : e.shiftKey, 'meta' : e.metaKey }; } : null, - 'mouseover' : null + 'click': extractMouseEvent, + 'dblclick': extractMouseEvent, + 'mousedown': extractMouseEvent, + 'mouseup': extractMouseEvent, + 'focus': null, + 'blur': null, + 'input': config.logDetails ? function (e) { + return { + 'value': e.target.value + }; + } : null, + 'change': config.logDetails ? function (e) { + return { + 'value': e.target.value + }; + } : null, + 'dragstart': null, + 'dragend': null, + 'drag': null, + 'drop': null, + 'keydown': config.logDetails ? function (e) { + return { + 'key': e.keyCode, + 'ctrl': e.ctrlKey, + 'alt': e.altKey, + 'shift': e.shiftKey, + 'meta': e.metaKey + }; + } : null, + 'mouseover': null }; - bufferBools = {}; bufferedEvents = { - 'wheel' : function(e) { return { 'x' : e.deltaX, 'y' : e.deltaY, 'z' : e.deltaZ }; }, - 'scroll' : function() { return { 'x' : window.scrollX, 'y' : window.scrollY }; }, - 'resize' : function() { return { 'width' : window.outerWidth, 'height' : window.outerHeight }; } + 'wheel': function wheel(e) { + return { + 'x': e.deltaX, + 'y': e.deltaY, + 'z': e.deltaZ + }; + }, + 'scroll': function scroll() { + return { + 'x': window.scrollX, + 'y': window.scrollY + }; + }, + 'resize': function resize() { + return { + 'width': window.outerWidth, + 'height': window.outerHeight + }; + } }; - refreshEvents = { - 'submit' : null + 'submit': null }; } - /** * Defines the way information is extracted from various events. * Also defines which events we will listen to. * @param {Object} options UserALE.js Configuration object to read from. * @param {string} type of html event (e.g., 'click', 'mouseover', etc.), such as passed to addEventListener methods. */ + function defineCustomDetails(options, type) { // Events list // Keys are event types // Values are functions that return details object if applicable var eventType = { - 'click' : extractMouseEvent, - 'dblclick' : extractMouseEvent, - 'mousedown' : extractMouseEvent, - 'mouseup' : extractMouseEvent, - 'focus' : null, - 'blur' : null, - 'input' : options.logDetails ? function(e) { return { 'value' : e.target.value }; } : null, - 'change' : options.logDetails ? function(e) { return { 'value' : e.target.value }; } : null, - 'dragstart' : null, - 'dragend' : null, - 'drag' : null, - 'drop' : null, - 'keydown' : options.logDetails ? function(e) { return { 'key' : e.keyCode, 'ctrl' : e.ctrlKey, 'alt' : e.altKey, 'shift' : e.shiftKey, 'meta' : e.metaKey }; } : null, - 'mouseover' : null, - 'wheel' : function(e) { return { 'x' : e.deltaX, 'y' : e.deltaY, 'z' : e.deltaZ }; }, - 'scroll' : function() { return { 'x' : window.scrollX, 'y' : window.scrollY }; }, - 'resize' : function() { return { 'width' : window.outerWidth, 'height' : window.outerHeight }; }, - 'submit' : null + 'click': extractMouseEvent, + 'dblclick': extractMouseEvent, + 'mousedown': extractMouseEvent, + 'mouseup': extractMouseEvent, + 'focus': null, + 'blur': null, + 'input': options.logDetails ? function (e) { + return { + 'value': e.target.value + }; + } : null, + 'change': options.logDetails ? function (e) { + return { + 'value': e.target.value + }; + } : null, + 'dragstart': null, + 'dragend': null, + 'drag': null, + 'drop': null, + 'keydown': options.logDetails ? function (e) { + return { + 'key': e.keyCode, + 'ctrl': e.ctrlKey, + 'alt': e.altKey, + 'shift': e.shiftKey, + 'meta': e.metaKey + }; + } : null, + 'mouseover': null, + 'wheel': function wheel(e) { + return { + 'x': e.deltaX, + 'y': e.deltaY, + 'z': e.deltaZ + }; + }, + 'scroll': function scroll() { + return { + 'x': window.scrollX, + 'y': window.scrollY + }; + }, + 'resize': function resize() { + return { + 'width': window.outerWidth, + 'height': window.outerHeight + }; + }, + 'submit': null }; return eventType[type]; } - /** * Hooks the event handlers for each event type of interest. * @param {Object} config Configuration object to use. * @return {boolean} Whether the operation succeeded */ + function attachHandlers(config) { defineDetails(config); - - Object.keys(events).forEach(function(ev) { - document.addEventListener(ev, function(e) { + Object.keys(events).forEach(function (ev) { + document.addEventListener(ev, function (e) { packageLog(e, events[ev]); }, true); }); - - intervalEvents.forEach(function(ev) { - document.addEventListener(ev, function(e) { - packageIntervalLog(e); + intervalEvents.forEach(function (ev) { + document.addEventListener(ev, function (e) { + packageIntervalLog(e); }, true); }); - - Object.keys(bufferedEvents).forEach(function(ev) { + Object.keys(bufferedEvents).forEach(function (ev) { bufferBools[ev] = true; - - window.addEventListener(ev, function(e) { + window.addEventListener(ev, function (e) { if (bufferBools[ev]) { bufferBools[ev] = false; packageLog(e, bufferedEvents[ev]); - setTimeout(function() { bufferBools[ev] = true; }, config.resolution); + setTimeout(function () { + bufferBools[ev] = true; + }, config.resolution); } }, true); }); - - Object.keys(refreshEvents).forEach(function(ev) { - document.addEventListener(ev, function(e) { + Object.keys(refreshEvents).forEach(function (ev) { + document.addEventListener(ev, function (e) { packageLog(e, events[ev]); - sendOnRefresh(logs$1,config); + sendOnRefresh(logs$1, config); }, true); }); - - windowEvents.forEach(function(ev) { - window.addEventListener(ev, function(e) { - packageLog(e, function() { return { 'window' : true }; }); + windowEvents.forEach(function (ev) { + window.addEventListener(ev, function (e) { + packageLog(e, function () { + return { + 'window': true + }; + }); }, true); }); - return true; } - /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - var config = {}; var logs = []; var startLoadTimestamp = Date.now(); var endLoadTimestamp; + window.onload = function () { - endLoadTimestamp = Date.now(); + endLoadTimestamp = Date.now(); }; exports.started = false; - - // Start up Userale config.on = false; config.useraleVersion = version$1; - configure(config, getInitialSettings()); initPackager(logs, config); if (config.autostart) { - setup(config); + setup(config); } - /** * Hooks the global event listener, and starts up the * logging interval. * @param {Object} config Configuration settings for the logger */ + + function setup(config) { - if (!exports.started) { - setTimeout(function () { - var state = document.readyState; - - if (state === 'interactive' || state === 'complete') { - attachHandlers(config); - initSender(logs, config); - exports.started = config.on = true; - packageCustomLog({ - type: 'load', - logType: 'raw', - pageLoadTime: endLoadTimestamp - startLoadTimestamp - }, () => {},false); - } else { - setup(config); - } - }, 100); - } - } + if (!exports.started) { + setTimeout(function () { + var state = document.readyState; + + if (state === 'interactive' || state === 'complete') { + attachHandlers(config); + initSender(logs, config); + exports.started = config.on = true; + packageCustomLog({ + type: 'load', + logType: 'raw', + pageLoadTime: endLoadTimestamp - startLoadTimestamp + }, function () {}, false); + } else { + setup(config); + } + }, 100); + } + } // Export the Userale API - // Export the Userale API var version = version$1; - /** * Used to start the logging process if the * autostart configuration option is set to false. */ + function start() { - if (!exports.started) { - setup(config); - } + if (!exports.started) { + setup(config); + } - config.on = true; + config.on = true; } - /** * Halts the logging process. Logs will no longer be sent. */ + function stop() { - config.on = false; + config.on = false; } - /** * Updates the current configuration * object with the provided values. * @param {Object} newConfig The configuration options to use. * @return {Object} Returns the updated configuration. */ + function options(newConfig) { - if (newConfig !== undefined) { - configure(config, newConfig); - } + if (newConfig !== undefined) { + configure(config, newConfig); + } - return config; + return config; } - /** * Appends a log to the log queue. * @param {Object} customLog The log to append. * @return {boolean} Whether the operation succeeded. */ + function log(customLog) { - if (customLog !== null && typeof customLog === 'object') { - logs.push(customLog); - return true; - } else { - return false; - } + if (customLog !== null && _typeof(customLog) === 'object') { + logs.push(customLog); + return true; + } else { + return false; + } } exports.buildPath = buildPath; diff --git a/build/userale-2.1.1.min.js b/build/userale-2.1.1.min.js index 9b6cd5bd..006ad836 100644 --- a/build/userale-2.1.1.min.js +++ b/build/userale-2.1.1.min.js @@ -15,4 +15,4 @@ * limitations under the License. * @preserved */ -!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).userale={})}(this,function(t){"use strict";var e="2.1.1",r=null;function n(t,o){Object.keys(o).forEach(function(e){var n;"userFromParams"!==e||(n=function(e){e=new RegExp("[?&]"+e+"(=([^&#]*)|&|#|$)"),e=window.location.href.match(e);return e&&e[2]?decodeURIComponent(e[2].replace(/\+/g," ")):null}(o[e]))&&(t.userId=n),t[e]=o[e]})}var i=function(){for(var e=0,n=0,t=arguments.length;n=o.logCountThreshold&&(P(t.slice(0),o,0),t.splice(0))},o.transmitInterval),r=e,(i=n).on&&(navigator.sendBeacon?window.addEventListener("unload",function(){navigator.sendBeacon(i.url,JSON.stringify(r))}):window.addEventListener("beforeunload",function(){0{},!1)):J(n)},100)}window.onload=function(){H=Date.now()},t.started=!1,Y.on=!1,Y.useraleVersion=e,n(Y,function(){var e,n,t={};null===r&&(e="userAleSessionId",n="session_"+String(Date.now()),r=null!==window.sessionStorage.getItem(e)?JSON.parse(window.sessionStorage.getItem(e)):(window.sessionStorage.setItem(e,JSON.stringify(n)),n));var o=(o=document.currentScript||(o=document.getElementsByTagName("script"))[o.length-1])?o.getAttribute.bind(o):function(){return null};return t.autostart="false"!==o("data-autostart"),t.url=o("data-url")||"http://localhost:8000",t.transmitInterval=+o("data-interval")||5e3,t.logCountThreshold=+o("data-threshold")||5,t.userId=o("data-user")||null,t.version=o("data-version")||null,t.logDetails="true"===o("data-log-details"),t.resolution=+o("data-resolution")||500,t.toolName=o("data-tool")||null,t.userFromParams=o("data-user-from-params")||null,t.time=function(e){{var n,t;n=e.timeStamp&&0e.timeStamp?(t=performance.timing.navigationStart,function(e){return e+t}):function(e){return e}):function(){return Date.now()}}return n}(document.createEvent("CustomEvent")),t.sessionID=o("data-session")||r,t.authHeader=o("data-auth")||null,t.custIndex=o("data-index")||null,t}()),w=F,g=Y,O=0,T=S=b=y=v=N=k=null,Y.autostart&&J(Y);t.buildPath=B,t.details=function(e,n){return{click:j,dblclick:j,mousedown:j,mouseup:j,focus:null,blur:null,input:e.logDetails?function(e){return{value:e.target.value}}:null,change:e.logDetails?function(e){return{value:e.target.value}}:null,dragstart:null,dragend:null,drag:null,drop:null,keydown:e.logDetails?function(e){return{key:e.keyCode,ctrl:e.ctrlKey,alt:e.altKey,shift:e.shiftKey,meta:e.metaKey}}:null,mouseover:null,wheel:function(e){return{x:e.deltaX,y:e.deltaY,z:e.deltaZ}},scroll:function(){return{x:window.scrollX,y:window.scrollY}},resize:function(){return{width:window.outerWidth,height:window.outerHeight}},submit:null}[n]},t.filter=function(e){k=e},t.getSelector=D,t.log=function(e){return null!==e&&"object"==typeof e&&(F.push(e),!0)},t.map=function(e){N=e},t.options=function(e){return void 0!==e&&n(Y,e),Y},t.packageCustomLog=E,t.packageLog=I,t.start=function(){t.started||J(Y),Y.on=!0},t.stop=function(){Y.on=!1},t.version="2.1.1",Object.defineProperty(t,"__esModule",{value:!0})}); \ No newline at end of file +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e="undefined"!=typeof globalThis?globalThis:e||self).userale={})}(this,function(t){"use strict";function n(e){return(n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e})(e)}var e="2.1.1",r=null;function o(t,o){Object.keys(o).forEach(function(e){var n;"userFromParams"!==e||(n=function(e){e=new RegExp("[?&]"+e+"(=([^&#]*)|&|#|$)"),e=window.location.href.match(e);return e&&e[2]?decodeURIComponent(e[2].replace(/\+/g," ")):null}(o[e]))&&(t.userId=n),t[e]=o[e]})}var i=function(){for(var e=0,n=0,t=arguments.length;n=o.logCountThreshold&&(R(t.slice(0),o,0),t.splice(0))},o.transmitInterval),r=e,(i=n).on&&(navigator.sendBeacon?window.addEventListener("unload",function(){navigator.sendBeacon(i.url,JSON.stringify(r))}):window.addEventListener("beforeunload",function(){0e.timeStamp?(t=performance.timing.navigationStart,function(e){return e+t}):function(e){return e}):function(){return Date.now()}}return n}(document.createEvent("CustomEvent")),t.sessionID=o("data-session")||r,t.authHeader=o("data-auth")||null,t.custIndex=o("data-index")||null,t}()),g=z,v=F,T=0,W=O=S=b=y=I=N=null,F.autostart&&U(F);t.buildPath=A,t.details=function(e,n){return{click:$,dblclick:$,mousedown:$,mouseup:$,focus:null,blur:null,input:e.logDetails?function(e){return{value:e.target.value}}:null,change:e.logDetails?function(e){return{value:e.target.value}}:null,dragstart:null,dragend:null,drag:null,drop:null,keydown:e.logDetails?function(e){return{key:e.keyCode,ctrl:e.ctrlKey,alt:e.altKey,shift:e.shiftKey,meta:e.metaKey}}:null,mouseover:null,wheel:function(e){return{x:e.deltaX,y:e.deltaY,z:e.deltaZ}},scroll:function(){return{x:window.scrollX,y:window.scrollY}},resize:function(){return{width:window.outerWidth,height:window.outerHeight}},submit:null}[n]},t.filter=function(e){N=e},t.getSelector=B,t.log=function(e){return null!==e&&"object"===n(e)&&(z.push(e),!0)},t.map=function(e){I=e},t.options=function(e){return void 0!==e&&o(F,e),F},t.packageCustomLog=x,t.packageLog=E,t.start=function(){t.started||U(F),F.on=!0},t.stop=function(){F.on=!1},t.version="2.1.1",Object.defineProperty(t,"__esModule",{value:!0})}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 10c2f993..255c56dc 100755 --- a/gulpfile.js +++ b/gulpfile.js @@ -36,6 +36,7 @@ var version = require('./package.json').version; var uglifyjs = require('uglify-es'); var userale = 'userale-' + version; var userAleWebExtDirName = 'UserALEWebExtension'; +const {babel: rollupBabel} = require('@rollup/plugin-babel'); // Clean build directory gulp.task('clean', function() { @@ -66,7 +67,8 @@ gulp.task('rollup', function() { }), json(), nodeResolve(), - commonjs() + commonjs({ include: /node_modules/ }), + rollupBabel({ babelHelpers: "runtime", exclude: /node_modules/, plugins: ["@babel/plugin-transform-block-scoping"] }), ] }) .then(function(bundle) { diff --git a/package-lock.json b/package-lock.json index b5ce3f77..cd2580ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,11 @@ "license": "Apache-2.0", "devDependencies": { "@babel/core": "^7.13.8", + "@babel/plugin-transform-block-scoping": "^7.12.13", "@babel/plugin-transform-runtime": "^7.13.9", "@babel/preset-env": "^7.13.9", "@babel/register": "^7.13.8", + "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^17.1.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.2.0", @@ -1322,6 +1324,29 @@ "node": ">= 8" } }, + "node_modules/@rollup/plugin-babel": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", + "integrity": "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-commonjs": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", @@ -2768,7 +2793,6 @@ "anymatch": "^2.0.0", "async-each": "^1.0.1", "braces": "^2.3.2", - "fsevents": "^1.2.7", "glob-parent": "^3.1.0", "inherits": "^2.0.3", "is-binary-path": "^1.0.0", @@ -3828,8 +3852,7 @@ "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "optionator": "^0.8.1" }, "bin": { "escodegen": "bin/escodegen.js", @@ -7483,7 +7506,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -8044,7 +8066,6 @@ "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", @@ -9924,9 +9945,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.41.2.tgz", "integrity": "sha512-6u8fJJXJx6fmvKrAC9DHYZgONvSkz8S9b/VFBjoQ6dkKdHyPpPbpqiNl2Bao9XBzDHpq672X6sGZ9G1ZBqAHMg==", "dev": true, - "dependencies": { - "fsevents": "~2.3.1" - }, "bin": { "rollup": "dist/bin/rollup" }, @@ -13441,6 +13459,16 @@ "fastq": "^1.6.0" } }, + "@rollup/plugin-babel": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.0.tgz", + "integrity": "sha512-9uIC8HZOnVLrLHxayq/PTzw+uS25E14KPUBh5ktF+18Mjo5yK0ToMMx6epY0uEgkjwJw0aBW4x2horYXh8juWw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@rollup/pluginutils": "^3.1.0" + } + }, "@rollup/plugin-commonjs": { "version": "17.1.0", "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-17.1.0.tgz", diff --git a/package.json b/package.json index a005e9c8..a26d0200 100644 --- a/package.json +++ b/package.json @@ -43,9 +43,11 @@ }, "devDependencies": { "@babel/core": "^7.13.8", + "@babel/plugin-transform-block-scoping": "^7.12.13", "@babel/plugin-transform-runtime": "^7.13.9", "@babel/preset-env": "^7.13.9", "@babel/register": "^7.13.8", + "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^17.1.0", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "^11.2.0", diff --git a/src/attachHandlers.js b/src/attachHandlers.js index 0d6967a8..5baa2018 100644 --- a/src/attachHandlers.js +++ b/src/attachHandlers.js @@ -20,14 +20,13 @@ import { packageLog } from './packageLogs.js'; import { packageIntervalLog} from './packageLogs'; import { sendOnRefresh } from "./sendLogs"; -// @todo var>let -var events; -var bufferBools; -var bufferedEvents; +let events; +let bufferBools; +let bufferedEvents; //@todo: Investigate drag events and their behavior -var intervalEvents = ['click', 'focus', 'blur', 'input', 'change', 'mouseover', 'submit']; -var refreshEvents; -var windowEvents = ['load', 'blur', 'focus']; +const intervalEvents = ['click', 'focus', 'blur', 'input', 'change', 'mouseover', 'submit']; +let refreshEvents; +const windowEvents = ['load', 'blur', 'focus']; /** * Maps an event to an object containing useful information. @@ -92,7 +91,7 @@ export function defineCustomDetails(options, type) { // Events list // Keys are event types // Values are functions that return details object if applicable - var eventType = { + const eventType = { 'click' : extractMouseEvent, 'dblclick' : extractMouseEvent, 'mousedown' : extractMouseEvent, diff --git a/src/configure.js b/src/configure.js index 32dc9ec1..fb369d9e 100644 --- a/src/configure.js +++ b/src/configure.js @@ -24,7 +24,7 @@ export function configure(config, newConfig) { Object.keys(newConfig).forEach(function(option) { if (option === 'userFromParams') { - var userId = getUserIdFromParams(newConfig[option]); + const userId = getUserIdFromParams(newConfig[option]); if (userId) { config.userId = userId; } @@ -39,9 +39,9 @@ export function configure(config, newConfig) { * @return {string|null} The extracted/decoded userid, or null if none is found. */ export function getUserIdFromParams(param) { - var userField = param; - var regex = new RegExp('[?&]' + userField + '(=([^&#]*)|&|#|$)'); - var results = window.location.href.match(regex); + const userField = param; + const regex = new RegExp('[?&]' + userField + '(=([^&#]*)|&|#|$)'); + const results = window.location.href.match(regex); if (results && results[2]) { return decodeURIComponent(results[2].replace(/\+/g, ' ')); diff --git a/src/getInitialSettings.js b/src/getInitialSettings.js index 01abf4c4..174c6424 100644 --- a/src/getInitialSettings.js +++ b/src/getInitialSettings.js @@ -15,7 +15,7 @@ * limitations under the License. */ - var sessionId = null; +let sessionId = null; /** * Extracts the initial configuration settings from the @@ -23,33 +23,35 @@ * @return {Object} The extracted configuration object */ export function getInitialSettings() { - var settings = {}; + const settings = {}; - if (sessionId === null) { - sessionId = getSessionId('userAleSessionId', 'session_' + String(Date.now())); - } + if (sessionId === null) { + sessionId = getSessionId('userAleSessionId', 'session_' + String(Date.now())); + } - var script = document.currentScript || (function () { - var scripts = document.getElementsByTagName('script'); - return scripts[scripts.length - 1]; - })(); + const script = document.currentScript || (function () { + const scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; + })(); - var get = script ? script.getAttribute.bind(script) : function() { return null; }; - settings.autostart = get('data-autostart') === 'false' ? false : true; - settings.url = get('data-url') || 'http://localhost:8000'; - settings.transmitInterval = +get('data-interval') || 5000; - settings.logCountThreshold = +get('data-threshold') || 5; - settings.userId = get('data-user') || null; - settings.version = get('data-version') || null; - settings.logDetails = get('data-log-details') === 'true' ? true : false; - settings.resolution = +get('data-resolution') || 500; - settings.toolName = get('data-tool') || null; - settings.userFromParams = get('data-user-from-params') || null; - settings.time = timeStampScale(document.createEvent('CustomEvent')); - settings.sessionID = get('data-session') || sessionId; - settings.authHeader = get('data-auth') || null; - settings.custIndex = get('data-index') || null; - return settings; + const get = script ? script.getAttribute.bind(script) : function () { + return null; + }; + settings.autostart = get('data-autostart') === 'false' ? false : true; + settings.url = get('data-url') || 'http://localhost:8000'; + settings.transmitInterval = +get('data-interval') || 5000; + settings.logCountThreshold = +get('data-threshold') || 5; + settings.userId = get('data-user') || null; + settings.version = get('data-version') || null; + settings.logDetails = get('data-log-details') === 'true' ? true : false; + settings.resolution = +get('data-resolution') || 500; + settings.toolName = get('data-tool') || null; + settings.userFromParams = get('data-user-from-params') || null; + settings.time = timeStampScale(document.createEvent('CustomEvent')); + settings.sessionID = get('data-session') || sessionId; + settings.authHeader = get('data-auth') || null; + settings.custIndex = get('data-index') || null; + return settings; } /** @@ -58,13 +60,13 @@ export function getInitialSettings() { * from refreshing the current user session * */ -export function getSessionId(sessionKey, value){ - if (window.sessionStorage.getItem(sessionKey) === null) { - window.sessionStorage.setItem(sessionKey, JSON.stringify(value)); - return value; - } +export function getSessionId(sessionKey, value) { + if (window.sessionStorage.getItem(sessionKey) === null) { + window.sessionStorage.setItem(sessionKey, JSON.stringify(value)); + return value; + } - return JSON.parse(window.sessionStorage.getItem(sessionKey)); + return JSON.parse(window.sessionStorage.getItem(sessionKey)); } @@ -74,32 +76,34 @@ export function getSessionId(sessionKey, value){ * @return {timeStampScale~tsScaler} The timestamp normalizing function. */ export function timeStampScale(e) { - if (e.timeStamp && e.timeStamp > 0) { - var delta = Date.now() - e.timeStamp; - /** - * Returns a timestamp depending on various browser quirks. - * @param {?Number} ts A timestamp to use for normalization. - * @return {Number} A normalized timestamp. - */ - var tsScaler; + let tsScaler; + if (e.timeStamp && e.timeStamp > 0) { + const delta = Date.now() - e.timeStamp; + /** + * Returns a timestamp depending on various browser quirks. + * @param {?Number} ts A timestamp to use for normalization. + * @return {Number} A normalized timestamp. + */ - if (delta < 0) { - tsScaler = function () { - return e.timeStamp / 1000; - }; - } else if (delta > e.timeStamp) { - var navStart = performance.timing.navigationStart; - tsScaler = function (ts) { - return ts + navStart; - } + if (delta < 0) { + tsScaler = function () { + return e.timeStamp / 1000; + }; + } else if (delta > e.timeStamp) { + const navStart = performance.timing.navigationStart; + tsScaler = function (ts) { + return ts + navStart; + } + } else { + tsScaler = function (ts) { + return ts; + } + } } else { - tsScaler = function (ts) { - return ts; - } + tsScaler = function () { + return Date.now(); + }; } - } else { - tsScaler = function () { return Date.now(); }; - } - return tsScaler; + return tsScaler; } diff --git a/src/main.js b/src/main.js index 7bd20942..f1bdfad0 100644 --- a/src/main.js +++ b/src/main.js @@ -22,15 +22,15 @@ import {attachHandlers} from './attachHandlers.js'; import {initPackager, packageCustomLog} from './packageLogs.js'; import {initSender} from './sendLogs.js'; -var config = {}; -var logs = []; -var startLoadTimestamp = Date.now() -var endLoadTimestamp +const config = {}; +const logs = []; +const startLoadTimestamp = Date.now() +let endLoadTimestamp window.onload = function () { endLoadTimestamp = Date.now() } -export var started = false; +export let started = false; export {defineCustomDetails as details} from './attachHandlers.js'; export { setLogMapper as map, @@ -61,7 +61,7 @@ if (config.autostart) { function setup(config) { if (!started) { setTimeout(function () { - var state = document.readyState; + const state = document.readyState; if (state === 'interactive' || state === 'complete') { attachHandlers(config); @@ -81,7 +81,7 @@ function setup(config) { // Export the Userale API -export var version = userAleVersion; +export const version = userAleVersion; /** * Used to start the logging process if the diff --git a/src/packageLogs.js b/src/packageLogs.js index cb7e3a26..59a7e40b 100644 --- a/src/packageLogs.js +++ b/src/packageLogs.js @@ -16,21 +16,21 @@ */ import { detect } from 'detect-browser'; -var browser = detect(); +const browser = detect(); -export var logs; -var config; +export let logs; +let config; // Interval Logging Globals -var intervalID; -var intervalType; -var intervalPath; -var intervalTimer; -var intervalCounter; -var intervalLog; +let intervalID; +let intervalType; +let intervalPath; +let intervalTimer; +let intervalCounter; +let intervalLog; -export var filterHandler = null; -export var mapHandler = null; +export let filterHandler = null; +export let mapHandler = null; /** * Assigns a handler to filter logs out of the queue. @@ -78,16 +78,16 @@ export function packageLog(e, detailFcn) { return false; } - var details = null; + let details = null; if (detailFcn) { details = detailFcn(e); } - var timeFields = extractTimeFields( + const timeFields = extractTimeFields( (e.timeStamp && e.timeStamp > 0) ? config.time(e.timeStamp) : Date.now() ); - var log = { + let log = { 'target' : getSelector(e.target), 'path' : buildPath(e), 'pageUrl': window.location.href, @@ -134,12 +134,12 @@ export function packageCustomLog(customLog, detailFcn, userAction) { return false; } - var details = null; + let details = null; if (detailFcn) { details = detailFcn(); } - var metaData = { + const metaData = { 'pageUrl': window.location.href, 'pageTitle': document.title, 'pageReferrer': document.referrer, @@ -156,7 +156,7 @@ export function packageCustomLog(customLog, detailFcn, userAction) { 'sessionID': config.sessionID }; - var log = Object.assign(metaData, customLog); + let log = Object.assign(metaData, customLog); if ((typeof filterHandler === 'function') && !filterHandler(log)) { return false; @@ -190,10 +190,10 @@ export function extractTimeFields(timeStamp) { * @return boolean */ export function packageIntervalLog(e) { - var target = getSelector(e.target); - var path = buildPath(e); - var type = e.type; - var timestamp = Math.floor((e.timeStamp && e.timeStamp > 0) ? config.time(e.timeStamp) : Date.now()); + const target = getSelector(e.target); + const path = buildPath(e); + const type = e.type; + const timestamp = Math.floor((e.timeStamp && e.timeStamp > 0) ? config.time(e.timeStamp) : Date.now()); // Init - this should only happen once on initialization if (intervalID == null) { @@ -304,11 +304,11 @@ export function getSelector(ele) { * @return {HTMLElement[]} Array of elements, starting at the event target, ending at the root element. */ export function buildPath(e) { - var path = []; + let path = []; if (e.path) { path = e.path; } else { - var ele = e.target + let ele = e.target while(ele) { path.push(ele); ele = ele.parentElement; @@ -324,9 +324,9 @@ export function buildPath(e) { * @return {string[]} Array of string CSS selectors. */ export function selectorizePath(path) { - var i = 0; - var pathEle; - var pathSelectors = []; + let i = 0; + let pathEle; + const pathSelectors = []; while (pathEle = path[i]) { pathSelectors.push(getSelector(pathEle)); ++i; diff --git a/src/sendLogs.js b/src/sendLogs.js index da431807..9d847062 100644 --- a/src/sendLogs.js +++ b/src/sendLogs.js @@ -15,7 +15,7 @@ * limitations under the License. */ -var sendIntervalId = null; +let sendIntervalId = null; /** * Initializes the log queue processors. @@ -102,10 +102,10 @@ export function sendOnClose(logs, config) { // @todo expose config object to sendLogs replate url with config.url export function sendLogs(logs, config, retries) { - var req = new XMLHttpRequest(); + const req = new XMLHttpRequest(); // @todo setRequestHeader for Auth - var data = JSON.stringify(logs); + const data = JSON.stringify(logs); req.open('POST', config.url); if (config.authHeader) {