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/11/03 05:27:15 UTC
[incubator-flagon-useralejs] 01/01: [FLAGON-468] added packageLogs
and supporting functions to exported functions via API
This is an automated email from the ASF dual-hosted git repository.
poorejc pushed a commit to branch FLAGON-469
in repository https://gitbox.apache.org/repos/asf/incubator-flagon-useralejs.git
commit 8ecfc002d955afa4c05ab14369678fdad2678e24
Author: poorejc <po...@apache.org>
AuthorDate: Sun Nov 3 01:26:52 2019 -0400
[FLAGON-468] added packageLogs and supporting functions to exported functions via API
---
build/UserAleWebExtension/background.js | 770 +++++++--------
build/UserAleWebExtension/content.js | 1633 ++++++++++++++++---------------
build/UserAleWebExtension/options.js | 38 +-
build/userale-2.0.2.js | 5 +
build/userale-2.0.2.min.js | 2 +-
src/main.js | 13 +-
6 files changed, 1239 insertions(+), 1222 deletions(-)
diff --git a/build/UserAleWebExtension/background.js b/build/UserAleWebExtension/background.js
index f8d7bcb..ddd48e8 100644
--- a/build/UserAleWebExtension/background.js
+++ b/build/UserAleWebExtension/background.js
@@ -26,397 +26,397 @@ var toolVersion = '2.0.2';
/* 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.
-*/
-
-var prefix = 'USERALE_';
-
-var CONFIG_CHANGE = prefix + 'CONFIG_CHANGE';
+/*
+* 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 prefix = 'USERALE_';
+
+var CONFIG_CHANGE = prefix + 'CONFIG_CHANGE';
var ADD_LOG = prefix + 'ADD_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.
- */
-
-
-/**
- * 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.
+ */
+
+
+/**
+ * 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.
- */
-
-/**
- * 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)),
- };
+/*
+ * 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.
+ */
+
+/**
+ * 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)),
+ };
}
-/*
- * 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 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);
}
-/*
- * 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.
- */
-
-// inherent dependency on globals.js, loaded by the webext
-
-// 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;
-var logs = [];
-var config = {
- autostart: true,
- url: 'http://localhost:8000',
- transmitInterval: 5000,
- logCountThreshold: 5,
- userId: null,
- version: null,
- resolution: 500,
- time: timeStampScale({}),
- on: true,
-};
-var sessionId = 'session_' + Date.now();
-
-var getTimestamp = ((typeof performance !== 'undefined') && (typeof performance.now !== 'undefined'))
- ? function () { return performance.now() + performance.timing.navigationStart; }
- : Date.now;
-
-browser.storage.local.set({ sessionId: sessionId });
-
-var store = browser.storage.local.get({
- userAleHost: userAleHost,
- userAleScript: userAleScript,
- toolUser: toolUser,
- toolName: toolName,
- toolVersion: toolVersion,
-}, storeCallback);
-
-function storeCallback(item) {
- config = Object.assign({}, config, {
- url: item.userAleHost,
- userId: item.toolUser,
- sessionID: sessionId,
- toolName: item.toolName,
- toolVersion: item.toolVersion
- });
- initSender(logs, config);
-}
-
-function dispatchTabMessage(message) {
- browser.tabs.query({}, function (tabs) {
- tabs.forEach(function (tab) {
- browser.tabs.sendMessage(tab.id, message);
- });
- });
-}
-
-function packageBrowserLog(type, logDetail) {
- var timeFields = extractTimeFields(getTimestamp());
-
- logs.push({
- 'target' : null,
- 'path' : null,
- 'clientTime' : timeFields.milli,
- 'microTime' : timeFields.micro,
- 'location' : null,
- 'type' : 'browser.' + type,
- 'logType': 'raw',
- 'userAction' : true,
- 'details' : logDetail,
- 'userId' : toolUser,
- 'toolVersion': null,
- 'toolName': null,
- 'useraleVersion': null,
- 'sessionID': sessionId,
- });
-}
-
-browser.runtime.onMessage.addListener(function (message) {
- switch (message.type) {
- case CONFIG_CHANGE:
- (function () {
- var updatedConfig = Object.assign({}, config, {
- url: message.payload.userAleHost,
- userId: message.payload.toolUser,
- toolName: message.payload.toolName,
- toolVersion: message.payload.toolVersion
- });
- initSender(logs, updatedConfig);
- dispatchTabMessage(message);
- })();
- break;
-
- case ADD_LOG:
- (function () {
- logs.push(message.payload);
- })();
- break;
-
- default:
- console.log('got unknown message type ', message);
- }
-});
-
-function getTabDetailById(tabId, onReady) {
- browser.tabs.get(tabId, function (tab) {
- onReady({
- active: tab.active,
- audible: tab.audible,
- incognito: tab.incognito,
- index: tab.index,
- muted: tab.mutedInfo ? tab.mutedInfo.muted : null,
- pinned: tab.pinned,
- selected: tab.selected,
- tabId: tab.id,
- title: tab.title,
- url: tab.url,
- windowId: tab.windowId,
- });
- });
-}
-
-browser.tabs.onActivated.addListener(function (e) {
- getTabDetailById(e.tabId, function (detail) {
- packageBrowserLog('tabs.onActivated', detail);
- });
-});
-
-browser.tabs.onCreated.addListener(function (tab, e) {
- packageBrowserLog('tabs.onCreated', {
- active: tab.active,
- audible: tab.audible,
- incognito: tab.incognito,
- index: tab.index,
- muted: tab.mutedInfo ? tab.mutedInfo.muted : null,
- pinned: tab.pinned,
- selected: tab.selected,
- tabId: tab.id,
- title: tab.title,
- url: tab.url,
- windowId: tab.windowId,
- });
-});
-
-browser.tabs.onDetached.addListener(function (tabId) {
- getTabDetailById(tabId, function (detail) {
- packageBrowserLog('tabs.onDetached', detail);
- });
-});
-
-browser.tabs.onMoved.addListener(function (tabId) {
- getTabDetailById(tabId, function (detail) {
- packageBrowserLog('tabs.onMoved', detail);
- });
-});
-
-browser.tabs.onRemoved.addListener(function (tabId) {
- packageBrowserLog('tabs.onRemoved', { tabId: tabId });
-});
-
-browser.tabs.onZoomChange.addListener(function (e) {
- getTabDetailById(e.tabId, function (detail) {
- packageBrowserLog('tabs.onZoomChange', Object.assign({}, {
- oldZoomFactor: e.oldZoomFactor,
- newZoomFactor: e.newZoomFactor,
- }, detail));
- });
-});
-
-/*
- eslint-enable
+/**
+ * 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.
+ */
+
+// inherent dependency on globals.js, loaded by the webext
+
+// 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;
+var logs = [];
+var config = {
+ autostart: true,
+ url: 'http://localhost:8000',
+ transmitInterval: 5000,
+ logCountThreshold: 5,
+ userId: null,
+ version: null,
+ resolution: 500,
+ time: timeStampScale({}),
+ on: true,
+};
+var sessionId = 'session_' + Date.now();
+
+var getTimestamp = ((typeof performance !== 'undefined') && (typeof performance.now !== 'undefined'))
+ ? function () { return performance.now() + performance.timing.navigationStart; }
+ : Date.now;
+
+browser.storage.local.set({ sessionId: sessionId });
+
+var store = browser.storage.local.get({
+ userAleHost: userAleHost,
+ userAleScript: userAleScript,
+ toolUser: toolUser,
+ toolName: toolName,
+ toolVersion: toolVersion,
+}, storeCallback);
+
+function storeCallback(item) {
+ config = Object.assign({}, config, {
+ url: item.userAleHost,
+ userId: item.toolUser,
+ sessionID: sessionId,
+ toolName: item.toolName,
+ toolVersion: item.toolVersion
+ });
+ initSender(logs, config);
+}
+
+function dispatchTabMessage(message) {
+ browser.tabs.query({}, function (tabs) {
+ tabs.forEach(function (tab) {
+ browser.tabs.sendMessage(tab.id, message);
+ });
+ });
+}
+
+function packageBrowserLog(type, logDetail) {
+ var timeFields = extractTimeFields(getTimestamp());
+
+ logs.push({
+ 'target' : null,
+ 'path' : null,
+ 'clientTime' : timeFields.milli,
+ 'microTime' : timeFields.micro,
+ 'location' : null,
+ 'type' : 'browser.' + type,
+ 'logType': 'raw',
+ 'userAction' : true,
+ 'details' : logDetail,
+ 'userId' : toolUser,
+ 'toolVersion': null,
+ 'toolName': null,
+ 'useraleVersion': null,
+ 'sessionID': sessionId,
+ });
+}
+
+browser.runtime.onMessage.addListener(function (message) {
+ switch (message.type) {
+ case CONFIG_CHANGE:
+ (function () {
+ var updatedConfig = Object.assign({}, config, {
+ url: message.payload.userAleHost,
+ userId: message.payload.toolUser,
+ toolName: message.payload.toolName,
+ toolVersion: message.payload.toolVersion
+ });
+ initSender(logs, updatedConfig);
+ dispatchTabMessage(message);
+ })();
+ break;
+
+ case ADD_LOG:
+ (function () {
+ logs.push(message.payload);
+ })();
+ break;
+
+ default:
+ console.log('got unknown message type ', message);
+ }
+});
+
+function getTabDetailById(tabId, onReady) {
+ browser.tabs.get(tabId, function (tab) {
+ onReady({
+ active: tab.active,
+ audible: tab.audible,
+ incognito: tab.incognito,
+ index: tab.index,
+ muted: tab.mutedInfo ? tab.mutedInfo.muted : null,
+ pinned: tab.pinned,
+ selected: tab.selected,
+ tabId: tab.id,
+ title: tab.title,
+ url: tab.url,
+ windowId: tab.windowId,
+ });
+ });
+}
+
+browser.tabs.onActivated.addListener(function (e) {
+ getTabDetailById(e.tabId, function (detail) {
+ packageBrowserLog('tabs.onActivated', detail);
+ });
+});
+
+browser.tabs.onCreated.addListener(function (tab, e) {
+ packageBrowserLog('tabs.onCreated', {
+ active: tab.active,
+ audible: tab.audible,
+ incognito: tab.incognito,
+ index: tab.index,
+ muted: tab.mutedInfo ? tab.mutedInfo.muted : null,
+ pinned: tab.pinned,
+ selected: tab.selected,
+ tabId: tab.id,
+ title: tab.title,
+ url: tab.url,
+ windowId: tab.windowId,
+ });
+});
+
+browser.tabs.onDetached.addListener(function (tabId) {
+ getTabDetailById(tabId, function (detail) {
+ packageBrowserLog('tabs.onDetached', detail);
+ });
+});
+
+browser.tabs.onMoved.addListener(function (tabId) {
+ getTabDetailById(tabId, function (detail) {
+ packageBrowserLog('tabs.onMoved', detail);
+ });
+});
+
+browser.tabs.onRemoved.addListener(function (tabId) {
+ packageBrowserLog('tabs.onRemoved', { tabId: tabId });
+});
+
+browser.tabs.onZoomChange.addListener(function (e) {
+ getTabDetailById(e.tabId, function (detail) {
+ packageBrowserLog('tabs.onZoomChange', Object.assign({}, {
+ oldZoomFactor: e.oldZoomFactor,
+ newZoomFactor: e.newZoomFactor,
+ }, detail));
+ });
+});
+
+/*
+ eslint-enable
*/
diff --git a/build/UserAleWebExtension/content.js b/build/UserAleWebExtension/content.js
index ae2be33..1edd011 100644
--- a/build/UserAleWebExtension/content.js
+++ b/build/UserAleWebExtension/content.js
@@ -26,836 +26,837 @@ var toolVersion = '2.0.2';
/* 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.
-*/
-
-var prefix = 'USERALE_';
-
-var CONFIG_CHANGE = prefix + 'CONFIG_CHANGE';
+/*
+* 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 prefix = 'USERALE_';
+
+var CONFIG_CHANGE = prefix + 'CONFIG_CHANGE';
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.
- */
-
- 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;
+/*
+ * 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;
}
-/*
- * 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;
- }
+/**
+ * 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));
}
-/*
- * 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;
+
+/**
+ * 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.
- */
-
-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);
+/*
+ * 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];
+ });
}
-/*
- * 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;
+/**
+ * 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 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;
+/*
+ * 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;
}
-/*
- * 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
+
+/**
+ * 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 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);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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 --git a/build/UserAleWebExtension/options.js b/build/UserAleWebExtension/options.js
index 274d183..1badba4 100644
--- a/build/UserAleWebExtension/options.js
+++ b/build/UserAleWebExtension/options.js
@@ -26,25 +26,25 @@ var toolVersion = '2.0.2';
/* 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.
-*/
-
-var prefix = 'USERALE_';
-
+/*
+* 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 prefix = 'USERALE_';
+
var CONFIG_CHANGE = prefix + 'CONFIG_CHANGE';
/*
diff --git a/build/userale-2.0.2.js b/build/userale-2.0.2.js
index 44c4c39..aa1e182 100644
--- a/build/userale-2.0.2.js
+++ b/build/userale-2.0.2.js
@@ -719,6 +719,7 @@ var userale = (function (exports) {
var logs$1 = [];
exports.started = false;
+
// Start up Userale
config$1.on = false;
config$1.useraleVersion = version;
@@ -802,10 +803,14 @@ var userale = (function (exports) {
}
}
+ exports.buildPath = buildPath;
+ exports.details = defineDetails;
exports.filter = setLogFilter;
+ exports.getSelector = getSelector;
exports.log = log;
exports.map = setLogMapper;
exports.options = options;
+ exports.packageLog = packageLog;
exports.start = start;
exports.stop = stop;
exports.version = version$1;
diff --git a/build/userale-2.0.2.min.js b/build/userale-2.0.2.min.js
index 6254ad4..8578afd 100644
--- a/build/userale-2.0.2.min.js
+++ b/build/userale-2.0.2.min.js
@@ -15,4 +15,4 @@
* limitations under the License.
* @preserved
*/
-var userale=function(n){"use strict";var a,i,u,l,s,c,d,f,t="2.0.2",r=null;function e(n,o){Object.keys(o).forEach(function(t){if("userFromParams"===t){var e=function(t){var e=new RegExp("[?&]"+t+"(=([^&#]*)|&|#|$)"),n=window.location.href.match(e);return n&&n[2]?decodeURIComponent(n[2].replace(/\+/g," ")):null}(o[t]);e&&(n.userId=e)}n[t]=o[t]})}var m=null,p=null;function o(t,e){if(!i.on)return!1;var n=null;e&&(n=e(t));var o=function(t){return{milli:Math.floor(t),micro:Number((t%1).toFixed [...]
\ No newline at end of file
+var userale=function(n){"use strict";var a,i,u,l,s,c,d,f,t="2.0.2",r=null;function e(n,o){Object.keys(o).forEach(function(t){if("userFromParams"===t){var e=function(t){var e=new RegExp("[?&]"+t+"(=([^&#]*)|&|#|$)"),n=window.location.href.match(e);return n&&n[2]?decodeURIComponent(n[2].replace(/\+/g," ")):null}(o[t]);e&&(n.userId=e)}n[t]=o[t]})}var m=null,p=null;function o(t,e){if(!i.on)return!1;var n=null;e&&(n=e(t));var o=function(t){return{milli:Math.floor(t),micro:Number((t%1).toFixed [...]
\ No newline at end of file
diff --git a/src/main.js b/src/main.js
index 28fe622..d6cae6c 100644
--- a/src/main.js
+++ b/src/main.js
@@ -25,7 +25,18 @@ import { initSender } from './sendLogs.js';
var config = {};
var logs = [];
export var started = false;
-export { setLogMapper as map, setLogFilter as filter } from './packageLogs.js';
+export {
+ setLogMapper as map,
+ setLogFilter as filter,
+ packageLog as packageLog,
+ getSelector as getSelector,
+ buildPath as buildPath,
+ } from './packageLogs.js';
+
+export {
+ defineDetails as details,
+} from './attachHandlers.js';
+
// Start up Userale
config.on = false;