315 lines
11 KiB
JavaScript
315 lines
11 KiB
JavaScript
|
/*! Idle Timer - v1.1.0 - 2016-03-21
|
||
|
* https://github.com/thorst/jquery-idletimer
|
||
|
* Copyright (c) 2016 Paul Irish; Licensed MIT */
|
||
|
/*
|
||
|
mousewheel (deprecated) -> IE6.0, Chrome, Opera, Safari
|
||
|
DOMMouseScroll (deprecated) -> Firefox 1.0
|
||
|
wheel (standard) -> Chrome 31, Firefox 17, IE9, Firefox Mobile 17.0
|
||
|
|
||
|
//No need to use, use DOMMouseScroll
|
||
|
MozMousePixelScroll -> Firefox 3.5, Firefox Mobile 1.0
|
||
|
|
||
|
//Events
|
||
|
WheelEvent -> see wheel
|
||
|
MouseWheelEvent -> see mousewheel
|
||
|
MouseScrollEvent -> Firefox 3.5, Firefox Mobile 1.0
|
||
|
*/
|
||
|
(function ($) {
|
||
|
|
||
|
$.idleTimer = function (firstParam, elem) {
|
||
|
var opts;
|
||
|
if ( typeof firstParam === "object" ) {
|
||
|
opts = firstParam;
|
||
|
firstParam = null;
|
||
|
} else if (typeof firstParam === "number") {
|
||
|
opts = { timeout: firstParam };
|
||
|
firstParam = null;
|
||
|
}
|
||
|
|
||
|
// element to watch
|
||
|
elem = elem || document;
|
||
|
|
||
|
// defaults that are to be stored as instance props on the elem
|
||
|
opts = $.extend({
|
||
|
idle: false, // indicates if the user is idle
|
||
|
timeout: 30000, // the amount of time (ms) before the user is considered idle
|
||
|
events: "mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove" // define active events
|
||
|
}, opts);
|
||
|
|
||
|
var jqElem = $(elem),
|
||
|
obj = jqElem.data("idleTimerObj") || {},
|
||
|
|
||
|
/* (intentionally not documented)
|
||
|
* Toggles the idle state and fires an appropriate event.
|
||
|
* @return {void}
|
||
|
*/
|
||
|
toggleIdleState = function (e) {
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
// toggle the state
|
||
|
obj.idle = !obj.idle;
|
||
|
|
||
|
// store toggle state date time
|
||
|
obj.olddate = +new Date();
|
||
|
|
||
|
// create a custom event, with state and name space
|
||
|
var event = $.Event((obj.idle ? "idle" : "active") + ".idleTimer");
|
||
|
|
||
|
// trigger event on object with elem and copy of obj
|
||
|
$(elem).trigger(event, [elem, $.extend({}, obj), e]);
|
||
|
},
|
||
|
/**
|
||
|
* Handle event triggers
|
||
|
* @return {void}
|
||
|
* @method event
|
||
|
* @static
|
||
|
*/
|
||
|
handleEvent = function (e) {
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
if (e.type === "storage" && e.originalEvent.key !== obj.timerSyncId) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// this is already paused, ignore events for now
|
||
|
if (obj.remaining != null) { return; }
|
||
|
|
||
|
/*
|
||
|
mousemove is kinda buggy, it can be triggered when it should be idle.
|
||
|
Typically is happening between 115 - 150 milliseconds after idle triggered.
|
||
|
@psyafter & @kaellis report "always triggered if using modal (jQuery ui, with overlay)"
|
||
|
@thorst has similar issues on ios7 "after $.scrollTop() on text area"
|
||
|
*/
|
||
|
if (e.type === "mousemove") {
|
||
|
// if coord are same, it didn't move
|
||
|
if (e.pageX === obj.pageX && e.pageY === obj.pageY) {
|
||
|
return;
|
||
|
}
|
||
|
// if coord don't exist how could it move
|
||
|
if (typeof e.pageX === "undefined" && typeof e.pageY === "undefined") {
|
||
|
return;
|
||
|
}
|
||
|
// under 200 ms is hard to do, and you would have to stop, as continuous activity will bypass this
|
||
|
var elapsed = (+new Date()) - obj.olddate;
|
||
|
if (elapsed < 200) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// clear any existing timeout
|
||
|
clearTimeout(obj.tId);
|
||
|
|
||
|
// if the idle timer is enabled, flip
|
||
|
if (obj.idle) {
|
||
|
toggleIdleState(e);
|
||
|
}
|
||
|
|
||
|
// store when user was last active
|
||
|
obj.lastActive = +new Date();
|
||
|
|
||
|
// update mouse coord
|
||
|
obj.pageX = e.pageX;
|
||
|
obj.pageY = e.pageY;
|
||
|
|
||
|
// sync lastActive
|
||
|
if (e.type !== "storage" && obj.timerSyncId) {
|
||
|
if (typeof(localStorage) !== "undefined") {
|
||
|
localStorage.setItem(obj.timerSyncId, obj.lastActive);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// set a new timeout
|
||
|
obj.tId = setTimeout(toggleIdleState, obj.timeout);
|
||
|
},
|
||
|
/**
|
||
|
* Restore initial settings and restart timer
|
||
|
* @return {void}
|
||
|
* @method reset
|
||
|
* @static
|
||
|
*/
|
||
|
reset = function () {
|
||
|
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
// reset settings
|
||
|
obj.idle = obj.idleBackup;
|
||
|
obj.olddate = +new Date();
|
||
|
obj.lastActive = obj.olddate;
|
||
|
obj.remaining = null;
|
||
|
|
||
|
// reset Timers
|
||
|
clearTimeout(obj.tId);
|
||
|
if (!obj.idle) {
|
||
|
obj.tId = setTimeout(toggleIdleState, obj.timeout);
|
||
|
}
|
||
|
|
||
|
},
|
||
|
/**
|
||
|
* Store remaining time, stop timer
|
||
|
* You can pause from an idle OR active state
|
||
|
* @return {void}
|
||
|
* @method pause
|
||
|
* @static
|
||
|
*/
|
||
|
pause = function () {
|
||
|
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
// this is already paused
|
||
|
if ( obj.remaining != null ) { return; }
|
||
|
|
||
|
// define how much is left on the timer
|
||
|
obj.remaining = obj.timeout - ((+new Date()) - obj.olddate);
|
||
|
|
||
|
// clear any existing timeout
|
||
|
clearTimeout(obj.tId);
|
||
|
},
|
||
|
/**
|
||
|
* Start timer with remaining value
|
||
|
* @return {void}
|
||
|
* @method resume
|
||
|
* @static
|
||
|
*/
|
||
|
resume = function () {
|
||
|
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
// this isn't paused yet
|
||
|
if ( obj.remaining == null ) { return; }
|
||
|
|
||
|
// start timer
|
||
|
if ( !obj.idle ) {
|
||
|
obj.tId = setTimeout(toggleIdleState, obj.remaining);
|
||
|
}
|
||
|
|
||
|
// clear remaining
|
||
|
obj.remaining = null;
|
||
|
},
|
||
|
/**
|
||
|
* Stops the idle timer. This removes appropriate event handlers
|
||
|
* and cancels any pending timeouts.
|
||
|
* @return {void}
|
||
|
* @method destroy
|
||
|
* @static
|
||
|
*/
|
||
|
destroy = function () {
|
||
|
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
//clear any pending timeouts
|
||
|
clearTimeout(obj.tId);
|
||
|
|
||
|
//Remove data
|
||
|
jqElem.removeData("idleTimerObj");
|
||
|
|
||
|
//detach the event handlers
|
||
|
jqElem.off("._idleTimer");
|
||
|
},
|
||
|
/**
|
||
|
* Returns the time until becoming idle
|
||
|
* @return {number}
|
||
|
* @method remainingtime
|
||
|
* @static
|
||
|
*/
|
||
|
remainingtime = function () {
|
||
|
|
||
|
var obj = $.data(elem, "idleTimerObj") || {};
|
||
|
|
||
|
//If idle there is no time remaining
|
||
|
if ( obj.idle ) { return 0; }
|
||
|
|
||
|
//If its paused just return that
|
||
|
if ( obj.remaining != null ) { return obj.remaining; }
|
||
|
|
||
|
//Determine remaining, if negative idle didn't finish flipping, just return 0
|
||
|
var remaining = obj.timeout - ((+new Date()) - obj.lastActive);
|
||
|
if (remaining < 0) { remaining = 0; }
|
||
|
|
||
|
//If this is paused return that number, else return current remaining
|
||
|
return remaining;
|
||
|
};
|
||
|
|
||
|
|
||
|
// determine which function to call
|
||
|
if (firstParam === null && typeof obj.idle !== "undefined") {
|
||
|
// they think they want to init, but it already is, just reset
|
||
|
reset();
|
||
|
return jqElem;
|
||
|
} else if (firstParam === null) {
|
||
|
// they want to init
|
||
|
} else if (firstParam !== null && typeof obj.idle === "undefined") {
|
||
|
// they want to do something, but it isnt init
|
||
|
// not sure the best way to handle this
|
||
|
return false;
|
||
|
} else if (firstParam === "destroy") {
|
||
|
destroy();
|
||
|
return jqElem;
|
||
|
} else if (firstParam === "pause") {
|
||
|
pause();
|
||
|
return jqElem;
|
||
|
} else if (firstParam === "resume") {
|
||
|
resume();
|
||
|
return jqElem;
|
||
|
} else if (firstParam === "reset") {
|
||
|
reset();
|
||
|
return jqElem;
|
||
|
} else if (firstParam === "getRemainingTime") {
|
||
|
return remainingtime();
|
||
|
} else if (firstParam === "getElapsedTime") {
|
||
|
return (+new Date()) - obj.olddate;
|
||
|
} else if (firstParam === "getLastActiveTime") {
|
||
|
return obj.lastActive;
|
||
|
} else if (firstParam === "isIdle") {
|
||
|
return obj.idle;
|
||
|
}
|
||
|
|
||
|
/* (intentionally not documented)
|
||
|
* Handles a user event indicating that the user isn't idle. namespaced with internal idleTimer
|
||
|
* @param {Event} event A DOM2-normalized event object.
|
||
|
* @return {void}
|
||
|
*/
|
||
|
jqElem.on($.trim((opts.events + " ").split(" ").join("._idleTimer ")), function (e) {
|
||
|
handleEvent(e);
|
||
|
});
|
||
|
|
||
|
if (opts.timerSyncId) {
|
||
|
$(window).bind("storage", handleEvent);
|
||
|
}
|
||
|
|
||
|
// Internal Object Properties, This isn't all necessary, but we
|
||
|
// explicitly define all keys here so we know what we are working with
|
||
|
obj = $.extend({}, {
|
||
|
olddate : +new Date(), // the last time state changed
|
||
|
lastActive: +new Date(), // the last time timer was active
|
||
|
idle : opts.idle, // current state
|
||
|
idleBackup : opts.idle, // backup of idle parameter since it gets modified
|
||
|
timeout : opts.timeout, // the interval to change state
|
||
|
remaining : null, // how long until state changes
|
||
|
timerSyncId : opts.timerSyncId, // localStorage key to use for syncing this timer
|
||
|
tId : null, // the idle timer setTimeout
|
||
|
pageX : null, // used to store the mouse coord
|
||
|
pageY : null
|
||
|
});
|
||
|
|
||
|
// set a timeout to toggle state. May wish to omit this in some situations
|
||
|
if (!obj.idle) {
|
||
|
obj.tId = setTimeout(toggleIdleState, obj.timeout);
|
||
|
}
|
||
|
|
||
|
// store our instance on the object
|
||
|
$.data(elem, "idleTimerObj", obj);
|
||
|
|
||
|
return jqElem;
|
||
|
};
|
||
|
|
||
|
// This allows binding to element
|
||
|
$.fn.idleTimer = function (firstParam) {
|
||
|
if (this[0]) {
|
||
|
return $.idleTimer(firstParam, this[0]);
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
})(jQuery);
|