├── LICENSE
├── README.md
├── userBehaviour.js
└── userBehaviour.min.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Taha Al-Jody
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
User Behaviour Tracking
2 | 5.3 KB
3 |
4 |
5 |
6 | []()
7 | [](https://github.com/kylelobo/The-Documentation-Compendium/issues)
8 | [](https://github.com/kylelobo/The-Documentation-Compendium/pulls)
9 | [](/LICENSE)
10 |
11 |
12 |
13 | ---
14 |
15 | Advanced User Behaviour Tracking Library with 15+ tracking dimensions including media interactions, form tracking, and custom event support.
16 |
17 |
18 | ## 📝 Table of Contents
19 |
20 | - [About](#about)
21 | - [Installation](#install)
22 | - [Configuration](#config)
23 | - [Methods](#methods)
24 | - [Tracking](#tracking)
25 | - [Results](#results)
26 |
27 | ## 🧐 About
28 |
29 | This Javascript Library allows to track user's behaviour by recording mouse activities:
30 |
31 | - Mouse tracking (movement, clicks, scroll)
32 | - Keyboard activity monitoring
33 | - Page navigation history
34 | - Form interaction tracking
35 | - Touch event capture
36 | - Media play events (audio/video)
37 | - Window visibility changes
38 | - Custom event registration
39 | - Device/browser fingerprinting
40 |
41 | ## 🏁 Installation
42 |
43 | There are two ways to include userBehaviour.js to your browser:
44 |
45 | 1. jsDelivr CDN
46 |
47 | ```html
48 |
49 | ```
50 |
51 | 2. Local file
52 |
53 | ```html
54 |
55 | ```
56 |
57 | ## 🔧 Configuration
58 |
59 | The library requires a configuration object. Pass the object to the library with:
60 |
61 | ```javascript
62 | userBehaviour.config({.....});
63 | ```
64 |
65 | If no configuration was passes the libray will use the default configuration:
66 |
67 | ```javascript
68 | {
69 | userInfo: true,
70 | clicks: true,
71 | mouseMovement: true,
72 | mouseMovementInterval: 1,
73 | mouseScroll: true,
74 | timeCount: true,
75 | windowResize: true,
76 | visibilitychange: true,
77 | keyboardActivity: true,
78 | pageNavigation: true,
79 | formInteractions: true,
80 | touchEvents: true,
81 | audioVideoInteraction: true,
82 | clearAfterProcess: true,
83 | processTime: 15,
84 | processData: function(results){
85 | console.log(results);
86 | },
87 | }
88 | ```
89 |
90 | | Config Key | Description | Type | Default |
91 | | ----------------------- | --------------------------------------------------------------- | -------- | ------- |
92 | | userInfo | Record browser/device details | bool | true |
93 | | clicks | Track mouse clicks | bool | true |
94 | | mouseMovement | Track mouse movement | bool | true |
95 | | mouseMovementInterval | Mouse position sampling interval (seconds) | int | 1 |
96 | | mouseScroll | Track page scrolling | bool | true |
97 | | timeCount | Track session timing | bool | true |
98 | | windowResize | Track window size changes | bool | true |
99 | | visibilitychange | Track tab visibility changes | bool | true |
100 | | keyboardActivity | Track keyboard input | bool | true |
101 | | pageNavigation | Track history changes (pushState/popState) | bool | true |
102 | | formInteractions | Track form submissions | bool | true |
103 | | touchEvents | Track touch interactions | bool | true |
104 | | audioVideoInteraction | Track media play events | bool | true |
105 | | customEventRegistration | Enable custom event tracking | bool | true |
106 | | clearAfterProcess | Clear data after processing | bool | true |
107 | | processTime | Automatic processing interval (seconds) - false for manual only | int/bool | 15 |
108 | | processData | Callback function for processed data | function | console |
109 |
110 | ## 📚 Methods
111 |
112 | This is a list of all available methods that can be called:
113 |
114 | | Method | Description | Example |
115 | | --------------------- | ------------------------------- | ------------------------------------------------------ |
116 | | registerCustomEvent() | Register custom event tracking | `userBehaviour.registerCustomEvent('event', callback)` |
117 | | showConfig() | View current configuration | `userBehaviour.showConfig()` |
118 | | config() | Update configuration | `userBehaviour.config({...})` |
119 | | start() | Start tracking | `userBehaviour.start()` |
120 | | stop() | Stop tracking | `userBehaviour.stop()` |
121 | | showResult() | Get current dataset | `userBehaviour.showResult()` |
122 | | processResults() | Force immediate data processing | `userBehaviour.processResults()` |
123 |
124 | ## 🚀 Tracking
125 |
126 | Start tracking with:
127 |
128 | ```javascript
129 | userBehaviour.start();
130 | ```
131 |
132 | Track custom events:
133 |
134 | ```javascript
135 | userBehaviour.registerCustomEvent("surveyCompleted", (e) => {
136 | console.log("Survey completed:", e.detail);
137 | });
138 | ```
139 |
140 | Manual data processing:
141 |
142 | ```javascript
143 | userBehaviour.processResults();
144 | ```
145 |
146 | Stop tracking with:
147 |
148 | ```javascript
149 | userBehaviour.stop();
150 | ```
151 |
152 | ## 🎈 Results
153 |
154 | To view the results at anytime after the tracking has started:
155 |
156 | ```javascript
157 | userBehaviour.showResult();
158 | ```
159 |
160 | The result will be passed to a function set regularly with an interval set in the [configuration](#config) section.
161 |
162 | The data could also be sent via a POST request using any HTTP request libraries e.g axios, ajax, ...
163 |
164 | ```javascript
165 | processData: function(results){
166 | axios.post('https://example.com', results);
167 | }
168 | ```
169 |
170 | If processTime was set to false, data will not be processed automatically. Therefore, you might require to process the data manually with:
171 |
172 | ```javascript
173 | userBehaviour.processResults();
174 | ```
175 |
176 | This method will still require processData to be set in the configuration.
177 |
178 | ### Example of Result
179 |
180 | ```javascript
181 | {
182 | "userInfo": {
183 | "appCodeName": "Mozilla",
184 | "appName": "Netscape",
185 | "vendor": "Google Inc.",
186 | "platform": "MacIntel",
187 | "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36"
188 | },
189 | "time": {
190 | "startTime": 1572725042761,
191 | "currentTime": 1572725069204
192 | },
193 | "clicks": {
194 | "clickCount": 3,
195 | "clickDetails": [
196 | [
197 | 554,
198 | 542,
199 | "html>body>div#login>div.ui.container.animated.fadeInDown>div.ui.center.aligned.colored.trends.segment>form.ui.form>div.fields>div.ten.wide.field>input",
200 | 1572725045313
201 | ]
202 | ]
203 | },
204 | "mouseMovements": [
205 | [
206 | 1031,
207 | 328,
208 | 1572725043646
209 | ]
210 | ],
211 | "mouseScroll": [],
212 | "keyboardActivities": [
213 | ["Enter", 1676543210000],
214 | ["Escape", 1676543220000]
215 | ],
216 | "navigationHistory": [
217 | ["https://example.com/about", 1676543230000],
218 | ["https://example.com/contact", 1676543240000]
219 | ],
220 | "formInteractions": [
221 | ["email_signup", 1676543250000],
222 | ["contact_form", 1676543260000]
223 | ],
224 | "touchEvents": [
225 | ["touchstart", 320, 480, 1676543270000]
226 | ],
227 | "mediaInteractions": [
228 | ["play", "video.mp4", 1676543280000]
229 | ]
230 | }
231 | ```
232 |
233 | ## 🎉 Acknowledgements
234 |
235 | - https://github.com/shnere/user-behavior for inispiration.
236 |
--------------------------------------------------------------------------------
/userBehaviour.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Taha Al-Jody
3 | * https://github.com/TA3/web-user-behaviour
4 | */
5 | var userBehaviour = (function () {
6 | var defaults = {
7 | userInfo: true,
8 | clicks: true,
9 | mouseMovement: true,
10 | mouseMovementInterval: 1,
11 | mouseScroll: true,
12 | timeCount: true,
13 | clearAfterProcess: true,
14 | processTime: 15,
15 | windowResize: true,
16 | visibilitychange: true,
17 | keyboardActivity: true,
18 | pageNavigation: true,
19 | formInteractions: true,
20 | touchEvents: true,
21 | audioVideoInteraction: true,
22 | customEventRegistration: true,
23 | processData: function (results) {
24 | console.log(results);
25 | },
26 | };
27 | var user_config = {};
28 | var mem = {
29 | processInterval: null,
30 | mouseInterval: null,
31 | mousePosition: [], //x,y,timestamp
32 | eventListeners: {
33 | scroll: null,
34 | click: null,
35 | mouseMovement: null,
36 | windowResize: null,
37 | visibilitychange: null,
38 | keyboardActivity: null,
39 | touchStart: null
40 | },
41 | eventsFunctions: {
42 | scroll: () => {
43 | results.mouseScroll.push([window.scrollX, window.scrollY, getTimeStamp()]);
44 | },
45 | click: (e) => {
46 | results.clicks.clickCount++;
47 | var path = [];
48 | var node = "";
49 | e.composedPath().forEach((el, i) => {
50 | if ((i !== e.composedPath().length - 1) && (i !== e.composedPath().length - 2)) {
51 | node = el.localName;
52 | (el.className !== "") ? el.classList.forEach((clE) => {
53 | node += "." + clE
54 | }): 0;
55 | (el.id !== "") ? node += "#" + el.id: 0;
56 | path.push(node);
57 | }
58 | })
59 | path = path.reverse().join(">");
60 | results.clicks.clickDetails.push([e.clientX, e.clientY, path, getTimeStamp()]);
61 | },
62 | mouseMovement: (e) => {
63 | mem.mousePosition = [e.clientX, e.clientY, getTimeStamp()];
64 | },
65 | windowResize: (e) => {
66 | results.windowSizes.push([window.innerWidth, window.innerHeight, getTimeStamp()]);
67 | },
68 | visibilitychange: (e) => {
69 | results.visibilitychanges.push([document.visibilityState, getTimeStamp()]);
70 | processResults();
71 | },
72 | keyboardActivity: (e) => {
73 | results.keyboardActivities.push([e.key, getTimeStamp()]);
74 | },
75 | pageNavigation: () => {
76 | results.navigationHistory.push([location.href, getTimeStamp()]);
77 | },
78 | formInteraction: (e) => {
79 | e.preventDefault(); // Prevent the form from submitting normally
80 | results.formInteractions.push([e.target.name, getTimeStamp()]);
81 | // Optionally, submit the form programmatically after tracking
82 | },
83 | touchStart: (e) => {
84 | results.touchEvents.push(['touchstart', e.touches[0].clientX, e.touches[0].clientY, getTimeStamp()]);
85 | },
86 | mediaInteraction: (e) => {
87 | results.mediaInteractions.push(['play', e.target.currentSrc, getTimeStamp()]);
88 | }
89 | }
90 | };
91 | var results = {};
92 |
93 | function resetResults() {
94 | results = {
95 | userInfo: {
96 | windowSize: [window.innerWidth, window.innerHeight],
97 | appCodeName: navigator.appCodeName || '',
98 | appName: navigator.appName || '',
99 | vendor: navigator.vendor || '',
100 | platform: navigator.platform || '',
101 | userAgent: navigator.userAgent || ''
102 | },
103 | time: {
104 | startTime: 0,
105 | currentTime: 0,
106 | stopTime: 0,
107 | },
108 | clicks: {
109 | clickCount: 0,
110 | clickDetails: []
111 | },
112 | mouseMovements: [],
113 | mouseScroll: [],
114 | keyboardActivities: [],
115 | navigationHistory: [],
116 | formInteractions: [],
117 | touchEvents: [],
118 | mediaInteractions: [],
119 | windowSizes: [],
120 | visibilitychanges: [],
121 | };
122 | };
123 | resetResults();
124 |
125 | function getTimeStamp() {
126 | return Date.now();
127 | };
128 |
129 | function config(ob) {
130 | user_config = {};
131 | Object.keys(defaults).forEach((i) => {
132 | i in ob ? user_config[i] = ob[i] : user_config[i] = defaults[i];
133 | })
134 | };
135 |
136 | function start() {
137 |
138 | if (Object.keys(user_config).length !== Object.keys(defaults).length) {
139 | console.log("no config provided. using default..");
140 | user_config = defaults;
141 | }
142 | // TIME SET
143 | if (user_config.timeCount !== undefined && user_config.timeCount) {
144 | results.time.startTime = getTimeStamp();
145 | }
146 | // MOUSE MOVEMENTS
147 | if (user_config.mouseMovement) {
148 | mem.eventListeners.mouseMovement = window.addEventListener("mousemove", mem.eventsFunctions.mouseMovement);
149 | mem.mouseInterval = setInterval(() => {
150 | if (mem.mousePosition && mem.mousePosition.length) {
151 | if (!results.mouseMovements.length || ((mem.mousePosition[0] !== results.mouseMovements[results.mouseMovements.length - 1][0]) && (mem.mousePosition[1] !== results.mouseMovements[results.mouseMovements.length - 1][1]))) {
152 | results.mouseMovements.push(mem.mousePosition)
153 | }
154 | }
155 | }, defaults.mouseMovementInterval * 1000);
156 | }
157 | //CLICKS
158 | if (user_config.clicks) {
159 | mem.eventListeners.click = window.addEventListener("click", mem.eventsFunctions.click);
160 | }
161 | //SCROLL
162 | if (user_config.mouseScroll) {
163 | mem.eventListeners.scroll = window.addEventListener("scroll", mem.eventsFunctions.scroll);
164 | }
165 | //Window sizes
166 | if (user_config.windowResize !== false) {
167 | mem.eventListeners.windowResize = window.addEventListener("resize", mem.eventsFunctions.windowResize);
168 | }
169 | //Before unload / visibilitychange
170 | if (user_config.visibilitychange !== false) {
171 | mem.eventListeners.visibilitychange = window.addEventListener("visibilitychange", mem.eventsFunctions.visibilitychange);
172 | }
173 | //Keyboard Activity
174 | if (user_config.keyboardActivity) {
175 | mem.eventListeners.keyboardActivity = window.addEventListener("keydown", mem.eventsFunctions.keyboardActivity);
176 | }
177 | //Page Navigation
178 | if (user_config.pageNavigation) {
179 | window.history.pushState = (f => function pushState() {
180 | var ret = f.apply(this, arguments);
181 | window.dispatchEvent(new Event('pushstate'));
182 | window.dispatchEvent(new Event('locationchange'));
183 | return ret;
184 | })(window.history.pushState);
185 |
186 | window.addEventListener('popstate', mem.eventsFunctions.pageNavigation);
187 | window.addEventListener('pushstate', mem.eventsFunctions.pageNavigation);
188 | window.addEventListener('locationchange', mem.eventsFunctions.pageNavigation);
189 | }
190 | //Form Interactions
191 | if (user_config.formInteractions) {
192 | document.querySelectorAll('form').forEach(form => form.addEventListener('submit', mem.eventsFunctions.formInteraction));
193 | }
194 | //Touch Events
195 | if (user_config.touchEvents) {
196 | mem.eventListeners.touchStart = window.addEventListener("touchstart", mem.eventsFunctions.touchStart);
197 | }
198 | //Audio & Video Interaction
199 | if (user_config.audioVideoInteraction) {
200 | document.querySelectorAll('video').forEach(video => {
201 | video.addEventListener('play', mem.eventsFunctions.mediaInteraction);
202 | // Add other media events as needed
203 | });
204 | }
205 |
206 | //PROCESS INTERVAL
207 | if (user_config.processTime !== false) {
208 | mem.processInterval = setInterval(() => {
209 | processResults();
210 | }, user_config.processTime * 1000)
211 | }
212 | };
213 |
214 | function processResults() {
215 | user_config.processData(result());
216 | if (user_config.clearAfterProcess) {
217 | resetResults();
218 | }
219 | }
220 |
221 | function stop() {
222 | if (user_config.processTime !== false) {
223 | clearInterval(mem.processInterval);
224 | }
225 | clearInterval(mem.mouseInterval);
226 | window.removeEventListener("scroll", mem.eventsFunctions.scroll);
227 | window.removeEventListener("click", mem.eventsFunctions.click);
228 | window.removeEventListener("mousemove", mem.eventsFunctions.mouseMovement);
229 | window.removeEventListener("resize", mem.eventsFunctions.windowResize);
230 | window.removeEventListener("visibilitychange", mem.eventsFunctions.visibilitychange);
231 | window.removeEventListener("keydown", mem.eventsFunctions.keyboardActivity);
232 | window.removeEventListener("touchstart", mem.eventsFunctions.touchStart);
233 | results.time.stopTime = getTimeStamp();
234 | processResults();
235 | }
236 |
237 | function result() {
238 | if (user_config.userInfo === false && userBehaviour.showResult().userInfo !== undefined) {
239 | delete userBehaviour.showResult().userInfo;
240 | }
241 | if (user_config.timeCount !== undefined && user_config.timeCount) {
242 | results.time.currentTime = getTimeStamp();
243 | }
244 | return results
245 | };
246 |
247 | function showConfig() {
248 | if (Object.keys(user_config).length !== Object.keys(defaults).length) {
249 | return defaults;
250 | } else {
251 | return user_config;
252 | }
253 | };
254 |
255 | return {
256 | showConfig: showConfig,
257 | config: config,
258 | start: start,
259 | stop: stop,
260 | showResult: result,
261 | processResults: processResults,
262 | registerCustomEvent: (eventName, callback) => {
263 | window.addEventListener(eventName, callback);
264 | },
265 | };
266 |
267 | })();
268 |
--------------------------------------------------------------------------------
/userBehaviour.min.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Taha Al-Jody
3 | * https://github.com/TA3/web-user-behaviour
4 | */
5 | var userBehaviour=function(){var e={userInfo:!0,clicks:!0,mouseMovement:!0,mouseMovementInterval:1,mouseScroll:!0,timeCount:!0,clearAfterProcess:!0,processTime:15,windowResize:!0,visibilitychange:!0,keyboardActivity:!0,pageNavigation:!0,formInteractions:!0,touchEvents:!0,audioVideoInteraction:!0,customEventRegistration:!0,processData:function(e){console.log(e)}},t={},n={processInterval:null,mouseInterval:null,mousePosition:[],eventListeners:{scroll:null,click:null,mouseMovement:null,windowResize:null,visibilitychange:null,keyboardActivity:null,touchStart:null},eventsFunctions:{scroll:()=>{i.mouseScroll.push([window.scrollX,window.scrollY,s()])},click:e=>{i.clicks.clickCount++;var t=[],n="";e.composedPath().forEach(((i,o)=>{o!==e.composedPath().length-1&&o!==e.composedPath().length-2&&(n=i.localName,""!==i.className&&i.classList.forEach((e=>{n+="."+e})),""!==i.id&&(n+="#"+i.id),t.push(n))})),t=t.reverse().join(">"),i.clicks.clickDetails.push([e.clientX,e.clientY,t,s()])},mouseMovement:e=>{n.mousePosition=[e.clientX,e.clientY,s()]},windowResize:e=>{i.windowSizes.push([window.innerWidth,window.innerHeight,s()])},visibilitychange:e=>{i.visibilitychanges.push([document.visibilityState,s()]),r()},keyboardActivity:e=>{i.keyboardActivities.push([e.key,s()])},pageNavigation:()=>{i.navigationHistory.push([location.href,s()])},formInteraction:e=>{e.preventDefault(),i.formInteractions.push([e.target.name,s()])},touchStart:e=>{i.touchEvents.push(["touchstart",e.touches[0].clientX,e.touches[0].clientY,s()])},mediaInteraction:e=>{i.mediaInteractions.push(["play",e.target.currentSrc,s()])}}},i={};function o(){i={userInfo:{windowSize:[window.innerWidth,window.innerHeight],appCodeName:navigator.appCodeName||"",appName:navigator.appName||"",vendor:navigator.vendor||"",platform:navigator.platform||"",userAgent:navigator.userAgent||""},time:{startTime:0,currentTime:0,stopTime:0},clicks:{clickCount:0,clickDetails:[]},mouseMovements:[],mouseScroll:[],keyboardActivities:[],navigationHistory:[],formInteractions:[],touchEvents:[],mediaInteractions:[],windowSizes:[],visibilitychanges:[]}}function s(){return Date.now()}function r(){t.processData(c()),t.clearAfterProcess&&o()}function c(){return!1===t.userInfo&&void 0!==userBehaviour.showResult().userInfo&&delete userBehaviour.showResult().userInfo,void 0!==t.timeCount&&t.timeCount&&(i.time.currentTime=s()),i}return o(),{showConfig:function(){return Object.keys(t).length!==Object.keys(e).length?e:t},config:function(n){t={},Object.keys(e).forEach((i=>{t[i]=i in n?n[i]:e[i]}))},start:function(){var o;Object.keys(t).length!==Object.keys(e).length&&(console.log("no config provided. using default.."),t=e),void 0!==t.timeCount&&t.timeCount&&(i.time.startTime=s()),t.mouseMovement&&(n.eventListeners.mouseMovement=window.addEventListener("mousemove",n.eventsFunctions.mouseMovement),n.mouseInterval=setInterval((()=>{n.mousePosition&&n.mousePosition.length&&(!i.mouseMovements.length||n.mousePosition[0]!==i.mouseMovements[i.mouseMovements.length-1][0]&&n.mousePosition[1]!==i.mouseMovements[i.mouseMovements.length-1][1])&&i.mouseMovements.push(n.mousePosition)}),1e3*e.mouseMovementInterval)),t.clicks&&(n.eventListeners.click=window.addEventListener("click",n.eventsFunctions.click)),t.mouseScroll&&(n.eventListeners.scroll=window.addEventListener("scroll",n.eventsFunctions.scroll)),!1!==t.windowResize&&(n.eventListeners.windowResize=window.addEventListener("resize",n.eventsFunctions.windowResize)),!1!==t.visibilitychange&&(n.eventListeners.visibilitychange=window.addEventListener("visibilitychange",n.eventsFunctions.visibilitychange)),t.keyboardActivity&&(n.eventListeners.keyboardActivity=window.addEventListener("keydown",n.eventsFunctions.keyboardActivity)),t.pageNavigation&&(window.history.pushState=(o=window.history.pushState,function(){var e=o.apply(this,arguments);return window.dispatchEvent(new Event("pushstate")),window.dispatchEvent(new Event("locationchange")),e}),window.addEventListener("popstate",n.eventsFunctions.pageNavigation),window.addEventListener("pushstate",n.eventsFunctions.pageNavigation),window.addEventListener("locationchange",n.eventsFunctions.pageNavigation)),t.formInteractions&&document.querySelectorAll("form").forEach((e=>e.addEventListener("submit",n.eventsFunctions.formInteraction))),t.touchEvents&&(n.eventListeners.touchStart=window.addEventListener("touchstart",n.eventsFunctions.touchStart)),t.audioVideoInteraction&&document.querySelectorAll("video").forEach((e=>{e.addEventListener("play",n.eventsFunctions.mediaInteraction)})),!1!==t.processTime&&(n.processInterval=setInterval((()=>{r()}),1e3*t.processTime))},stop:function(){!1!==t.processTime&&clearInterval(n.processInterval),clearInterval(n.mouseInterval),window.removeEventListener("scroll",n.eventsFunctions.scroll),window.removeEventListener("click",n.eventsFunctions.click),window.removeEventListener("mousemove",n.eventsFunctions.mouseMovement),window.removeEventListener("resize",n.eventsFunctions.windowResize),window.removeEventListener("visibilitychange",n.eventsFunctions.visibilitychange),window.removeEventListener("keydown",n.eventsFunctions.keyboardActivity),window.removeEventListener("touchstart",n.eventsFunctions.touchStart),i.time.stopTime=s(),r()},showResult:c,processResults:r,registerCustomEvent:(e,t)=>{window.addEventListener(e,t)}}}();
--------------------------------------------------------------------------------