/*! 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);