66 | Wait 10 seconds, you will see a expiring warning. Wait 10 more seconds and you will see that you have been logged out.
67 |
68 |
69 | In the real world I forward them to the logout url, which intern fowards them to login screen, instead of showing the 2nd dialog.
70 | You can modify the session.logout function.
71 |
72 |
73 | We could use the active.idleTimer event to clearTimeout, however I prefer the user to explicitly say they want to keep the
74 | session open by clicking ok, not just moving the mouse on the screen.
75 |
76 |
77 | This demo takes into account when a mobile device closes the browser, and after the idle timeout expires, launches the browser again. Instead
78 | of displaying the warning, it will jump straight to the logged out dialog.
79 |
80 |
81 | For the sake of complete demo, I've included the code needed to call a keepalive url to keep the server side session valid.
82 |
83 |
84 |
85 |
86 |
87 |
88 |
Session Expiration Warning
89 |
90 |
91 |
You've been inactive for a while. For your security, we'll log you out automatically. Click "Stay Online" to continue your session.
92 |
Your session will expire in 120 seconds.
93 |
94 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
You have been logged out
106 |
107 |
108 |
Your session has expired.
109 |
110 |
112 |
113 |
114 |
115 |
187 |
188 |
189 |
--------------------------------------------------------------------------------
/dist/idle-timer.1.0.0.js:
--------------------------------------------------------------------------------
1 | /*! Idle Timer - v1.0.0 - 2014-03-10
2 | * https://github.com/thorst/jquery-idletimer
3 | * Copyright (c) 2014 Paul Irish; Licensed MIT */
4 | /*
5 | mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
6 | DOMMouseScroll (deprecated) -> Firefox 1.0
7 | wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
8 |
9 | //No need to use, use DOMMouseScroll
10 | MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
11 |
12 | //Events
13 | WheelEvent -> see wheel
14 | MouseWheelEvent -> see mousewheel
15 | MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
16 | */
17 | (function ($) {
18 |
19 | $.idleTimer = function (firstParam, elem) {
20 | var opts;
21 | if ( typeof firstParam === "object" ) {
22 | opts = firstParam;
23 | firstParam = null;
24 | } else if (typeof firstParam === "number") {
25 | opts = { timeout: firstParam };
26 | firstParam = null;
27 | }
28 |
29 | // element to watch
30 | elem = elem || document;
31 |
32 | // defaults that are to be stored as instance props on the elem
33 | opts = $.extend({
34 | idle: false, // indicates if the user is idle
35 | timeout: 30000, // the amount of time (ms) before the user is considered idle
36 | events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
37 | }, opts);
38 |
39 | var jqElem = $(elem),
40 | obj = jqElem.data("idleTimerObj") || {},
41 |
42 | /* (intentionally not documented)
43 | * Toggles the idle state and fires an appropriate event.
44 | * @return {void}
45 | */
46 | toggleIdleState = function (e) {
47 |
48 | var obj = $.data(elem, "idleTimerObj") || {};
49 |
50 | // toggle the state
51 | obj.idle = !obj.idle;
52 |
53 | // store toggle state date time
54 | obj.olddate = +new Date();
55 |
56 | // create a custom event, with state and name space
57 | var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer");
58 |
59 | // trigger event on object with elem and copy of obj
60 | $(elem).trigger(event, [elem, $.extend({}, obj), e]);
61 | },
62 | /**
63 | * Handle event triggers
64 | * @return {void}
65 | * @method event
66 | * @static
67 | */
68 | handleEvent = function (e) {
69 |
70 | var obj = $.data(elem, "idleTimerObj") || {};
71 |
72 | // this is already paused, ignore events for now
73 | if (obj.remaining != null) { return; }
74 |
75 | /*
76 | mousemove is kinda buggy, it can be triggered when it should be idle.
77 | Typically is happening between 115 - 150 milliseconds after idle triggered.
78 | @psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
79 | @thorst has similar issues on ios7 "after $.scrollTop() on text area"
80 | */
81 | if (e.type === "mousemove") {
82 | // if coord are same, it didn't move
83 | if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
84 | return;
85 | }
86 | // if coord don't exist how could it move
87 | if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
88 | return;
89 | }
90 | // under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
91 | var elapsed = (+new Date()) - obj.olddate;
92 | if (elapsed < 200) {
93 | return;
94 | }
95 | }
96 |
97 | // clear any existing timeout
98 | clearTimeout(obj.tId);
99 |
100 | // if the idle timer is enabled, flip
101 | if (obj.idle) {
102 | toggleIdleState(e);
103 | }
104 |
105 | // store when user was last active
106 | obj.lastActive = +new Date();
107 |
108 | // update mouse coord
109 | obj.pageX = e.pageX;
110 | obj.pageY = e.pageY;
111 |
112 | // set a new timeout
113 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
114 |
115 | },
116 | /**
117 | * Restore initial settings and restart timer
118 | * @return {void}
119 | * @method reset
120 | * @static
121 | */
122 | reset = function () {
123 |
124 | var obj = $.data(elem, "idleTimerObj") || {};
125 |
126 | // reset settings
127 | obj.idle = obj.idleBackup;
128 | obj.olddate = +new Date();
129 | obj.lastActive = obj.olddate;
130 | obj.remaining = null;
131 |
132 | // reset Timers
133 | clearTimeout(obj.tId);
134 | if (!obj.idle) {
135 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
136 | }
137 |
138 | },
139 | /**
140 | * Store remaining time, stop timer
141 | * You can pause from an idle OR active state
142 | * @return {void}
143 | * @method pause
144 | * @static
145 | */
146 | pause = function () {
147 |
148 | var obj = $.data(elem, "idleTimerObj") || {};
149 |
150 | // this is already paused
151 | if ( obj.remaining != null ) { return; }
152 |
153 | // define how much is left on the timer
154 | obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
155 |
156 | // clear any existing timeout
157 | clearTimeout(obj.tId);
158 | },
159 | /**
160 | * Start timer with remaining value
161 | * @return {void}
162 | * @method resume
163 | * @static
164 | */
165 | resume = function () {
166 |
167 | var obj = $.data(elem, "idleTimerObj") || {};
168 |
169 | // this isn't paused yet
170 | if ( obj.remaining == null ) { return; }
171 |
172 | // start timer
173 | if ( !obj.idle ) {
174 | obj.tId = setTimeout(toggleIdleState, obj.remaining);
175 | }
176 |
177 | // clear remaining
178 | obj.remaining = null;
179 | },
180 | /**
181 | * Stops the idle timer. This removes appropriate event handlers
182 | * and cancels any pending timeouts.
183 | * @return {void}
184 | * @method destroy
185 | * @static
186 | */
187 | destroy = function () {
188 |
189 | var obj = $.data(elem, "idleTimerObj") || {};
190 |
191 | //clear any pending timeouts
192 | clearTimeout(obj.tId);
193 |
194 | //Remove data
195 | jqElem.removeData("idleTimerObj");
196 |
197 | //detach the event handlers
198 | jqElem.off("._idleTimer");
199 | },
200 | /**
201 | * Returns the time until becoming idle
202 | * @return {number}
203 | * @method remainingtime
204 | * @static
205 | */
206 | remainingtime = function () {
207 |
208 | var obj = $.data(elem, "idleTimerObj") || {};
209 |
210 | //If idle there is no time remaining
211 | if ( obj.idle ) { return 0; }
212 |
213 | //If its paused just return that
214 | if ( obj.remaining != null ) { return obj.remaining; }
215 |
216 | //Determine remaining, if negative idle didn't finish flipping, just return 0
217 | var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
218 | if (remaining < 0) { remaining = 0; }
219 |
220 | //If this is paused return that number, else return current remaining
221 | return remaining;
222 | };
223 |
224 |
225 | // determine which function to call
226 | if (firstParam === null && typeof obj.idle !== "undefined") {
227 | // they think they want to init, but it already is, just reset
228 | reset();
229 | return jqElem;
230 | } else if (firstParam === null) {
231 | // they want to init
232 | } else if (firstParam !== null && typeof obj.idle === "undefined") {
233 | // they want to do something, but it isnt init
234 | // not sure the best way to handle this
235 | return false;
236 | } else if (firstParam === "destroy") {
237 | destroy();
238 | return jqElem;
239 | } else if (firstParam === "pause") {
240 | pause();
241 | return jqElem;
242 | } else if (firstParam === "resume") {
243 | resume();
244 | return jqElem;
245 | } else if (firstParam === "reset") {
246 | reset();
247 | return jqElem;
248 | } else if (firstParam === "getRemainingTime") {
249 | return remainingtime();
250 | } else if (firstParam === "getElapsedTime") {
251 | return (+new Date()) - obj.olddate;
252 | } else if (firstParam === "getLastActiveTime") {
253 | return obj.lastActive;
254 | } else if (firstParam === "isIdle") {
255 | return obj.idle;
256 | }
257 |
258 | /* (intentionally not documented)
259 | * Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
260 | * @param {Event} event A DOM2-normalized event object.
261 | * @return {void}
262 | */
263 | jqElem.on($.trim((opts.events + " ").split(" ").join("._idleTimer ")), function (e) {
264 | handleEvent(e);
265 | });
266 |
267 |
268 | // Internal Object Properties, This isn't all necessary, but we
269 | // explicitly define all keys here so we know what we are working with
270 | obj = $.extend({}, {
271 | olddate : +new Date(), // the last time state changed
272 | lastActive: +new Date(), // the last time timer was active
273 | idle : opts.idle, // current state
274 | idleBackup : opts.idle, // backup of idle parameter since it gets modified
275 | timeout : opts.timeout, // the interval to change state
276 | remaining : null, // how long until state changes
277 | tId : null, // the idle timer setTimeout
278 | pageX : null, // used to store the mouse coord
279 | pageY : null
280 | });
281 |
282 | // set a timeout to toggle state. May wish to omit this in some situations
283 | if (!obj.idle) {
284 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
285 | }
286 |
287 | // store our instance on the object
288 | $.data(elem, "idleTimerObj", obj);
289 |
290 | return jqElem;
291 | };
292 |
293 | // This allows binding to element
294 | $.fn.idleTimer = function (firstParam) {
295 | if (this[0]) {
296 | return $.idleTimer(firstParam, this[0]);
297 | }
298 |
299 | return this;
300 | };
301 |
302 | })(jQuery);
303 |
--------------------------------------------------------------------------------
/dist/idle-timer.1.0.1.js:
--------------------------------------------------------------------------------
1 | /*! Idle Timer - v1.0.1 - 2014-03-21
2 | * https://github.com/thorst/jquery-idletimer
3 | * Copyright (c) 2014 Paul Irish; Licensed MIT */
4 | /*
5 | mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
6 | DOMMouseScroll (deprecated) -> Firefox 1.0
7 | wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
8 |
9 | //No need to use, use DOMMouseScroll
10 | MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
11 |
12 | //Events
13 | WheelEvent -> see wheel
14 | MouseWheelEvent -> see mousewheel
15 | MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
16 | */
17 | (function ($) {
18 |
19 | $.idleTimer = function (firstParam, elem) {
20 | var opts;
21 | if ( typeof firstParam === "object" ) {
22 | opts = firstParam;
23 | firstParam = null;
24 | } else if (typeof firstParam === "number") {
25 | opts = { timeout: firstParam };
26 | firstParam = null;
27 | }
28 |
29 | // element to watch
30 | elem = elem || document;
31 |
32 | // defaults that are to be stored as instance props on the elem
33 | opts = $.extend({
34 | idle: false, // indicates if the user is idle
35 | timeout: 30000, // the amount of time (ms) before the user is considered idle
36 | events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
37 | }, opts);
38 |
39 | var jqElem = $(elem),
40 | obj = jqElem.data("idleTimerObj") || {},
41 |
42 | /* (intentionally not documented)
43 | * Toggles the idle state and fires an appropriate event.
44 | * @return {void}
45 | */
46 | toggleIdleState = function (e) {
47 |
48 | var obj = $.data(elem, "idleTimerObj") || {};
49 |
50 | // toggle the state
51 | obj.idle = !obj.idle;
52 |
53 | // store toggle state date time
54 | obj.olddate = +new Date();
55 |
56 | // create a custom event, with state and name space
57 | var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer");
58 |
59 | // trigger event on object with elem and copy of obj
60 | $(elem).trigger(event, [elem, $.extend({}, obj), e]);
61 | },
62 | /**
63 | * Handle event triggers
64 | * @return {void}
65 | * @method event
66 | * @static
67 | */
68 | handleEvent = function (e) {
69 |
70 | var obj = $.data(elem, "idleTimerObj") || {};
71 |
72 | // this is already paused, ignore events for now
73 | if (obj.remaining != null) { return; }
74 |
75 | /*
76 | mousemove is kinda buggy, it can be triggered when it should be idle.
77 | Typically is happening between 115 - 150 milliseconds after idle triggered.
78 | @psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
79 | @thorst has similar issues on ios7 "after $.scrollTop() on text area"
80 | */
81 | if (e.type === "mousemove") {
82 | // if coord are same, it didn't move
83 | if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
84 | return;
85 | }
86 | // if coord don't exist how could it move
87 | if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
88 | return;
89 | }
90 | // under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
91 | var elapsed = (+new Date()) - obj.olddate;
92 | if (elapsed < 200) {
93 | return;
94 | }
95 | }
96 |
97 | // clear any existing timeout
98 | clearTimeout(obj.tId);
99 |
100 | // if the idle timer is enabled, flip
101 | if (obj.idle) {
102 | toggleIdleState(e);
103 | }
104 |
105 | // store when user was last active
106 | obj.lastActive = +new Date();
107 |
108 | // update mouse coord
109 | obj.pageX = e.pageX;
110 | obj.pageY = e.pageY;
111 |
112 | // set a new timeout
113 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
114 |
115 | },
116 | /**
117 | * Restore initial settings and restart timer
118 | * @return {void}
119 | * @method reset
120 | * @static
121 | */
122 | reset = function () {
123 |
124 | var obj = $.data(elem, "idleTimerObj") || {};
125 |
126 | // reset settings
127 | obj.idle = obj.idleBackup;
128 | obj.olddate = +new Date();
129 | obj.lastActive = obj.olddate;
130 | obj.remaining = null;
131 |
132 | // reset Timers
133 | clearTimeout(obj.tId);
134 | if (!obj.idle) {
135 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
136 | }
137 |
138 | },
139 | /**
140 | * Store remaining time, stop timer
141 | * You can pause from an idle OR active state
142 | * @return {void}
143 | * @method pause
144 | * @static
145 | */
146 | pause = function () {
147 |
148 | var obj = $.data(elem, "idleTimerObj") || {};
149 |
150 | // this is already paused
151 | if ( obj.remaining != null ) { return; }
152 |
153 | // define how much is left on the timer
154 | obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
155 |
156 | // clear any existing timeout
157 | clearTimeout(obj.tId);
158 | },
159 | /**
160 | * Start timer with remaining value
161 | * @return {void}
162 | * @method resume
163 | * @static
164 | */
165 | resume = function () {
166 |
167 | var obj = $.data(elem, "idleTimerObj") || {};
168 |
169 | // this isn't paused yet
170 | if ( obj.remaining == null ) { return; }
171 |
172 | // start timer
173 | if ( !obj.idle ) {
174 | obj.tId = setTimeout(toggleIdleState, obj.remaining);
175 | }
176 |
177 | // clear remaining
178 | obj.remaining = null;
179 | },
180 | /**
181 | * Stops the idle timer. This removes appropriate event handlers
182 | * and cancels any pending timeouts.
183 | * @return {void}
184 | * @method destroy
185 | * @static
186 | */
187 | destroy = function () {
188 |
189 | var obj = $.data(elem, "idleTimerObj") || {};
190 |
191 | //clear any pending timeouts
192 | clearTimeout(obj.tId);
193 |
194 | //Remove data
195 | jqElem.removeData("idleTimerObj");
196 |
197 | //detach the event handlers
198 | jqElem.off("._idleTimer");
199 | },
200 | /**
201 | * Returns the time until becoming idle
202 | * @return {number}
203 | * @method remainingtime
204 | * @static
205 | */
206 | remainingtime = function () {
207 |
208 | var obj = $.data(elem, "idleTimerObj") || {};
209 |
210 | //If idle there is no time remaining
211 | if ( obj.idle ) { return 0; }
212 |
213 | //If its paused just return that
214 | if ( obj.remaining != null ) { return obj.remaining; }
215 |
216 | //Determine remaining, if negative idle didn't finish flipping, just return 0
217 | var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
218 | if (remaining < 0) { remaining = 0; }
219 |
220 | //If this is paused return that number, else return current remaining
221 | return remaining;
222 | };
223 |
224 |
225 | // determine which function to call
226 | if (firstParam === null && typeof obj.idle !== "undefined") {
227 | // they think they want to init, but it already is, just reset
228 | reset();
229 | return jqElem;
230 | } else if (firstParam === null) {
231 | // they want to init
232 | } else if (firstParam !== null && typeof obj.idle === "undefined") {
233 | // they want to do something, but it isnt init
234 | // not sure the best way to handle this
235 | return false;
236 | } else if (firstParam === "destroy") {
237 | destroy();
238 | return jqElem;
239 | } else if (firstParam === "pause") {
240 | pause();
241 | return jqElem;
242 | } else if (firstParam === "resume") {
243 | resume();
244 | return jqElem;
245 | } else if (firstParam === "reset") {
246 | reset();
247 | return jqElem;
248 | } else if (firstParam === "getRemainingTime") {
249 | return remainingtime();
250 | } else if (firstParam === "getElapsedTime") {
251 | return (+new Date()) - obj.olddate;
252 | } else if (firstParam === "getLastActiveTime") {
253 | return obj.lastActive;
254 | } else if (firstParam === "isIdle") {
255 | return obj.idle;
256 | }
257 |
258 | /* (intentionally not documented)
259 | * Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
260 | * @param {Event} event A DOM2-normalized event object.
261 | * @return {void}
262 | */
263 | jqElem.on($.trim((opts.events + " ").split(" ").join("._idleTimer ")), function (e) {
264 | handleEvent(e);
265 | });
266 |
267 |
268 | // Internal Object Properties, This isn't all necessary, but we
269 | // explicitly define all keys here so we know what we are working with
270 | obj = $.extend({}, {
271 | olddate : +new Date(), // the last time state changed
272 | lastActive: +new Date(), // the last time timer was active
273 | idle : opts.idle, // current state
274 | idleBackup : opts.idle, // backup of idle parameter since it gets modified
275 | timeout : opts.timeout, // the interval to change state
276 | remaining : null, // how long until state changes
277 | tId : null, // the idle timer setTimeout
278 | pageX : null, // used to store the mouse coord
279 | pageY : null
280 | });
281 |
282 | // set a timeout to toggle state. May wish to omit this in some situations
283 | if (!obj.idle) {
284 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
285 | }
286 |
287 | // store our instance on the object
288 | $.data(elem, "idleTimerObj", obj);
289 |
290 | return jqElem;
291 | };
292 |
293 | // This allows binding to element
294 | $.fn.idleTimer = function (firstParam) {
295 | if (this[0]) {
296 | return $.idleTimer(firstParam, this[0]);
297 | }
298 |
299 | return this;
300 | };
301 |
302 | })(jQuery);
303 |
--------------------------------------------------------------------------------
/test/idle-timer_test.js:
--------------------------------------------------------------------------------
1 | (function($) {
2 | /*
3 | ======== A Handy Little QUnit Reference ========
4 | http://docs.jquery.com/QUnit
5 |
6 | Test methods:
7 | expect(numAssertions)
8 | stop(increment)
9 | start(decrement)
10 | Test assertions:
11 | ok(value, [message])
12 | equal(actual, expected, [message])
13 | notEqual(actual, expected, [message])
14 | deepEqual(actual, expected, [message])
15 | notDeepEqual(actual, expected, [message])
16 | strictEqual(actual, expected, [message])
17 | notStrictEqual(actual, expected, [message])
18 | raises(block, [expected], [message])
19 | */
20 | //Name | expects | test
21 | module("Events Auto Binding");
22 | asyncTest("idle Event Triggered", 2, function () {
23 | $(document).on("idle.idleTimer", function (event, elem, obj) {
24 |
25 | ok(true, "idle fires at document");
26 | ok(obj.idle, "object returned properly");
27 |
28 | $.idleTimer("destroy");
29 | $(document).off();
30 |
31 | start();
32 | });
33 | $.idleTimer( 100 );
34 | });
35 | asyncTest("idle Event Triggered when using unique id", 2, function () {
36 | $(document).on("idle.idleTimersomeUniqueString", function (event, elem, obj) {
37 |
38 | ok(true, "idle fires at document");
39 | ok(obj.idle, "object returned properly");
40 |
41 | $.idleTimer("destroy", document, "someUniqueString");
42 | $(document).off();
43 |
44 | start();
45 | });
46 | $.idleTimer( 100, document, "someUniqueString" );
47 | });
48 | asyncTest( "active Event Triggered", 2, function() {
49 | $( document ).on( "active.idleTimer", function(event, elem, obj){
50 |
51 | ok(true, "active fires at document");
52 | ok(!obj.idle, "object returned properly");
53 |
54 | $.idleTimer("destroy");
55 | $(document).off();
56 |
57 | start();
58 | });
59 | $.idleTimer({idle:true});
60 | setTimeout( function(){
61 | $( "#qunit-fixture" ).trigger( "keydown" );
62 | }, 100 );
63 | });
64 |
65 |
66 |
67 |
68 | module("Events Element Binding");
69 | asyncTest("idle Triggered", 2, function () {
70 | $("#qunit-fixture").on("idle.idleTimer", function (event, elem, obj) {
71 |
72 | ok(true, "idle fires at document");
73 | ok(obj.idle, "object returned properly");
74 |
75 | $("#qunit-fixture").idleTimer("destroy");
76 |
77 | start();
78 | });
79 | $("#qunit-fixture").idleTimer(100);
80 | });
81 | asyncTest("idle Triggered with multiple idle timers", 4, function () {
82 | var oneFinished = false;
83 | $("#qunit-fixture").on("idle.idleTimersomeUniqueString", function (event, elem, obj) {
84 |
85 | ok(true, "idle fires at document");
86 | ok(obj.idle, "object returned properly");
87 |
88 | $("#qunit-fixture").idleTimer("destroy", "someUniqueString");
89 |
90 | if (oneFinished) {
91 | start();
92 | }
93 | oneFinished = true;
94 | });
95 | $("#qunit-fixture").on("idle.idleTimeranotherUniqueString", function (event, elem, obj) {
96 |
97 | ok(true, "idle fires at document");
98 | ok(obj.idle, "object returned properly");
99 |
100 | $("#qunit-fixture").idleTimer("destroy", "anotherUniqueString");
101 |
102 | if (oneFinished) {
103 | start();
104 | }
105 | oneFinished = true;
106 | });
107 | $("#qunit-fixture").idleTimer(100, "someUniqueString");
108 | $("#qunit-fixture").idleTimer(100, "anotherUniqueString");
109 | });
110 | asyncTest("active Triggered", 2, function () {
111 | $("#qunit-fixture").on("active.idleTimer", function (event, elem, obj) {
112 |
113 | ok(true, "active fires at document");
114 | ok(!obj.idle, "object returned properly");
115 |
116 | $("#qunit-fixture").idleTimer("destroy");
117 |
118 | start();
119 | });
120 | $("#qunit-fixture").idleTimer({ idle: true });
121 | setTimeout(function () {
122 | $("#qunit-fixture").trigger("keydown");
123 | }, 100);
124 | });
125 |
126 | module("Timer sync");
127 | asyncTest("setting lastActive via localStorage", 1, function() {
128 | localStorage.clear();
129 | $.idleTimer( {timeout: 500, timerSyncId: "timer-test"} );
130 | setTimeout( function() {
131 | $( "#qunit-fixture" ).trigger( "keydown" );
132 | }, 100 );
133 | setTimeout( function() {
134 | ok(localStorage.getItem("timer-test"), "localStorage key was set");
135 | $.idleTimer("destroy");
136 | $(document).off();
137 | start();
138 | }, 300 );
139 | });
140 | asyncTest( "storage triggers active", 2, function() {
141 | localStorage.clear();
142 | $( document ).on( "active.idleTimer", function(event, elem, obj){
143 |
144 | ok(true, "active fires at document");
145 | ok(!obj.idle, "object returned properly");
146 |
147 | $.idleTimer("destroy");
148 | $(document).off();
149 |
150 | start();
151 | });
152 | $.idleTimer( {idle:true, timerSyncId: "timer-storage-event-test"} );
153 | setTimeout( function() {
154 | var e = $.Event("storage");
155 | // simulate a storage event for this timer's sync ID
156 | e.originalEvent = {
157 | key: "timer-storage-event-test",
158 | oldValue: "1",
159 | newValue: "2"
160 | };
161 | $(window).trigger(e);
162 | }, 100 );
163 | });
164 | asyncTest( "storage triggers active on matching timerSyncId, unique id does not matter", 4, function() {
165 | localStorage.clear();
166 | var oneFinished = false;
167 | $( document ).on( "active.idleTimersomeUniqueString", function(event, elem, obj){
168 |
169 | ok(true, "active fires at document");
170 | ok(!obj.idle, "object returned properly");
171 |
172 | if (oneFinished) {
173 | $.idleTimer("destroy", "someUniqueString");
174 | $(document).off();
175 | start();
176 | }
177 | oneFinished = true;
178 | });
179 | $( document ).on( "active.idleTimeranotherUniqueString", function(event, elem, obj){
180 |
181 | ok(true, "active fires at document");
182 | ok(!obj.idle, "object returned properly");
183 |
184 |
185 | if (oneFinished) {
186 | $.idleTimer("destroy", "anotherUniqueString");
187 | $(document).off();
188 | start();
189 | }
190 | oneFinished = true;
191 | });
192 | $.idleTimer( {idle:true, timerSyncId: "timer-storage-event-test"}, document, "someUniqueString");
193 | $.idleTimer( {idle:true, timerSyncId: "timer-storage-event-test"}, document, "anotherUniqueString");
194 | setTimeout( function() {
195 | var e = $.Event("storage");
196 | // simulate a storage event for this timer's sync ID
197 | e.originalEvent = {
198 | key: "timer-storage-event-test",
199 | oldValue: "1",
200 | newValue: "2"
201 | };
202 | $(window).trigger(e);
203 | }, 100 );
204 | });
205 |
206 | /*
207 | Need to actually test pause/resume/reset, not just thier return type
208 | */
209 | module("Functional");
210 | asyncTest("Pause works and is a jQuery instance", 4, function () {
211 |
212 | $.idleTimer(100);
213 | equal(typeof $.idleTimer( "pause" ).jquery , "string", "pause should be jquery" );
214 |
215 | $.idleTimer("resume");
216 | equal(typeof $(document).idleTimer("pause").jquery, "string", "pause should be jquery");
217 |
218 | setTimeout(function () {
219 | ok(!$.idleTimer("isIdle"), "timer still active");
220 | ok(!$(document).idleTimer("isIdle"), "timer still active");
221 |
222 | $.idleTimer("destroy");
223 | $(document).off();
224 |
225 | start();
226 | }, 200);
227 | });
228 |
229 |
230 |
231 | asyncTest("Resume works and is a jQuery instance", 4, function () {
232 |
233 | $.idleTimer(100);
234 |
235 | $.idleTimer("pause");
236 | equal(typeof $.idleTimer("resume").jquery, "string", "resume should be jquery");
237 |
238 | $.idleTimer("pause");
239 | equal(typeof $(document).idleTimer("resume").jquery, "string", "resume should be jquery");
240 |
241 | setTimeout(function () {
242 | ok($.idleTimer("isIdle"), "timer inactive");
243 | ok($(document).idleTimer("isIdle"), "timer inactive");
244 |
245 | $.idleTimer("destroy");
246 | $(document).off();
247 |
248 | start();
249 | }, 200);
250 | });
251 |
252 | asyncTest("Resume works and is a jQuery instance when using unique id", 4, function () {
253 |
254 | $.idleTimer(100, document, "someUniqueString");
255 |
256 | $.idleTimer("pause");
257 | equal(typeof $.idleTimer("resume", document, "someUniqueString").jquery, "string", "resume should be jquery");
258 |
259 | $.idleTimer("pause");
260 | equal(typeof $(document).idleTimer("resume", "someUniqueString").jquery, "string", "resume should be jquery");
261 |
262 | setTimeout(function () {
263 | ok($.idleTimer("isIdle", document, "someUniqueString"), "timer inactive");
264 | ok($(document).idleTimer("isIdle", "someUniqueString"), "timer inactive");
265 |
266 | $.idleTimer("destroy", "someUniqueString");
267 | $(document).off();
268 |
269 | start();
270 | }, 200);
271 | });
272 |
273 | test("Elapsed time is a number", 2, function () {
274 |
275 | $.idleTimer(100);
276 |
277 | equal(typeof $.idleTimer("getElapsedTime"), "number", "Elapsed time should be a number");
278 | equal(typeof $(document).idleTimer("getElapsedTime"), "number", "Elapsed time should be a number");
279 | });
280 |
281 | test("Init works and is a jQuery instance", 4, function () {
282 |
283 | equal(typeof $.idleTimer(100).jquery, "string", "Init should be jquery");
284 | equal(typeof $("#qunit-fixture").idleTimer(100).jquery, "string", "Destroy should be jquery");
285 |
286 | equal(typeof $(document).data("idleTimerObj").idle, "boolean", "Init data added");
287 | equal(typeof $("#qunit-fixture").data("idleTimerObj").idle, "boolean", "Init data added");
288 | });
289 |
290 | test("Destroy works and is a jQuery instance", 4, function () {
291 |
292 | $.idleTimer(100);
293 | $("#qunit-fixture").idleTimer(100);
294 |
295 | equal(typeof $.idleTimer("destroy").jquery, "string", "Destroy should be jquery");
296 | equal(typeof $("#qunit-fixture").idleTimer("destroy").jquery, "string", "Destroy should be jquery");
297 |
298 | equal(typeof $(document).data("idleTimerObj"), "undefined", "destroy removed data");
299 | equal(typeof $("#qunit-fixture").data("idleTimerObj"), "undefined", "destroy removed data");
300 | });
301 |
302 | asyncTest("Reset is a jQuery instance", 6, function () {
303 |
304 | //start the timer
305 | $.idleTimer(200);
306 | $.idleTimer("pause");
307 | $("#qunit-fixture").idleTimer(200);
308 | $("#qunit-fixture").idleTimer("pause");
309 |
310 | //After a bit, reset it
311 | setTimeout(function () {
312 | equal(typeof $.idleTimer("reset").jquery, "string", "reset should be jquery");
313 | equal(typeof $("#qunit-fixture").idleTimer("reset").jquery, "string", "reset should be jquery");
314 |
315 | ok($(document).data("idleTimerObj").remaining===null, "reset remaining");
316 | ok($("#qunit-fixture").data("idleTimerObj").remaining === null, "reset remaining");
317 | }, 100);
318 |
319 | setTimeout(function () {
320 | ok($.idleTimer("isIdle"), "timer inactive");
321 | ok($("#qunit-fixture").idleTimer("isIdle"), "timer inactive");
322 |
323 | $.idleTimer("destroy");
324 | $("#qunit-fixture").idleTimer("destroy");
325 | $(document).off();
326 |
327 | start();
328 | }, 400);
329 |
330 | });
331 |
332 | test("Last Active time is a number", 2, function () {
333 |
334 | $.idleTimer(100);
335 |
336 | equal(typeof $.idleTimer("getLastActiveTime"), "number", "Last Active time should be a number");
337 | equal(typeof $(document).idleTimer("getLastActiveTime"), "number", "Last Active time should be a number");
338 |
339 | $.idleTimer("destroy");
340 | });
341 |
342 | test("Remaining time is a number", 2, function () {
343 |
344 | $.idleTimer(100);
345 |
346 | equal( typeof $.idleTimer( "getRemainingTime" ), "number", "Remaining time should be a number" );
347 | equal(typeof $(document).idleTimer("getRemainingTime"), "number", "Remaining time should be a number");
348 |
349 | $.idleTimer("destroy");
350 | });
351 | test("isIdle is a boolean", 2, function () {
352 |
353 | $.idleTimer(100);
354 |
355 | equal(typeof $.idleTimer("isIdle"), "boolean", "isIdle should be a boolean");
356 | equal(typeof $(document).idleTimer("isIdle"), "boolean", "isIdle should be a boolean");
357 |
358 | $.idleTimer("destroy");
359 | });
360 |
361 | }(jQuery));
362 |
--------------------------------------------------------------------------------
/dist/idle-timer.1.1.0.js:
--------------------------------------------------------------------------------
1 | /*! Idle Timer - v1.1.0 - 2016-03-21
2 | * https://github.com/thorst/jquery-idletimer
3 | * Copyright (c) 2016 Paul Irish; Licensed MIT */
4 | /*
5 | mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
6 | DOMMouseScroll (deprecated) -> Firefox 1.0
7 | wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
8 |
9 | //No need to use, use DOMMouseScroll
10 | MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
11 |
12 | //Events
13 | WheelEvent -> see wheel
14 | MouseWheelEvent -> see mousewheel
15 | MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
16 | */
17 | (function ($) {
18 |
19 | $.idleTimer = function (firstParam, elem) {
20 | var opts;
21 | if ( typeof firstParam === "object" ) {
22 | opts = firstParam;
23 | firstParam = null;
24 | } else if (typeof firstParam === "number") {
25 | opts = { timeout: firstParam };
26 | firstParam = null;
27 | }
28 |
29 | // element to watch
30 | elem = elem || document;
31 |
32 | // defaults that are to be stored as instance props on the elem
33 | opts = $.extend({
34 | idle: false, // indicates if the user is idle
35 | timeout: 30000, // the amount of time (ms) before the user is considered idle
36 | events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
37 | }, opts);
38 |
39 | var jqElem = $(elem),
40 | obj = jqElem.data("idleTimerObj") || {},
41 |
42 | /* (intentionally not documented)
43 | * Toggles the idle state and fires an appropriate event.
44 | * @return {void}
45 | */
46 | toggleIdleState = function (e) {
47 | var obj = $.data(elem, "idleTimerObj") || {};
48 |
49 | // toggle the state
50 | obj.idle = !obj.idle;
51 |
52 | // store toggle state date time
53 | obj.olddate = +new Date();
54 |
55 | // create a custom event, with state and name space
56 | var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer");
57 |
58 | // trigger event on object with elem and copy of obj
59 | $(elem).trigger(event, [elem, $.extend({}, obj), e]);
60 | },
61 | /**
62 | * Handle event triggers
63 | * @return {void}
64 | * @method event
65 | * @static
66 | */
67 | handleEvent = function (e) {
68 | var obj = $.data(elem, "idleTimerObj") || {};
69 |
70 | if (e.type === "storage" && e.originalEvent.key !== obj.timerSyncId) {
71 | return;
72 | }
73 |
74 | // this is already paused, ignore events for now
75 | if (obj.remaining != null) { return; }
76 |
77 | /*
78 | mousemove is kinda buggy, it can be triggered when it should be idle.
79 | Typically is happening between 115 - 150 milliseconds after idle triggered.
80 | @psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
81 | @thorst has similar issues on ios7 "after $.scrollTop() on text area"
82 | */
83 | if (e.type === "mousemove") {
84 | // if coord are same, it didn't move
85 | if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
86 | return;
87 | }
88 | // if coord don't exist how could it move
89 | if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
90 | return;
91 | }
92 | // under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
93 | var elapsed = (+new Date()) - obj.olddate;
94 | if (elapsed < 200) {
95 | return;
96 | }
97 | }
98 |
99 | // clear any existing timeout
100 | clearTimeout(obj.tId);
101 |
102 | // if the idle timer is enabled, flip
103 | if (obj.idle) {
104 | toggleIdleState(e);
105 | }
106 |
107 | // store when user was last active
108 | obj.lastActive = +new Date();
109 |
110 | // update mouse coord
111 | obj.pageX = e.pageX;
112 | obj.pageY = e.pageY;
113 |
114 | // sync lastActive
115 | if (e.type !== "storage" && obj.timerSyncId) {
116 | if (typeof(localStorage) !== "undefined") {
117 | localStorage.setItem(obj.timerSyncId, obj.lastActive);
118 | }
119 | }
120 |
121 | // set a new timeout
122 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
123 | },
124 | /**
125 | * Restore initial settings and restart timer
126 | * @return {void}
127 | * @method reset
128 | * @static
129 | */
130 | reset = function () {
131 |
132 | var obj = $.data(elem, "idleTimerObj") || {};
133 |
134 | // reset settings
135 | obj.idle = obj.idleBackup;
136 | obj.olddate = +new Date();
137 | obj.lastActive = obj.olddate;
138 | obj.remaining = null;
139 |
140 | // reset Timers
141 | clearTimeout(obj.tId);
142 | if (!obj.idle) {
143 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
144 | }
145 |
146 | },
147 | /**
148 | * Store remaining time, stop timer
149 | * You can pause from an idle OR active state
150 | * @return {void}
151 | * @method pause
152 | * @static
153 | */
154 | pause = function () {
155 |
156 | var obj = $.data(elem, "idleTimerObj") || {};
157 |
158 | // this is already paused
159 | if ( obj.remaining != null ) { return; }
160 |
161 | // define how much is left on the timer
162 | obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
163 |
164 | // clear any existing timeout
165 | clearTimeout(obj.tId);
166 | },
167 | /**
168 | * Start timer with remaining value
169 | * @return {void}
170 | * @method resume
171 | * @static
172 | */
173 | resume = function () {
174 |
175 | var obj = $.data(elem, "idleTimerObj") || {};
176 |
177 | // this isn't paused yet
178 | if ( obj.remaining == null ) { return; }
179 |
180 | // start timer
181 | if ( !obj.idle ) {
182 | obj.tId = setTimeout(toggleIdleState, obj.remaining);
183 | }
184 |
185 | // clear remaining
186 | obj.remaining = null;
187 | },
188 | /**
189 | * Stops the idle timer. This removes appropriate event handlers
190 | * and cancels any pending timeouts.
191 | * @return {void}
192 | * @method destroy
193 | * @static
194 | */
195 | destroy = function () {
196 |
197 | var obj = $.data(elem, "idleTimerObj") || {};
198 |
199 | //clear any pending timeouts
200 | clearTimeout(obj.tId);
201 |
202 | //Remove data
203 | jqElem.removeData("idleTimerObj");
204 |
205 | //detach the event handlers
206 | jqElem.off("._idleTimer");
207 | },
208 | /**
209 | * Returns the time until becoming idle
210 | * @return {number}
211 | * @method remainingtime
212 | * @static
213 | */
214 | remainingtime = function () {
215 |
216 | var obj = $.data(elem, "idleTimerObj") || {};
217 |
218 | //If idle there is no time remaining
219 | if ( obj.idle ) { return 0; }
220 |
221 | //If its paused just return that
222 | if ( obj.remaining != null ) { return obj.remaining; }
223 |
224 | //Determine remaining, if negative idle didn't finish flipping, just return 0
225 | var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
226 | if (remaining < 0) { remaining = 0; }
227 |
228 | //If this is paused return that number, else return current remaining
229 | return remaining;
230 | };
231 |
232 |
233 | // determine which function to call
234 | if (firstParam === null && typeof obj.idle !== "undefined") {
235 | // they think they want to init, but it already is, just reset
236 | reset();
237 | return jqElem;
238 | } else if (firstParam === null) {
239 | // they want to init
240 | } else if (firstParam !== null && typeof obj.idle === "undefined") {
241 | // they want to do something, but it isnt init
242 | // not sure the best way to handle this
243 | return false;
244 | } else if (firstParam === "destroy") {
245 | destroy();
246 | return jqElem;
247 | } else if (firstParam === "pause") {
248 | pause();
249 | return jqElem;
250 | } else if (firstParam === "resume") {
251 | resume();
252 | return jqElem;
253 | } else if (firstParam === "reset") {
254 | reset();
255 | return jqElem;
256 | } else if (firstParam === "getRemainingTime") {
257 | return remainingtime();
258 | } else if (firstParam === "getElapsedTime") {
259 | return (+new Date()) - obj.olddate;
260 | } else if (firstParam === "getLastActiveTime") {
261 | return obj.lastActive;
262 | } else if (firstParam === "isIdle") {
263 | return obj.idle;
264 | }
265 |
266 | /* (intentionally not documented)
267 | * Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
268 | * @param {Event} event A DOM2-normalized event object.
269 | * @return {void}
270 | */
271 | jqElem.on($.trim((opts.events + " ").split(" ").join("._idleTimer ")), function (e) {
272 | handleEvent(e);
273 | });
274 |
275 | if (opts.timerSyncId) {
276 | $(window).bind("storage", handleEvent);
277 | }
278 |
279 | // Internal Object Properties, This isn't all necessary, but we
280 | // explicitly define all keys here so we know what we are working with
281 | obj = $.extend({}, {
282 | olddate : +new Date(), // the last time state changed
283 | lastActive: +new Date(), // the last time timer was active
284 | idle : opts.idle, // current state
285 | idleBackup : opts.idle, // backup of idle parameter since it gets modified
286 | timeout : opts.timeout, // the interval to change state
287 | remaining : null, // how long until state changes
288 | timerSyncId : opts.timerSyncId, // localStorage key to use for syncing this timer
289 | tId : null, // the idle timer setTimeout
290 | pageX : null, // used to store the mouse coord
291 | pageY : null
292 | });
293 |
294 | // set a timeout to toggle state. May wish to omit this in some situations
295 | if (!obj.idle) {
296 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
297 | }
298 |
299 | // store our instance on the object
300 | $.data(elem, "idleTimerObj", obj);
301 |
302 | return jqElem;
303 | };
304 |
305 | // This allows binding to element
306 | $.fn.idleTimer = function (firstParam) {
307 | if (this[0]) {
308 | return $.idleTimer(firstParam, this[0]);
309 | }
310 |
311 | return this;
312 | };
313 |
314 | })(jQuery);
315 |
--------------------------------------------------------------------------------
/dist/idle-timer.js:
--------------------------------------------------------------------------------
1 | /*! Idle Timer - v1.1.1 - 2020-06-25
2 | * https://github.com/thorst/jquery-idletimer
3 | * Copyright (c) 2020 Paul Irish; Licensed MIT */
4 | /*
5 | mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
6 | DOMMouseScroll (deprecated) -> Firefox 1.0
7 | wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
8 |
9 | //No need to use, use DOMMouseScroll
10 | MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
11 |
12 | //Events
13 | WheelEvent -> see wheel
14 | MouseWheelEvent -> see mousewheel
15 | MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
16 | */
17 | (function ($) {
18 |
19 | $.idleTimer = function (firstParam, elem) {
20 | var opts;
21 | if ( typeof firstParam === "object" ) {
22 | opts = firstParam;
23 | firstParam = null;
24 | } else if (typeof firstParam === "number") {
25 | opts = { timeout: firstParam };
26 | firstParam = null;
27 | }
28 |
29 | // element to watch
30 | elem = elem || document;
31 |
32 | // defaults that are to be stored as instance props on the elem
33 | opts = $.extend({
34 | idle: false, // indicates if the user is idle
35 | timeout: 30000, // the amount of time (ms) before the user is considered idle
36 | events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
37 | }, opts);
38 |
39 | var jqElem = $(elem),
40 | obj = jqElem.data("idleTimerObj") || {},
41 |
42 | /* (intentionally not documented)
43 | * Toggles the idle state and fires an appropriate event.
44 | * @return {void}
45 | */
46 | toggleIdleState = function (e) {
47 | var obj = $.data(elem, "idleTimerObj") || {};
48 |
49 | // toggle the state
50 | obj.idle = !obj.idle;
51 |
52 | // store toggle state date time
53 | obj.olddate = +new Date();
54 |
55 | // create a custom event, with state and name space
56 | var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer");
57 |
58 | // trigger event on object with elem and copy of obj
59 | $(elem).trigger(event, [elem, $.extend({}, obj), e]);
60 | },
61 | /**
62 | * Handle event triggers
63 | * @return {void}
64 | * @method event
65 | * @static
66 | */
67 | handleEvent = function (e) {
68 | var obj = $.data(elem, "idleTimerObj") || {};
69 |
70 | // ignore writting to storage unless related to idleTimer
71 | if (e.type === "storage" && e.originalEvent.key !== obj.timerSyncId) {
72 | return;
73 | }
74 |
75 | // this is already paused, ignore events for now
76 | if (obj.remaining != null) { return; }
77 |
78 | /*
79 | mousemove is kinda buggy, it can be triggered when it should be idle.
80 | Typically is happening between 115 - 150 milliseconds after idle triggered.
81 | @psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
82 | @thorst has similar issues on ios7 "after $.scrollTop() on text area"
83 | */
84 | if (e.type === "mousemove") {
85 | // if coord are same, it didn't move
86 | if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
87 | return;
88 | }
89 | // if coord don't exist how could it move
90 | if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
91 | return;
92 | }
93 | // under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
94 | var elapsed = (+new Date()) - obj.olddate;
95 | if (elapsed < 200) {
96 | return;
97 | }
98 | }
99 |
100 | // clear any existing timeout
101 | clearTimeout(obj.tId);
102 |
103 | // if the idle timer is enabled, flip
104 | if (obj.idle) {
105 | toggleIdleState(e);
106 | }
107 |
108 | // store when user was last active
109 | obj.lastActive = +new Date();
110 |
111 | // update mouse coord
112 | obj.pageX = e.pageX;
113 | obj.pageY = e.pageY;
114 |
115 | // sync lastActive
116 | if (e.type !== "storage" && obj.timerSyncId) {
117 | if (typeof(localStorage) !== "undefined") {
118 | localStorage.setItem(obj.timerSyncId, obj.lastActive);
119 | }
120 | }
121 |
122 | // set a new timeout
123 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
124 | },
125 | /**
126 | * Restore initial settings and restart timer
127 | * @return {void}
128 | * @method reset
129 | * @static
130 | */
131 | reset = function () {
132 |
133 | var obj = $.data(elem, "idleTimerObj") || {};
134 |
135 | // reset settings
136 | obj.idle = obj.idleBackup;
137 | obj.olddate = +new Date();
138 | obj.lastActive = obj.olddate;
139 | obj.remaining = null;
140 |
141 | // reset Timers
142 | clearTimeout(obj.tId);
143 | if (!obj.idle) {
144 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
145 | }
146 |
147 | },
148 | /**
149 | * Store remaining time, stop timer
150 | * You can pause from an idle OR active state
151 | * @return {void}
152 | * @method pause
153 | * @static
154 | */
155 | pause = function () {
156 |
157 | var obj = $.data(elem, "idleTimerObj") || {};
158 |
159 | // this is already paused
160 | if ( obj.remaining != null ) { return; }
161 |
162 | // define how much is left on the timer
163 | obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
164 |
165 | // clear any existing timeout
166 | clearTimeout(obj.tId);
167 | },
168 | /**
169 | * Start timer with remaining value
170 | * @return {void}
171 | * @method resume
172 | * @static
173 | */
174 | resume = function () {
175 |
176 | var obj = $.data(elem, "idleTimerObj") || {};
177 |
178 | // this isn't paused yet
179 | if ( obj.remaining == null ) { return; }
180 |
181 | // start timer
182 | if ( !obj.idle ) {
183 | obj.tId = setTimeout(toggleIdleState, obj.remaining);
184 | }
185 |
186 | // clear remaining
187 | obj.remaining = null;
188 | },
189 | /**
190 | * Stops the idle timer. This removes appropriate event handlers
191 | * and cancels any pending timeouts.
192 | * @return {void}
193 | * @method destroy
194 | * @static
195 | */
196 | destroy = function () {
197 |
198 | var obj = $.data(elem, "idleTimerObj") || {};
199 |
200 | //clear any pending timeouts
201 | clearTimeout(obj.tId);
202 |
203 | //Remove data
204 | jqElem.removeData("idleTimerObj");
205 |
206 | //detach the event handlers
207 | jqElem.off("._idleTimer");
208 | },
209 | /**
210 | * Returns the time until becoming idle
211 | * @return {number}
212 | * @method remainingtime
213 | * @static
214 | */
215 | remainingtime = function () {
216 |
217 | var obj = $.data(elem, "idleTimerObj") || {};
218 |
219 | //If idle there is no time remaining
220 | if ( obj.idle ) { return 0; }
221 |
222 | //If its paused just return that
223 | if ( obj.remaining != null ) { return obj.remaining; }
224 |
225 | //Determine remaining, if negative idle didn't finish flipping, just return 0
226 | var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
227 | if (remaining < 0) { remaining = 0; }
228 |
229 | //If this is paused return that number, else return current remaining
230 | return remaining;
231 | };
232 |
233 |
234 | // determine which function to call
235 | if (firstParam === null && typeof obj.idle !== "undefined") {
236 | // they think they want to init, but it already is, just reset
237 | reset();
238 | return jqElem;
239 | } else if (firstParam === null) {
240 | // they want to init
241 | } else if (firstParam !== null && typeof obj.idle === "undefined") {
242 | // they want to do something, but it isnt init
243 | // not sure the best way to handle this
244 | return false;
245 | } else if (firstParam === "destroy") {
246 | destroy();
247 | return jqElem;
248 | } else if (firstParam === "pause") {
249 | pause();
250 | return jqElem;
251 | } else if (firstParam === "resume") {
252 | resume();
253 | return jqElem;
254 | } else if (firstParam === "reset") {
255 | reset();
256 | return jqElem;
257 | } else if (firstParam === "getRemainingTime") {
258 | return remainingtime();
259 | } else if (firstParam === "getElapsedTime") {
260 | return (+new Date()) - obj.olddate;
261 | } else if (firstParam === "getLastActiveTime") {
262 | return obj.lastActive;
263 | } else if (firstParam === "isIdle") {
264 | return obj.idle;
265 | }
266 |
267 | // Test via a getter in the options object to see if the passive property is accessed
268 | // This isnt working in jquery, though is planned for 4.0
269 | // https://github.com/jquery/jquery/issues/2871
270 | /*var supportsPassive = false;
271 | try {
272 | var Popts = Object.defineProperty({}, "passive", {
273 | get: function() {
274 | supportsPassive = true;
275 | }
276 | });
277 | window.addEventListener("test", null, Popts);
278 | } catch (e) {}
279 | */
280 |
281 | /* (intentionally not documented)
282 | * Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
283 | * @param {Event} event A DOM2-normalized event object.
284 | * @return {void}
285 | */
286 | jqElem.on((opts.events + " ").split(" ").join("._idleTimer ").trim(), function (e) {
287 | handleEvent(e);
288 | });
289 | //}, supportsPassive ? { passive: true } : false);
290 |
291 | if (opts.timerSyncId) {
292 | $(window).on("storage", handleEvent);
293 | }
294 |
295 | // Internal Object Properties, This isn't all necessary, but we
296 | // explicitly define all keys here so we know what we are working with
297 | obj = $.extend({}, {
298 | olddate : +new Date(), // the last time state changed
299 | lastActive: +new Date(), // the last time timer was active
300 | idle : opts.idle, // current state
301 | idleBackup : opts.idle, // backup of idle parameter since it gets modified
302 | timeout : opts.timeout, // the interval to change state
303 | remaining : null, // how long until state changes
304 | timerSyncId : opts.timerSyncId, // localStorage key to use for syncing this timer
305 | tId : null, // the idle timer setTimeout
306 | pageX : null, // used to store the mouse coord
307 | pageY : null
308 | });
309 |
310 | // set a timeout to toggle state. May wish to omit this in some situations
311 | if (!obj.idle) {
312 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
313 | }
314 |
315 | // store our instance on the object
316 | $.data(elem, "idleTimerObj", obj);
317 |
318 | return jqElem;
319 | };
320 |
321 | // This allows binding to element
322 | $.fn.idleTimer = function (firstParam) {
323 | if (this[0]) {
324 | return $.idleTimer(firstParam, this[0]);
325 | }
326 |
327 | return this;
328 | };
329 |
330 | })(jQuery);
331 |
--------------------------------------------------------------------------------
/dist/idle-timer.1.1.1.js:
--------------------------------------------------------------------------------
1 | /*! Idle Timer - v1.1.1 - 2020-06-25
2 | * https://github.com/thorst/jquery-idletimer
3 | * Copyright (c) 2020 Paul Irish; Licensed MIT */
4 | /*
5 | mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
6 | DOMMouseScroll (deprecated) -> Firefox 1.0
7 | wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
8 |
9 | //No need to use, use DOMMouseScroll
10 | MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
11 |
12 | //Events
13 | WheelEvent -> see wheel
14 | MouseWheelEvent -> see mousewheel
15 | MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
16 | */
17 | (function ($) {
18 |
19 | $.idleTimer = function (firstParam, elem) {
20 | var opts;
21 | if ( typeof firstParam === "object" ) {
22 | opts = firstParam;
23 | firstParam = null;
24 | } else if (typeof firstParam === "number") {
25 | opts = { timeout: firstParam };
26 | firstParam = null;
27 | }
28 |
29 | // element to watch
30 | elem = elem || document;
31 |
32 | // defaults that are to be stored as instance props on the elem
33 | opts = $.extend({
34 | idle: false, // indicates if the user is idle
35 | timeout: 30000, // the amount of time (ms) before the user is considered idle
36 | events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
37 | }, opts);
38 |
39 | var jqElem = $(elem),
40 | obj = jqElem.data("idleTimerObj") || {},
41 |
42 | /* (intentionally not documented)
43 | * Toggles the idle state and fires an appropriate event.
44 | * @return {void}
45 | */
46 | toggleIdleState = function (e) {
47 | var obj = $.data(elem, "idleTimerObj") || {};
48 |
49 | // toggle the state
50 | obj.idle = !obj.idle;
51 |
52 | // store toggle state date time
53 | obj.olddate = +new Date();
54 |
55 | // create a custom event, with state and name space
56 | var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer");
57 |
58 | // trigger event on object with elem and copy of obj
59 | $(elem).trigger(event, [elem, $.extend({}, obj), e]);
60 | },
61 | /**
62 | * Handle event triggers
63 | * @return {void}
64 | * @method event
65 | * @static
66 | */
67 | handleEvent = function (e) {
68 | var obj = $.data(elem, "idleTimerObj") || {};
69 |
70 | // ignore writting to storage unless related to idleTimer
71 | if (e.type === "storage" && e.originalEvent.key !== obj.timerSyncId) {
72 | return;
73 | }
74 |
75 | // this is already paused, ignore events for now
76 | if (obj.remaining != null) { return; }
77 |
78 | /*
79 | mousemove is kinda buggy, it can be triggered when it should be idle.
80 | Typically is happening between 115 - 150 milliseconds after idle triggered.
81 | @psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
82 | @thorst has similar issues on ios7 "after $.scrollTop() on text area"
83 | */
84 | if (e.type === "mousemove") {
85 | // if coord are same, it didn't move
86 | if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
87 | return;
88 | }
89 | // if coord don't exist how could it move
90 | if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
91 | return;
92 | }
93 | // under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
94 | var elapsed = (+new Date()) - obj.olddate;
95 | if (elapsed < 200) {
96 | return;
97 | }
98 | }
99 |
100 | // clear any existing timeout
101 | clearTimeout(obj.tId);
102 |
103 | // if the idle timer is enabled, flip
104 | if (obj.idle) {
105 | toggleIdleState(e);
106 | }
107 |
108 | // store when user was last active
109 | obj.lastActive = +new Date();
110 |
111 | // update mouse coord
112 | obj.pageX = e.pageX;
113 | obj.pageY = e.pageY;
114 |
115 | // sync lastActive
116 | if (e.type !== "storage" && obj.timerSyncId) {
117 | if (typeof(localStorage) !== "undefined") {
118 | localStorage.setItem(obj.timerSyncId, obj.lastActive);
119 | }
120 | }
121 |
122 | // set a new timeout
123 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
124 | },
125 | /**
126 | * Restore initial settings and restart timer
127 | * @return {void}
128 | * @method reset
129 | * @static
130 | */
131 | reset = function () {
132 |
133 | var obj = $.data(elem, "idleTimerObj") || {};
134 |
135 | // reset settings
136 | obj.idle = obj.idleBackup;
137 | obj.olddate = +new Date();
138 | obj.lastActive = obj.olddate;
139 | obj.remaining = null;
140 |
141 | // reset Timers
142 | clearTimeout(obj.tId);
143 | if (!obj.idle) {
144 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
145 | }
146 |
147 | },
148 | /**
149 | * Store remaining time, stop timer
150 | * You can pause from an idle OR active state
151 | * @return {void}
152 | * @method pause
153 | * @static
154 | */
155 | pause = function () {
156 |
157 | var obj = $.data(elem, "idleTimerObj") || {};
158 |
159 | // this is already paused
160 | if ( obj.remaining != null ) { return; }
161 |
162 | // define how much is left on the timer
163 | obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
164 |
165 | // clear any existing timeout
166 | clearTimeout(obj.tId);
167 | },
168 | /**
169 | * Start timer with remaining value
170 | * @return {void}
171 | * @method resume
172 | * @static
173 | */
174 | resume = function () {
175 |
176 | var obj = $.data(elem, "idleTimerObj") || {};
177 |
178 | // this isn't paused yet
179 | if ( obj.remaining == null ) { return; }
180 |
181 | // start timer
182 | if ( !obj.idle ) {
183 | obj.tId = setTimeout(toggleIdleState, obj.remaining);
184 | }
185 |
186 | // clear remaining
187 | obj.remaining = null;
188 | },
189 | /**
190 | * Stops the idle timer. This removes appropriate event handlers
191 | * and cancels any pending timeouts.
192 | * @return {void}
193 | * @method destroy
194 | * @static
195 | */
196 | destroy = function () {
197 |
198 | var obj = $.data(elem, "idleTimerObj") || {};
199 |
200 | //clear any pending timeouts
201 | clearTimeout(obj.tId);
202 |
203 | //Remove data
204 | jqElem.removeData("idleTimerObj");
205 |
206 | //detach the event handlers
207 | jqElem.off("._idleTimer");
208 | },
209 | /**
210 | * Returns the time until becoming idle
211 | * @return {number}
212 | * @method remainingtime
213 | * @static
214 | */
215 | remainingtime = function () {
216 |
217 | var obj = $.data(elem, "idleTimerObj") || {};
218 |
219 | //If idle there is no time remaining
220 | if ( obj.idle ) { return 0; }
221 |
222 | //If its paused just return that
223 | if ( obj.remaining != null ) { return obj.remaining; }
224 |
225 | //Determine remaining, if negative idle didn't finish flipping, just return 0
226 | var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
227 | if (remaining < 0) { remaining = 0; }
228 |
229 | //If this is paused return that number, else return current remaining
230 | return remaining;
231 | };
232 |
233 |
234 | // determine which function to call
235 | if (firstParam === null && typeof obj.idle !== "undefined") {
236 | // they think they want to init, but it already is, just reset
237 | reset();
238 | return jqElem;
239 | } else if (firstParam === null) {
240 | // they want to init
241 | } else if (firstParam !== null && typeof obj.idle === "undefined") {
242 | // they want to do something, but it isnt init
243 | // not sure the best way to handle this
244 | return false;
245 | } else if (firstParam === "destroy") {
246 | destroy();
247 | return jqElem;
248 | } else if (firstParam === "pause") {
249 | pause();
250 | return jqElem;
251 | } else if (firstParam === "resume") {
252 | resume();
253 | return jqElem;
254 | } else if (firstParam === "reset") {
255 | reset();
256 | return jqElem;
257 | } else if (firstParam === "getRemainingTime") {
258 | return remainingtime();
259 | } else if (firstParam === "getElapsedTime") {
260 | return (+new Date()) - obj.olddate;
261 | } else if (firstParam === "getLastActiveTime") {
262 | return obj.lastActive;
263 | } else if (firstParam === "isIdle") {
264 | return obj.idle;
265 | }
266 |
267 | // Test via a getter in the options object to see if the passive property is accessed
268 | // This isnt working in jquery, though is planned for 4.0
269 | // https://github.com/jquery/jquery/issues/2871
270 | /*var supportsPassive = false;
271 | try {
272 | var Popts = Object.defineProperty({}, "passive", {
273 | get: function() {
274 | supportsPassive = true;
275 | }
276 | });
277 | window.addEventListener("test", null, Popts);
278 | } catch (e) {}
279 | */
280 |
281 | /* (intentionally not documented)
282 | * Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
283 | * @param {Event} event A DOM2-normalized event object.
284 | * @return {void}
285 | */
286 | jqElem.on((opts.events + " ").split(" ").join("._idleTimer ").trim(), function (e) {
287 | handleEvent(e);
288 | });
289 | //}, supportsPassive ? { passive: true } : false);
290 |
291 | if (opts.timerSyncId) {
292 | $(window).on("storage", handleEvent);
293 | }
294 |
295 | // Internal Object Properties, This isn't all necessary, but we
296 | // explicitly define all keys here so we know what we are working with
297 | obj = $.extend({}, {
298 | olddate : +new Date(), // the last time state changed
299 | lastActive: +new Date(), // the last time timer was active
300 | idle : opts.idle, // current state
301 | idleBackup : opts.idle, // backup of idle parameter since it gets modified
302 | timeout : opts.timeout, // the interval to change state
303 | remaining : null, // how long until state changes
304 | timerSyncId : opts.timerSyncId, // localStorage key to use for syncing this timer
305 | tId : null, // the idle timer setTimeout
306 | pageX : null, // used to store the mouse coord
307 | pageY : null
308 | });
309 |
310 | // set a timeout to toggle state. May wish to omit this in some situations
311 | if (!obj.idle) {
312 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
313 | }
314 |
315 | // store our instance on the object
316 | $.data(elem, "idleTimerObj", obj);
317 |
318 | return jqElem;
319 | };
320 |
321 | // This allows binding to element
322 | $.fn.idleTimer = function (firstParam) {
323 | if (this[0]) {
324 | return $.idleTimer(firstParam, this[0]);
325 | }
326 |
327 | return this;
328 | };
329 |
330 | })(jQuery);
331 |
--------------------------------------------------------------------------------
/demos/autologoutsync.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Jquery-idletimer
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
26 |
27 |
28 |
29 |
50 |
51 |
Concept
52 |
53 | Wait 10 seconds, you will see a expiring warning. Wait 10 more seconds and you will see that you have been logged out.
54 |
55 |
56 | In the real world I forward them to the logout url, which intern fowards them to login screen, instead of showing the 2nd dialog. You can modify the app.session.logout function.
57 |
58 |
59 | We could use the active.idleTimer event to clearTimeout, however I prefer the user to explicitly say they want to keep the session open by clicking ok, not just moving the mouse on the screen.
60 |
61 |
62 | This demo takes into account when a mobile device closes the browser, and after the idle timeout expires, launches the browser again. Instead of displaying the warning, it will jump straight to the logged out dialog.
63 |
64 |
65 | For this demo we've enabled localStorage to sync accross tabs of the same browser. This will keep the client side happy, but we still need a keepAlive service to keep the server side session active.
66 |
67 |
68 | For the sake of complete demo, I've included the code needed to call a keepalive url to keep the server side session valid.
69 |
70 |
71 |
72 |
73 |
74 |
75 |
Session Expiration Warning
76 |
77 |
78 |
79 |
You've been inactive for a while. For your security, we'll log you out automatically. Click "Stay Online" to continue your session.
80 |
Your session will expire in 120 seconds.
81 |
82 |
83 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
You have been logged out
97 |
98 |
99 |
Your session has expired.
100 |
101 |
102 |
103 |
104 |
105 |
248 |
249 |
250 |
251 |
--------------------------------------------------------------------------------
/src/idle-timer.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2009 Nicholas C. Zakas
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | */
22 | /*
23 | mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
24 | DOMMouseScroll (deprecated) -> Firefox 1.0
25 | wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
26 |
27 | //No need to use, use DOMMouseScroll
28 | MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
29 |
30 | //Events
31 | WheelEvent -> see wheel
32 | MouseWheelEvent -> see mousewheel
33 | MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
34 | */
35 | (function ($) {
36 |
37 | $.idleTimer = function (firstParam, elem, uniqueId) {
38 | var opts;
39 | if ( typeof firstParam === "object" ) {
40 | opts = firstParam;
41 | firstParam = null;
42 | } else if (typeof firstParam === "number") {
43 | opts = { timeout: firstParam };
44 | firstParam = null;
45 | }
46 |
47 | // element to watch
48 | elem = elem || document;
49 |
50 | uniqueId = uniqueId || "";
51 |
52 | // defaults that are to be stored as instance props on the elem
53 | opts = $.extend({
54 | idle: false, // indicates if the user is idle
55 | timeout: 30000, // the amount of time (ms) before the user is considered idle
56 | events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
57 | }, opts);
58 |
59 | var jqElem = $(elem),
60 | obj = jqElem.data("idleTimerObj" + uniqueId) || {},
61 |
62 | /* (intentionally not documented)
63 | * Toggles the idle state and fires an appropriate event.
64 | * @return {void}
65 | */
66 | toggleIdleState = function (e) {
67 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
68 |
69 | // toggle the state
70 | obj.idle = !obj.idle;
71 |
72 | // store toggle state date time
73 | obj.olddate = +new Date();
74 |
75 | // create a custom event, with state and name space
76 | var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer" + uniqueId);
77 |
78 | // trigger event on object with elem and copy of obj
79 | $(elem).trigger(event, [elem, $.extend({}, obj), e]);
80 | },
81 | /**
82 | * Handle event triggers
83 | * @return {void}
84 | * @method event
85 | * @static
86 | */
87 | handleEvent = function (e) {
88 |
89 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
90 |
91 | // ignore writting to storage unless related to idleTimer
92 | if (e.type === "storage" && e.originalEvent.key !== obj.timerSyncId) {
93 | return;
94 | }
95 |
96 | // this is already paused, ignore events for now
97 | if (obj.remaining != null) { return; }
98 |
99 | /*
100 | mousemove is kinda buggy, it can be triggered when it should be idle.
101 | Typically is happening between 115 - 150 milliseconds after idle triggered.
102 | @psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
103 | @thorst has similar issues on ios7 "after $.scrollTop() on text area"
104 | */
105 | if (e.type === "mousemove") {
106 | // if coord are same, it didn't move
107 | if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
108 | return;
109 | }
110 | // if coord don't exist how could it move
111 | if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
112 | return;
113 | }
114 | // under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
115 | var elapsed = (+new Date()) - obj.olddate;
116 | if (elapsed < 200) {
117 | return;
118 | }
119 | }
120 |
121 | // clear any existing timeout
122 | clearTimeout(obj.tId);
123 |
124 | // if the idle timer is enabled, flip
125 | if (obj.idle) {
126 | toggleIdleState(e);
127 | }
128 |
129 | // store when user was last active
130 | obj.lastActive = +new Date();
131 |
132 | // update mouse coord
133 | obj.pageX = e.pageX;
134 | obj.pageY = e.pageY;
135 |
136 | // sync lastActive
137 | if (e.type !== "storage" && obj.timerSyncId) {
138 | if (typeof(localStorage) !== "undefined") {
139 | localStorage.setItem(obj.timerSyncId, obj.lastActive);
140 | }
141 | }
142 |
143 | // set a new timeout
144 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
145 | },
146 | /**
147 | * Restore initial settings and restart timer
148 | * @return {void}
149 | * @method reset
150 | * @static
151 | */
152 | reset = function () {
153 |
154 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
155 |
156 | // reset settings
157 | obj.idle = obj.idleBackup;
158 | obj.olddate = +new Date();
159 | obj.lastActive = obj.olddate;
160 | obj.remaining = null;
161 |
162 | // reset Timers
163 | clearTimeout(obj.tId);
164 | if (!obj.idle) {
165 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
166 | }
167 |
168 | },
169 | /**
170 | * Store remaining time, stop timer
171 | * You can pause from an idle OR active state
172 | * @return {void}
173 | * @method pause
174 | * @static
175 | */
176 | pause = function () {
177 |
178 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
179 |
180 | // this is already paused
181 | if ( obj.remaining != null ) { return; }
182 |
183 | // define how much is left on the timer
184 | obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
185 |
186 | // clear any existing timeout
187 | clearTimeout(obj.tId);
188 | },
189 | /**
190 | * Start timer with remaining value
191 | * @return {void}
192 | * @method resume
193 | * @static
194 | */
195 | resume = function () {
196 |
197 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
198 |
199 | // this isn't paused yet
200 | if ( obj.remaining == null ) { return; }
201 |
202 | // start timer
203 | if ( !obj.idle ) {
204 | obj.tId = setTimeout(toggleIdleState, obj.remaining);
205 | }
206 |
207 | // clear remaining
208 | obj.remaining = null;
209 | },
210 | /**
211 | * Stops the idle timer. This removes appropriate event handlers
212 | * and cancels any pending timeouts.
213 | * @return {void}
214 | * @method destroy
215 | * @static
216 | */
217 | destroy = function () {
218 |
219 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
220 |
221 | //clear any pending timeouts
222 | clearTimeout(obj.tId);
223 |
224 | //Remove data
225 | jqElem.removeData("idleTimerObj" + uniqueId);
226 |
227 | //detach the event handlers
228 | jqElem.off("._idleTimer" + uniqueId);
229 | },
230 | /**
231 | * Returns the time until becoming idle
232 | * @return {number}
233 | * @method remainingtime
234 | * @static
235 | */
236 | remainingtime = function () {
237 |
238 | var obj = $.data(elem, "idleTimerObj" + uniqueId) || {};
239 |
240 | //If idle there is no time remaining
241 | if ( obj.idle ) { return 0; }
242 |
243 | //If its paused just return that
244 | if ( obj.remaining != null ) { return obj.remaining; }
245 |
246 | //Determine remaining, if negative idle didn't finish flipping, just return 0
247 | var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
248 | if (remaining < 0) { remaining = 0; }
249 |
250 | //If this is paused return that number, else return current remaining
251 | return remaining;
252 | };
253 |
254 |
255 | // determine which function to call
256 | if (firstParam === null && typeof obj.idle !== "undefined") {
257 | // they think they want to init, but it already is, just reset
258 | reset();
259 | return jqElem;
260 | } else if (firstParam === null) {
261 | // they want to init
262 | } else if (firstParam !== null && typeof obj.idle === "undefined") {
263 | // they want to do something, but it isnt init
264 | // not sure the best way to handle this
265 | return false;
266 | } else if (firstParam === "destroy") {
267 | destroy();
268 | return jqElem;
269 | } else if (firstParam === "pause") {
270 | pause();
271 | return jqElem;
272 | } else if (firstParam === "resume") {
273 | resume();
274 | return jqElem;
275 | } else if (firstParam === "reset") {
276 | reset();
277 | return jqElem;
278 | } else if (firstParam === "getRemainingTime") {
279 | return remainingtime();
280 | } else if (firstParam === "getElapsedTime") {
281 | return (+new Date()) - obj.olddate;
282 | } else if (firstParam === "getLastActiveTime") {
283 | return obj.lastActive;
284 | } else if (firstParam === "isIdle") {
285 | return obj.idle;
286 | }
287 |
288 | // Test via a getter in the options object to see if the passive property is accessed
289 | // This isnt working in jquery, though is planned for 4.0
290 | // https://github.com/jquery/jquery/issues/2871
291 | /*var supportsPassive = false;
292 | try {
293 | var Popts = Object.defineProperty({}, "passive", {
294 | get: function() {
295 | supportsPassive = true;
296 | }
297 | });
298 | window.addEventListener("test", null, Popts);
299 | } catch (e) {}
300 | */
301 |
302 | /* (intentionally not documented)
303 | * Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
304 | * @param {Event} event A DOM2-normalized event object.
305 | * @return {void}
306 | */
307 | jqElem.on((opts.events + " ").split(" ").join("._idleTimer" + uniqueId + " ").trim(), function (e) {
308 |
309 | handleEvent(e);
310 | });
311 | //}, supportsPassive ? { passive: true } : false);
312 |
313 | if (opts.timerSyncId) {
314 | $(window).on("storage", handleEvent);
315 | }
316 |
317 | // Internal Object Properties, This isn't all necessary, but we
318 | // explicitly define all keys here so we know what we are working with
319 | obj = $.extend({}, {
320 | olddate : +new Date(), // the last time state changed
321 | lastActive: +new Date(), // the last time timer was active
322 | idle : opts.idle, // current state
323 | idleBackup : opts.idle, // backup of idle parameter since it gets modified
324 | timeout : opts.timeout, // the interval to change state
325 | remaining : null, // how long until state changes
326 | timerSyncId : opts.timerSyncId, // localStorage key to use for syncing this timer
327 | tId : null, // the idle timer setTimeout
328 | pageX : null, // used to store the mouse coord
329 | pageY : null
330 | });
331 |
332 | // set a timeout to toggle state. May wish to omit this in some situations
333 | if (!obj.idle) {
334 | obj.tId = setTimeout(toggleIdleState, obj.timeout);
335 | }
336 |
337 | // store our instance on the object
338 | $.data(elem, "idleTimerObj" + uniqueId, obj);
339 |
340 | return jqElem;
341 | };
342 |
343 | // This allows binding to element
344 | $.fn.idleTimer = function (firstParam, uniqueId) {
345 | if (this[0]) {
346 | return $.idleTimer(firstParam, this[0], uniqueId);
347 | }
348 |
349 | return this;
350 | };
351 |
352 | })(jQuery);
353 |
--------------------------------------------------------------------------------
/demos/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Jquery-idletimer
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
34 |
35 |
36 |