You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flagon.apache.org by po...@apache.org on 2019/09/27 06:15:56 UTC

[incubator-flagon-useralejs] branch master updated (f82c6d5 -> fe1b477)

This is an automated email from the ASF dual-hosted git repository.

poorejc pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-flagon-useralejs.git.


    from f82c6d5  [FLAGON-341] resolves duplicate logging issue in WebExtension
     add 397a22b  [FLAGON-440] created sendOnRefresh function to call via attachEventListeners so data isn't lost for events that trigger refreshes
     add 4e9ccd4  [FLAGON-434/441] Pushing feature that stores sessionId in session storage and retrieves it whenever a page is reloaded
     add e61a99f  [FLAGON-443] added options API example to index.html
     add 8422279  minor package-lock fix due to vulnerability in gulp-eslint v5.0.0
     add 094b872  [FLAGON-451] updated WIP mods to unit testing framework to mock sessionStorage
     add e545d8c  Moved SessionId initialization to a one-time run in getInitialSettings. Changed to camelCase. Removed weird JSON.parse check
     add 0c844d0  Removed storage in favor of having testUtils handle it.
     add 8e67a26  Fixed missing window.sessionStorage. Added VirtualConsole to make future debugging easier
     add d75ebed  [FLAGON-455] Update Index.html to integrate forms example, .options API example
     add a382e91  [FLAGON-434] resolving merge conflicts
     new fe1b477  fixing merge conflicts

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 build/UserAleWebExtension/background.js |  769 +++++++--------
 build/UserAleWebExtension/content.js    | 1567 ++++++++++++++++---------------
 build/UserAleWebExtension/options.js    |   38 +-
 build/userale-2.0.2.js                  |  266 +++---
 build/userale-2.0.2.min.js              |    2 +-
 example/index.html                      |   16 +-
 package-lock.json                       |   48 +-
 package.json                            |    1 +
 src/attachHandlers.js                   |   20 +-
 src/getInitialSettings.js               |   28 +-
 src/sendLogs.js                         |   18 +
 test/getInitialSettings_spec.js         |    1 +
 test/testUtils.js                       |   23 +-
 13 files changed, 1507 insertions(+), 1290 deletions(-)


[incubator-flagon-useralejs] 01/01: fixing merge conflicts

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

poorejc pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-flagon-useralejs.git

commit fe1b477fd1da22ae36e45d22f9b8f3236408bfeb
Merge: f82c6d5 a382e91
Author: poorejc <po...@apache.org>
AuthorDate: Fri Sep 27 02:15:14 2019 -0400

    fixing merge conflicts

 build/UserAleWebExtension/background.js |  769 +++++++--------
 build/UserAleWebExtension/content.js    | 1567 ++++++++++++++++---------------
 build/UserAleWebExtension/options.js    |   38 +-
 build/userale-2.0.2.js                  |  266 +++---
 build/userale-2.0.2.min.js              |    2 +-
 example/index.html                      |   16 +-
 package-lock.json                       |   48 +-
 package.json                            |    1 +
 src/attachHandlers.js                   |   20 +-
 src/getInitialSettings.js               |   28 +-
 src/sendLogs.js                         |   18 +
 test/getInitialSettings_spec.js         |    1 +
 test/testUtils.js                       |   23 +-
 13 files changed, 1507 insertions(+), 1290 deletions(-)

diff --cc build/UserAleWebExtension/content.js
index ad46097,ae2be33..345486f
--- a/build/UserAleWebExtension/content.js
+++ b/build/UserAleWebExtension/content.js
@@@ -50,760 -50,812 +50,839 @@@ var ADD_LOG = prefix + 'ADD_LOG'
  
  var version = "2.0.2";
  
- /*
-  * 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.
-  */
- 
- /**
-  * Extracts the initial configuration settings from the
-  * currently executing script tag.
-  * @return {Object} The extracted configuration object
-  */
- function getInitialSettings() {
-   var settings = {};
- 
-   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') || 'session_' + String(Date.now());
- 
-   return settings;
- }
- 
- /**
-  * 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) {
-   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;
-       };
-     } else {
-       tsScaler = function (ts) {
-         return ts;
-       };
-     }
-   } else {
-     tsScaler = function () { return Date.now(); };
-   }
- 
-   return tsScaler;
- }
- 
- /*
-  * 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.
-  */
- 
- /**
-  * Shallow merges the first argument with the second.
-  * Retrieves/updates the userid if userFromParams is provided.
-  * @param  {Object} config    Current configuration object to be merged into.
-  * @param  {Object} newConfig Configuration object to merge into the current config.
-  */
- function configure(config, newConfig) {
-   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 + '(=([^&#]*)|&|#|$)');
-   var results = window.location.href.match(regex);
- 
-   if (results && results[2]) {
-     return decodeURIComponent(results[2].replace(/\+/g, ' '));
-   } else {
-     return null;
-   }
- }
- 
- /*
-  * 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 logs;
- var config;
- 
- // 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 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 = newLogs;
-   config = newConfig;
-   filterHandler = null;
-   mapHandler = null;
-   intervalID = null;
-   intervalType = null;
-   intervalPath = null;
-   intervalTimer = null;
-   intervalCounter = 0;
-   intervalLog = null;
- }
- 
- /**
-  * Transforms the provided event into a log and appends it to the log container.
-  * @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.on) {
-     return false;
-   }
- 
-   var details = null;
-   if (detailFcn) {
-     details = detailFcn(e);
-   }
- 
-   var timeFields = extractTimeFields(
-     (e.timeStamp && e.timeStamp > 0) ? config.time(e.timeStamp) : Date.now()
-   );
- 
-   var log = {
-     'target' : getSelector(e.target),
-     'path' : buildPath(e),
-     'pageUrl': window.location.href,
-     'pageTitle': document.title,
-     'pageReferrer': document.referrer,
-     'clientTime' : timeFields.milli,
-     'microTime' : timeFields.micro,
-     'location' : getLocation(e),
-     'type' : e.type,
-     'logType': 'raw',
-     'userAction' : true,
-     'details' : details,
-     'userId' : config.userId,
-     'toolVersion' : config.version,
-     'toolName' : config.toolName,
-     'useraleVersion': config.useraleVersion,
-     'sessionID': config.sessionID
-   };
- 
-   if ((typeof filterHandler === 'function') && !filterHandler(log)) {
-     return false;
-   }
- 
-   if (typeof mapHandler === 'function') {
-     log = mapHandler(log);
-   }
- 
-   logs.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)),
-   };
- }
- 
- /**
-  * 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.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,
-             'count': intervalCounter,
-             'duration': timestamp - intervalTimer,  // microseconds
-             'startTime': intervalTimer,
-             'endTime': timestamp,
-             'type': intervalType,
-             'logType': 'interval',    
-             'targetChange': intervalID !== target,
-             'typeChange': intervalType !== type,
-             'userAction': false,
-             'userId': config.userId,
-             'toolVersion': config.version,
-             'toolName': config.toolName,
-             'useraleVersion': config.useraleVersion,
-             'sessionID': config.sessionID
-         };
- 
-         if (typeof filterHandler === 'function' && !filterHandler(intervalLog)) {
-           return false;
-         }
- 
-         if (typeof mapHandler === 'function') {
-           intervalLog = mapHandler(intervalLog);
-         }
- 
-         logs.push(intervalLog);
- 
-         // Reset
-         intervalID = target;
-         intervalType = type;
-         intervalPath = path;
-         intervalTimer = timestamp;
-         intervalCounter = 0;
-     }
- 
-     // Interval is still occuring, just update counter
-     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 };
-   } else if (e.clientX != null) {
-     return { 'x' : document.documentElement.scrollLeft + e.clientX, 'y' : document.documentElement.scrollTop + e.clientY };
-   } else {
-     return { 'x' : null, 'y' : null };
-   }
- }
- 
- /**
-  * 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) : '');
-   } else if (ele.nodeName) {
-     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) {
-       path.push(ele);
-       ele = ele.parentElement;
-     }
-   }
- 
-   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;
- }
- 
- /*
-  * 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 events;
- var bufferBools;
- var bufferedEvents;
- //@todo: Investigate drag events and their behavior
- var intervalEvents = ['click', 'focus', 'blur', 'input', 'change', 'mouseover', 'submit'];
- 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
-   };
- }
- 
- /**
-  * 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,
-     'submit' : 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 }; }
-   };
- }
- 
- /**
-  * 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) {
-       packageLog(e, events[ev]);
-     }, true);
-   });
- 
-   intervalEvents.forEach(function(ev) {
-     document.addEventListener(ev, function(e) {
-         packageIntervalLog(e);
-     }, true);
-   });
- 
-   Object.keys(bufferedEvents).forEach(function(ev) {
-     bufferBools[ev] = true;
- 
-     window.addEventListener(ev, function(e) {
-       if (bufferBools[ev]) {
-         bufferBools[ev] = false;
-         packageLog(e, bufferedEvents[ev]);
-         setTimeout(function() { bufferBools[ev] = true; }, config.resolution);
-       }
-     }, 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 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);
-   }
- 
-   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.
-  * @param  {Array} logs   Array of logs to read from.
-  * @param  {Object} config Configuration object to be read from.
-  * @return {Number}        The newly created interval id.
-  */
- function sendOnInterval(logs, config) {
-   return setInterval(function() {
-     if (!config.on) {
-       return;
-     }
- 
-     if (logs.length >= config.logCountThreshold) {
-       sendLogs(logs.slice(0), config.url, 0); // Send a copy
-       logs.splice(0); // Clear array reference (no reassignment)
-     }
-   }, config.transmitInterval);
- }
- 
- /**
-  * 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() {
-       navigator.sendBeacon(config.url, JSON.stringify(logs));
-     });
-   } else {
-     window.addEventListener('beforeunload', function() {
-       if (logs.length > 0) {
-         sendLogs(logs, config.url, 1);
-       }
-     });
-   }
- }
- 
- /**
-  * Sends the provided array of logs to the specified url,
-  * retrying the request up to the specified number of retries.
-  * @param  {Array} logs    Array of logs to send.
-  * @param  {string} url     URL to send the POST request to.
-  * @param  {Number} retries Maximum number of attempts to send the logs.
-  */
- function sendLogs(logs, url, retries) {
-   var req = new XMLHttpRequest();
- 
-   var data = JSON.stringify(logs);
- 
-   req.open('POST', url);
-   req.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
- 
-   req.onreadystatechange = function() {
-     if (req.readyState === 4 && req.status !== 200) {
-       if (retries > 0) {
-         sendLogs(logs, url, retries--);
-       }
-     }
-   };
- 
-   req.send(data);
- }
- 
- /*
-  * 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$1 = {};
- var logs$1 = [];
- var started = false;
- 
- // Start up Userale
- config$1.on = false;
- config$1.useraleVersion = version;
- 
- configure(config$1, getInitialSettings());
- initPackager(logs$1, config$1);
- 
- if (config$1.autostart) {
-   setup(config$1);
- }
- 
- /**
-  * Hooks the global event listener, and starts up the
-  * logging interval.
-  * @param  {Object} config Configuration settings for the logger
-  */
- function setup(config) {
-   if (!started) {
-     setTimeout(function() {
-       var state = document.readyState;
- 
-       if (state === 'interactive' || state === 'complete') {
-         attachHandlers(config);
-         initSender(logs$1, config);
-         started = config.on = true;
-       } else {
-         setup(config);
-       }
-     }, 100);
-   }
+ /*
+  * 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 sessionId = null;
+ 
+ /**
+  * Extracts the initial configuration settings from the
+  * currently executing script tag.
+  * @return {Object} The extracted configuration object
+  */
+ function getInitialSettings() {
+   var settings = {};
+ 
+   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];
+   })();
+ 
+   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;
+ 
+   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){
+   if (window.sessionStorage.getItem(sessionKey) === null) {
+     window.sessionStorage.setItem(sessionKey, JSON.stringify(value));
+     return JSON.stringify(value);
+   }
+ 
+   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) {
+   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;
+       };
+     } else {
+       tsScaler = function (ts) {
+         return ts;
+       };
+     }
+   } else {
+     tsScaler = function () { return Date.now(); };
+   }
+ 
+   return tsScaler;
  }
  
++<<<<<<< HEAD
 +/**
 + * 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$1, newConfig);
 +  }
 +
 +  return config$1;
++=======
+ /*
+  * 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.
+  */
+ 
+ /**
+  * Shallow merges the first argument with the second.
+  * Retrieves/updates the userid if userFromParams is provided.
+  * @param  {Object} config    Current configuration object to be merged into.
+  * @param  {Object} newConfig Configuration object to merge into the current config.
+  */
+ function configure(config, newConfig) {
+   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 + '(=([^&#]*)|&|#|$)');
+   var results = window.location.href.match(regex);
+ 
+   if (results && results[2]) {
+     return decodeURIComponent(results[2].replace(/\+/g, ' '));
+   } else {
+     return null;
+   }
  }
  
- /*
-  * 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.
-  */
- 
- // browser is defined in firefox, but not in chrome. In chrome, they use
- // the 'chrome' global instead. Let's map it to browser so we don't have
- // to have if-conditions all over the place.
- 
- var browser = browser || chrome;
+ /*
+  * 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 logs;
+ var config;
+ 
+ // 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 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 = newLogs;
+   config = newConfig;
+   filterHandler = null;
+   mapHandler = null;
+   intervalID = null;
+   intervalType = null;
+   intervalPath = null;
+   intervalTimer = null;
+   intervalCounter = 0;
+   intervalLog = null;
+ }
+ 
+ /**
+  * Transforms the provided event into a log and appends it to the log container.
+  * @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.on) {
+     return false;
+   }
+ 
+   var details = null;
+   if (detailFcn) {
+     details = detailFcn(e);
+   }
+ 
+   var timeFields = extractTimeFields(
+     (e.timeStamp && e.timeStamp > 0) ? config.time(e.timeStamp) : Date.now()
+   );
+ 
+   var log = {
+     'target' : getSelector(e.target),
+     'path' : buildPath(e),
+     'pageUrl': window.location.href,
+     'pageTitle': document.title,
+     'pageReferrer': document.referrer,
+     'clientTime' : timeFields.milli,
+     'microTime' : timeFields.micro,
+     'location' : getLocation(e),
+     'type' : e.type,
+     'logType': 'raw',
+     'userAction' : true,
+     'details' : details,
+     'userId' : config.userId,
+     'toolVersion' : config.version,
+     'toolName' : config.toolName,
+     'useraleVersion': config.useraleVersion,
+     'sessionID': config.sessionID
+   };
+ 
+   if ((typeof filterHandler === 'function') && !filterHandler(log)) {
+     return false;
+   }
+ 
+   if (typeof mapHandler === 'function') {
+     log = mapHandler(log);
+   }
+ 
+   logs.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)),
+   };
+ }
+ 
+ /**
+  * 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.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,
+             'count': intervalCounter,
+             'duration': timestamp - intervalTimer,  // microseconds
+             'startTime': intervalTimer,
+             'endTime': timestamp,
+             'type': intervalType,
+             'logType': 'interval',    
+             'targetChange': intervalID !== target,
+             'typeChange': intervalType !== type,
+             'userAction': false,
+             'userId': config.userId,
+             'toolVersion': config.version,
+             'toolName': config.toolName,
+             'useraleVersion': config.useraleVersion,
+             'sessionID': config.sessionID
+         };
+ 
+         if (typeof filterHandler === 'function' && !filterHandler(intervalLog)) {
+           return false;
+         }
+ 
+         if (typeof mapHandler === 'function') {
+           intervalLog = mapHandler(intervalLog);
+         }
+ 
+         logs.push(intervalLog);
+ 
+         // Reset
+         intervalID = target;
+         intervalType = type;
+         intervalPath = path;
+         intervalTimer = timestamp;
+         intervalCounter = 0;
+     }
+ 
+     // Interval is still occuring, just update counter
+     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 };
+   } else if (e.clientX != null) {
+     return { 'x' : document.documentElement.scrollLeft + e.clientX, 'y' : document.documentElement.scrollTop + e.clientY };
+   } else {
+     return { 'x' : null, 'y' : null };
+   }
+ }
+ 
+ /**
+  * 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) : '');
+   } else if (ele.nodeName) {
+     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) {
+       path.push(ele);
+       ele = ele.parentElement;
+     }
+   }
+ 
+   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;
++>>>>>>> FLAGON-434
+ }
  
- // creates a Future for retrieval of the named keys
- // the value specified is the default value if one doesn't exist in the storage
- let store = browser.storage.local.get({
-   sessionId: null,
-   userAleHost: userAleHost,
-   userAleScript: userAleScript,
-   toolUser: toolUser,
-   toolName: toolName,
-   toolVersion: toolVersion,
- }, storeCallback);
-         
- function storeCallback(item) {
-   injectScript({
-     url: item.userAleHost,
-     userId: item.toolUser,
-     sessionID: item.sessionId,
-     toolName: item.toolName,
-     toolVersion: item.toolVersion
-   });
+ /*
+  * 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 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);
+   }
+ 
+   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.
+  * @param  {Array} logs   Array of logs to read from.
+  * @param  {Object} config Configuration object to be read from.
+  * @return {Number}        The newly created interval id.
+  */
+ function sendOnInterval(logs, config) {
+   return setInterval(function() {
+     if (!config.on) {
+       return;
+     }
+ 
+     if (logs.length >= config.logCountThreshold) {
+       sendLogs(logs.slice(0), config.url, 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
+  * is different than sendOnClose because browser security practices prevent you from
+  * listening the process responsible for window navigation actions, in action (e.g., refresh;
+  * you can only detect, after the fact, the process responsible for the current window state.
+  * @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.url, 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() {
+       navigator.sendBeacon(config.url, JSON.stringify(logs));
+     });
+   } else {
+     window.addEventListener('beforeunload', function() {
+       if (logs.length > 0) {
+         sendLogs(logs, config.url, 1);
+       }
+     });
+   }
+ }
+ 
+ /**
+  * Sends the provided array of logs to the specified url,
+  * retrying the request up to the specified number of retries.
+  * @param  {Array} logs    Array of logs to send.
+  * @param  {string} url     URL to send the POST request to.
+  * @param  {Number} retries Maximum number of attempts to send the logs.
+  */
+ function sendLogs(logs, url, retries) {
+   var req = new XMLHttpRequest();
+ 
+   var data = JSON.stringify(logs);
+ 
+   req.open('POST', url);
+   req.setRequestHeader('Content-type', 'application/json;charset=UTF-8');
+ 
+   req.onreadystatechange = function() {
+     if (req.readyState === 4 && req.status !== 200) {
+       if (retries > 0) {
+         sendLogs(logs, url, retries--);
+       }
+     }
+   };
+ 
+   req.send(data);
  }
  
- function queueLog(log) {
-   browser.runtime.sendMessage({ type: ADD_LOG, payload: log });
+ /*
+  * 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 events;
+ var bufferBools;
+ 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
+   };
+ }
+ 
+ /**
+  * 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
+   };
+ 
+   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 }; }
+   };
+ 
+   refreshEvents = {
+     'submit' : null
+   };
+ }
+ 
+ /**
+  * 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) {
+       packageLog(e, events[ev]);
+     }, true);
+   });
+ 
+   intervalEvents.forEach(function(ev) {
+     document.addEventListener(ev, function(e) {
+         packageIntervalLog(e);
+     }, true);
+   });
+ 
+   Object.keys(bufferedEvents).forEach(function(ev) {
+     bufferBools[ev] = true;
+ 
+     window.addEventListener(ev, function(e) {
+       if (bufferBools[ev]) {
+         bufferBools[ev] = false;
+         packageLog(e, bufferedEvents[ev]);
+         setTimeout(function() { bufferBools[ev] = true; }, config.resolution);
+       }
+     }, true);
+   });
+ 
+   Object.keys(refreshEvents).forEach(function(ev) {
+     document.addEventListener(ev, function(e) {
+       packageLog(e, events[ev]);
+       sendOnRefresh(logs,config);
+     }, true);
+   });
+ 
+   windowEvents.forEach(function(ev) {
+     window.addEventListener(ev, function(e) {
+       packageLog(e, function() { return { 'window' : true }; });
+     }, true);
+   });
+ 
+   return true;
  }
  
++<<<<<<< HEAD
 +function injectScript(config) {
 +  options(config);
 +//  start();  not necessary given that autostart in place, and option is masked from WebExt users
 +  setLogFilter(function (log) {
 +    queueLog(Object.assign({}, log, {
 +      pageUrl: document.location.href,
 +    }));
 +    return false;
 +  });
++=======
+ /*
+  * 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$1 = {};
+ var logs$1 = [];
+ var started = false;
+ 
+ // Start up Userale
+ config$1.on = false;
+ config$1.useraleVersion = version;
+ 
+ configure(config$1, getInitialSettings());
+ initPackager(logs$1, config$1);
+ 
+ if (config$1.autostart) {
+   setup(config$1);
+ }
+ 
+ /**
+  * Hooks the global event listener, and starts up the
+  * logging interval.
+  * @param  {Object} config Configuration settings for the logger
+  */
+ function setup(config) {
+   if (!started) {
+     setTimeout(function() {
+       var state = document.readyState;
+ 
+       if (state === 'interactive' || state === 'complete') {
+         attachHandlers(config);
+         initSender(logs$1, config);
+         started = config.on = true;
+       } else {
+         setup(config);
+       }
+     }, 100);
+   }
+ }
+ 
+ /**
+  * 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$1, newConfig);
+   }
+ 
+   return config$1;
++>>>>>>> FLAGON-434
  }
  
- browser.runtime.onMessage.addListener(function (message) {
-   if (message.type === CONFIG_CHANGE) {
-     options({
-       url: message.payload.userAleHost,
-       userId: message.payload.toolUser,
-       toolName: message.payload.toolName,
-       toolVersion: message.payload.toolVersion
-     });
-   }
- });
- 
- /*
-  eslint-enable
+ /*
+  * 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.
+  */
+ 
+ // browser is defined in firefox, but not in chrome. In chrome, they use
+ // the 'chrome' global instead. Let's map it to browser so we don't have
+ // to have if-conditions all over the place.
+ 
+ var browser = browser || chrome;
+ 
+ // creates a Future for retrieval of the named keys
+ // the value specified is the default value if one doesn't exist in the storage
+ let store = browser.storage.local.get({
+   sessionId: null,
+   userAleHost: userAleHost,
+   userAleScript: userAleScript,
+   toolUser: toolUser,
+   toolName: toolName,
+   toolVersion: toolVersion,
+ }, storeCallback);
+         
+ function storeCallback(item) {
+   injectScript({
+     url: item.userAleHost,
+     userId: item.toolUser,
+     sessionID: item.sessionId,
+     toolName: item.toolName,
+     toolVersion: item.toolVersion
+   });
+ }
+ 
+ function queueLog(log) {
+   browser.runtime.sendMessage({ type: ADD_LOG, payload: log });
+ }
+ 
+ function injectScript(config) {
+   options(config);
+ //  start();  not necessary given that autostart in place, and option is masked from WebExt users
+   setLogFilter(function (log) {
+     queueLog(Object.assign({}, log, {
+       pageUrl: document.location.href,
+     }));
+     return false;
+   });
+ }
+ 
+ browser.runtime.onMessage.addListener(function (message) {
+   if (message.type === CONFIG_CHANGE) {
+     options({
+       url: message.payload.userAleHost,
+       userId: message.payload.toolUser,
+       toolName: message.payload.toolName,
+       toolVersion: message.payload.toolVersion
+     });
+   }
+ });
+ 
+ /*
+  eslint-enable
   */
diff --cc example/index.html
index 2aa7cf7,8c8cde3..b5c90a4
--- a/example/index.html
+++ b/example/index.html
@@@ -18,19 -18,30 +18,29 @@@ limitations under the License
  <head>
    <title>UserAleJS - Example Page</title>
    <script
--    src="file:///{your file path}/incubator-flagon-useralejs/build/userale-2.0.2.min.js"
++    src="file:///Users/jpoore/Documents/Apache_Flagon/test/incubator-flagon-useralejs/build/userale-2.0.2.min.js"
      data-url="http://localhost:8000/"
      data-user="example-user"
 -    data-log-details="true"
      data-version="2.0.2"
      data-tool="Apache UserALE.js Example"
    ></script>
 -<!--Try out the options function to change UserALE.js params!
++<!--Try out the options function to change UserALE.js params@
+   <script type="text/javascript">
+       const changeMe = "me";
+       window.userale.options({
+         "userId": changeMe,
+         "version": "4.2.0",
+         "sessionID": "42"
+       })
+   </script>
+ -->
 -<!--Try out a UserALE.js filter to eliminate the logs that you don't want!
 -  <script type="text/javascript">
 -  window.userale.filter(function (log) {
 -    var type_array = ['mouseup', 'mouseover', 'dblclick', 'blur', 'focus'];
 -    var logType_array = ['interval'];
 -    return !type_array.includes(log.type) && !logType_array.includes(log.logType);
 -  });
 +<!--Try out a UserALE.js filter!
 +    <script type="text/javascript">
 +    window.userale.filter(function (log) {
-       var type_array = ['mouseup', 'mouseover', 'dblclick', 'blur', 'focus']
-       var logType_array = ['interval']
++      var type_array = ['mouseup', 'mouseover', 'dblclick', 'blur', 'focus'];
++      var logType_array = ['interval'];
 +      return !type_array.includes(log.type) && !logType_array.includes(log.logType);
 +    });
    </script>
  -->
  </head>