-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathactivity-monitor.js
203 lines (166 loc) · 7.33 KB
/
activity-monitor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
/*jshint -W116, -W030, latedef: false */
'use strict';
(function (root, factory) {
if (typeof module !== 'undefined' && module.exports) {
// CommonJS
if (typeof angular === 'undefined') {
factory(require('angular'));
} else {
factory(angular);
}
module.exports = 'ActivityMonitor';
} else if (typeof define === 'function' && define.amd) {
// AMD
define(['angular'], factory);
} else {
// Global variables
factory(root.angular);
}
}(this, function (angular) {
var m = angular
.module('ActivityMonitor', [])
.service('ActivityMonitor', ActivityMonitor);
var MILLISECOND = 1000;
var EVENT_KEEPALIVE = 'keepAlive';
var EVENT_INACTIVE = 'inactive';
var EVENT_WARNING = 'warning';
var EVENT_ACTIVITY = 'activity';
ActivityMonitor.$inject = ['$document'];
function ActivityMonitor($document) {
var service = this;
/* configuration */
service.options = {
enabled: false, /* is the ActivityMonitor enabled? */
keepAlive: 800, /* keepAlive ping invterval (seconds) */
inactive: 900, /* how long until user is considered inactive? (seconds) */
warning: 60, /* when to warn user when nearing inactive state (deducted from inactive in seconds) */
monitor: 3, /* how frequently to check if the user is inactive (seconds) */
disableOnInactive: true, /* by default, once user becomes inactive, all listeners are detached */
DOMevents: ['mousemove', 'mousedown', 'mouseup', 'keypress', 'wheel', 'touchstart', 'scroll'] /* list of DOM events to determine user's activity */
};
/* user activity */
service.user = {
action: Date.now(), /* timestamp of the users' last action */
active: true, /* is the user considered active? */
warning: false /* is the user in warning state? */
};
service.activity = activity; /* method consumers can use to supply activity */
service.on = service.bind = subscribe; /* expose method to subscribe to events */
service.off = service.unbind = unsubscribe; /* expose method to unsubscribe from events */
var events = {};
events[EVENT_KEEPALIVE] = {}; /* functions to invoke along with ping (options.frequency) */
events[EVENT_INACTIVE] = {}; /* functions to invoke when user goes inactive (options.threshold) */
events[EVENT_WARNING] = {}; /* functions to invoke when warning user about inactivity (options.warning) */
events[EVENT_ACTIVITY] = {}; /* functions to invoke any time a user makes a move */
var timer = {
inactivity: null, /* setInterval handle to determine whether the user is inactive */
keepAlive: null /* setInterval handle for ping handler (options.frequency) */
};
enable.timer = timer;
service.enable = enable;
service.disable = disable;
return service;
///////////////
function disable() {
service.options.enabled = false;
disableIntervals()
$document.off(service.options.DOMevents.join(' '), activity);
}
function disableIntervals(){
clearInterval(timer.inactivity);
clearInterval(timer.keepAlive);
delete timer.inactivity;
delete timer.keepAlive;
}
function enable() {
$document.on(service.options.DOMevents.join(' '), activity);
service.options.enabled = true;
service.user.warning = false;
enableIntervals();
}
function enableIntervals(){
timer.keepAlive = setInterval(function () {
publish(EVENT_KEEPALIVE);
}, service.options.keepAlive * MILLISECOND);
timer.inactivity = setInterval(function () {
var now = Date.now();
var warning = now - (service.options.inactive - service.options.warning) * MILLISECOND;
var inactive = now - service.options.inactive * MILLISECOND;
/* should we display warning */
if (!service.user.warning && service.user.action <= warning) {
service.user.warning = true;
publish(EVENT_WARNING);
}
/* should user be considered inactive? */
if (service.user.active && service.user.action <= inactive) {
service.user.active = false;
publish(EVENT_INACTIVE);
if(service.options.disableOnInactive){
disable();
}else{
disableIntervals();//user inactive is known, lets stop checking, for now
dynamicActivity = reactivate;//hot swap method that handles document event watching
}
}
}, service.options.monitor * MILLISECOND);
}
/* function that lives in memory with the intention of being swapped out */
function dynamicActivity(){
regularActivityMonitor();
}
/* after user inactive, this method is hot swapped as the dynamicActivity method in-which the next user activity reactivates monitors */
function reactivate() {
enableIntervals();
dynamicActivity = regularActivityMonitor;
}
/* invoked on every user action */
function activity(){
dynamicActivity()
}
/* during a users active state the following method is called */
function regularActivityMonitor() {
service.user.active = true;
service.user.action = Date.now();
publish(EVENT_ACTIVITY);
if (service.user.warning) {
service.user.warning = false;
publish(EVENT_KEEPALIVE);
}
}
function publish(event) {
if (!service.options.enabled) return;
var spaces = Object.keys(events[event]);
if (!event || !spaces.length) return;
spaces.forEach(function (space) {
events[event][space] && events[event][space]();
});
}
function subscribe(event, callback) {
if (!event || typeof callback !== 'function') return;
event = _namespace(event, callback);
events[event.name][event.space] = callback;
!service.options.enabled && enable();
}
function unsubscribe(event, callback) {
event = _namespace(event, callback);
if (!event.space) {
events[event.name] = {};
return;
}
events[event.name][event.space] = null;
}
/* method to return event namespace */
function _namespace(event, callback) {
event = event.split('.');
if (!event[1] && typeof callback === 'function') {
/* if no namespace, use callback and strip all linebreaks and spaces */
event[1] = callback.toString().substr(0, 150).replace(/\r?\n|\r|\s+/gm, '');
}
return {
name: event[0],
space: event[1]
};
}
}
return m;
}));