├── .gitignore ├── .jshintrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Gruntfile.js ├── LICENSE-MIT ├── README.md ├── bower.json ├── demos ├── autologout.html ├── autologoutsync.html ├── defaultbinding.html ├── index.html ├── respond.proxy.gif └── respond.proxy.js ├── dist ├── idle-timer.0.9.3.js ├── idle-timer.0.9.3.min.js ├── idle-timer.1.0.0.js ├── idle-timer.1.0.0.min.js ├── idle-timer.1.0.1.js ├── idle-timer.1.0.1.min.js ├── idle-timer.1.1.0.js ├── idle-timer.1.1.0.min.js ├── idle-timer.1.1.1.js ├── idle-timer.1.1.1.min.js ├── idle-timer.js └── idle-timer.min.js ├── idle-timer.jquery.json ├── jquery-idletimer.sln ├── jquery-idletimer.v12.suo ├── libs ├── jquery-loader.js ├── jquery │ └── jquery.js └── qunit │ ├── qunit.css │ └── qunit.js ├── package-lock.json ├── package.json ├── src ├── .jshintrc └── idle-timer.js └── test ├── .jshintrc ├── idle-timer.html └── idle-timer_test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | build/* 3 | yarn-error.log 4 | yarn.lock 5 | undefined/coverage.tmp 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "node": true, 14 | "quotmark": "double" 15 | } 16 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "0.10" 5 | before_install: 6 | - npm install -g grunt-cli 7 | install: 8 | - npm install 9 | before_script: 10 | - grunt 11 | script: 12 | - grunt coveralls -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ####VERSION: 1.1.0 2 | ####RELEASED: 03/10/2014 3 | ####AUTHOR: TODD HORST 4 | ------- 5 | 6 | This is the first release I'm in charge of, and as such, I've made the project my own. Hopefully things work well, and we can 7 | keep this project evolving. Please feel free to submit requests for feature. 8 | 9 | #####SRC 10 | 11 | Fixed: 12 | * if users set idle=true and left startImmediately=true ( the default ), it would toggle incorrectly 13 | * opts references (changed to obj) in toggleIdleState 14 | * chaining on all functions 15 | * mousemove issues experienced in chrome & iOS7 #4 16 | * every time you called a function (say getElapsedTime), it would reset olddate, so you were getting elapsed from last getElapsedTime, not actual time from last state change 17 | * can now call functions on default binding ex $.idleTimer("pause"); 18 | * can now call default binding with parameter $.idleTimer({idle:true}); 19 | * destroy doesnt remove bindings user added, only internally bound events 20 | 21 | Added: 22 | * reset, pause/resume #19 23 | * getRemainingTime, getLastActiveTime (supports autologout demo), isIdle (state of object) 24 | * internally documentation for obj keys 25 | * element, trigger event, and (copy of) object are parameters to raised idle/active event 26 | * `wheel` event. wheel is to replace DOMMouseScroll & mousewheel. We could remove DOMMouseScroll now as its technically not needed to support firefox n-1. mousewheel will stick around until we drop ie8. 27 | * `MSPointerDown` `MSPointerMove` events #22 28 | * ability to send only settings parameter 29 | * Ability to sync timer state across browser tabs 30 | 31 | Update: 32 | * changed $().data() to jQuery.data() for speed 33 | * removed myelem in toggleIdleState and replaced with elem 34 | 35 | 36 | ###Breaking Changes 37 | 38 | NOTE: 39 | I realize these changes could upset some people. If you need something added back, please open a ticket. If its determined that 40 | these are necessary, I will be happy to put them back. They were removed to clean up the code or fix bugs. You can of course continue 41 | using the old version. 42 | 43 | * Passing timeout AND settings is no longer valid. Example: `$( document ).idleTimer(3000, {idle:true});` Change to `$( document ).idleTimer({timeout:3000,idle:true});` 44 | * Using `$( document ).data("idleTimer");` returned a string. This is less ideal. Start using `$( document ).idleTimer("isIdle");` 45 | to get a boolean value of the current state of the element. If you need the string use: 46 | `var state = $( document ).idleTimer("isIdle") ? "idle" : "active"` 47 | * `Enabled` param has been removed. If needed dont initialize idleTimer until you need it. Previously there was no way to enable 48 | once it was disabled anyway, unless manually modifying the internal data. You may also want to look at reset (resets data/state without removing 49 | bindings) and pause (stops timer until resumed) 50 | * `StartImmediately` param has been removed. If you need to not have the timer start set `idle` setting to true. This was redundant and 51 | could cause a bug if users didn't flip both `idle` and `startImmediately` 52 | * Firefox lateness bug fix, Firefox has updated their implementation, so there is no need to have added code to support intended 53 | browsers. Besides I always look at elem instead of local instance on myelem now 54 | * Chrome "alert triggers event" bugfix, I believe Chrome has fixed this, if you still experience the issue let me know and I'll put it back 55 | but I was unable to reproduce this 56 | 57 | 58 | 59 | ###DEMO 60 | 61 | * Updated jQuery to newest (1.11.0) 62 | * Remove jQuery UI (was used for dialog) 63 | * Added bootstrap and standard template 64 | * Added examples of all functions 65 | * Made clearer which log was for document/textarea idletimer 66 | * Stops event propagation on textarea 67 | * Shows default state according to obj idle value 68 | * Added demo for autologout (handles that fact that js gets paused when iOS app gets minimized) 69 | * Added demo with all autobinding 70 | 71 | ###README 72 | 73 | * Link to demo 74 | * Document new functions 75 | * Document supported browsers 76 | * Document jQuery dependency 77 | * Document author history 78 | * Removed note about mixing `$.idleTimer` and `$( document ).idleTimer(...)`, related bugs have been fixed 79 | * General cleanup 80 | 81 | ###DIST 82 | 83 | * Automated builds from travis 84 | * Grunt settings have been updated, so that it builds correctly 85 | * Tests added for new functions, admittedly Im still learning this 86 | * Tests only keydown. Need to test idleTimer not the browser events. Having them all in a loop was unpredictable. 87 | 88 | ###THOUGHTS 89 | 90 | I thought about allowing users to send a callback function in, ala most jquery methods. However 91 | after researching and thinking the callbacks would be synchronous and events are async. So we 92 | will be sticking with the current method 93 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Important notes 4 | Please don't edit files in the `dist` subdirectory as they are generated via grunt. You'll find source code in the `src` subdirectory! 5 | 6 | ### Code style 7 | Regarding code style like indentation and whitespace, **follow the conventions you see used in the source already.** 8 | 9 | ### PhantomJS 10 | While grunt can run the included unit tests via [PhantomJS](http://phantomjs.org/), this shouldn't be considered a substitute for the real thing. Please be sure to test the `test/*.html` unit test file(s) in _actual_ browsers. 11 | 12 | See the [Why does grunt complain that PhantomJS isn't installed?](https://github.com/gruntjs/grunt/blob/master/docs/faq.md#why-does-grunt-complain-that-phantomjs-isnt-installed) guide in the [Grunt FAQ](https://github.com/gruntjs/grunt/blob/master/docs/faq.md) for help with installing or troubleshooting PhantomJS. 13 | 14 | ## Modifying the code 15 | First, ensure that you have the latest [Node.js](http://nodejs.org/) and [npm](http://npmjs.org/) installed. 16 | 17 | Test that grunt is installed globally by running `grunt --version` at the command-line. If grunt isn't installed globally, run `npm install -g grunt` to install the latest version. _You may need to run `sudo npm install -g grunt`._ 18 | 19 | _Note that in Windows, you may have to run `grunt.cmd` instead of `grunt`._ 20 | 21 | 1. Fork and clone the repo. 22 | 1. Run `npm install` to install all dependencies (including grunt). 23 | 1. Run `grunt` to grunt this project. 24 | 25 | Assuming that you don't see any red, you're ready to go. Just be sure to run `grunt` after making any changes, to ensure that nothing is broken. 26 | 27 | ## Submitting pull requests 28 | 29 | 1. Create a new branch, please don't work in your `master` branch directly. 30 | 1. Add failing tests for the change you want to make. Run `grunt` to see the tests fail. 31 | 1. Fix stuff. 32 | 1. Run `grunt` to see if the tests pass. Repeat steps 2-4 until done. 33 | 1. Open `test/*.html` unit test file(s) in actual browser to ensure tests pass everywhere. 34 | 1. Update the documentation to reflect any changes. 35 | 1. Push to your fork and submit a pull request. 36 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | module.exports = function(grunt) { 4 | 5 | // Project configuration. 6 | grunt.initConfig({ 7 | // Metadata. 8 | pkg: grunt.file.readJSON("idle-timer.jquery.json"), 9 | banner: "/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - " + 10 | "<%= grunt.template.today('yyyy-mm-dd') %>\n" + 11 | "<%= pkg.homepage ? '* ' + pkg.homepage + '\\n' : '' %>" + 12 | "* Copyright (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %>;" + 13 | " Licensed <%= pkg.licenses.map(o => o['type']).join(', ') %> */\n", 14 | minbanner: "/*! <%= pkg.title || pkg.name %> v<%= pkg.version %> <%= grunt.template.today('yyyy-mm-dd') %> | " + 15 | "<%= pkg.homepage ? pkg.homepage : '' %> | (c) <%= grunt.template.today('yyyy') %> <%= pkg.author.name %> | " + 16 | "Licensed <%= pkg.licenses.map(o => o['type']).join(', ') %> */\n", 17 | // Task configuration. 18 | concat: { 19 | options: { 20 | banner: "<%= banner %>", 21 | stripBanners: true 22 | }, 23 | dist: { 24 | files: [ 25 | { 26 | src: ["src/<%= pkg.name %>.js"], 27 | dest: "dist/<%= pkg.name %>.js" 28 | }, 29 | { 30 | src: ["src/<%= pkg.name %>.js"], 31 | dest: "dist/<%= pkg.name %>.<%= pkg.version %>.js" 32 | } 33 | ] 34 | }, 35 | }, 36 | uglify: { 37 | options: { 38 | banner: "<%= minbanner %>" 39 | }, 40 | dist: { 41 | files: [ 42 | { 43 | src: "<%= concat.dist.files[0].dest %>", 44 | dest: "dist/<%= pkg.name %>.min.js" 45 | }, 46 | { 47 | src: "<%= concat.dist.files[0].dest %>", 48 | dest: "dist/<%= pkg.name %>.<%= pkg.version %>.min.js" 49 | } 50 | ] 51 | }, 52 | }, 53 | qunit: { 54 | all: { 55 | options: { 56 | urls:["test/idle-timer.html"] 57 | } 58 | } 59 | }, 60 | coveralls: { 61 | options: { 62 | // dont fail if coveralls fails 63 | force: true 64 | }, 65 | main_target: { 66 | src: "build/report/lcov/lcov.info" 67 | } 68 | }, 69 | jshint: { 70 | gruntfile: { 71 | options: { 72 | jshintrc: ".jshintrc", 73 | reporterOutput: "" 74 | }, 75 | src: "Gruntfile.js" 76 | }, 77 | src: { 78 | options: { 79 | jshintrc: "src/.jshintrc", 80 | reporterOutput: "" 81 | }, 82 | src: ["src/**/*.js"] 83 | }, 84 | test: { 85 | options: { 86 | jshintrc: "test/.jshintrc", 87 | reporterOutput: "" 88 | }, 89 | src: ["test/**/*.js"] 90 | }, 91 | }, 92 | watch: { 93 | gruntfile: { 94 | files: "<%= jshint.gruntfile.src %>", 95 | tasks: ["jshint:gruntfile"] 96 | }, 97 | src: { 98 | files: "<%= jshint.src.src %>", 99 | tasks: ["jshint:src", "qunit"] 100 | }, 101 | test: { 102 | files: "<%= jshint.test.src %>", 103 | tasks: ["jshint:test", "qunit"] 104 | }, 105 | }, 106 | }); 107 | 108 | // These plugins provide necessary tasks. 109 | grunt.loadNpmTasks("grunt-contrib-jshint"); 110 | grunt.loadNpmTasks("grunt-coveralls"); 111 | grunt.loadNpmTasks("grunt-contrib-qunit"); 112 | grunt.loadNpmTasks("grunt-contrib-concat"); 113 | grunt.loadNpmTasks("grunt-contrib-uglify"); 114 | grunt.loadNpmTasks("grunt-contrib-watch"); 115 | 116 | // Default task. 117 | grunt.registerTask("default", ["jshint", "qunit", "concat", "uglify"]); 118 | 119 | }; 120 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Paul Irish 2 | 3 | Permission is hereby granted, free of charge, to any person 4 | obtaining a copy of this software and associated documentation 5 | files (the "Software"), to deal in the Software without 6 | restriction, including without limitation the rights to use, 7 | copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the 9 | Software is furnished to do so, subject to the following 10 | conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jQuery Idle Timer Plugin 2 | ======================== 3 | 4 | [![Build Status](https://travis-ci.org/thorst/jquery-idletimer.svg?branch=master)](https://travis-ci.org/thorst/jquery-idletimer) 5 | [![Dependency Status](https://david-dm.org/thorst/jquery-idletimer.svg?theme=shields.io)](https://david-dm.org/thorst/jquery-idletimer) 6 | [![devDependency Status](https://david-dm.org/thorst/jquery-idletimer/dev-status.svg?theme=shields.io)](https://david-dm.org/thorst/jquery-idletimer#info=devDependencies) 7 | [![Coverage Status](http://img.shields.io/coveralls/thorst/jquery-idletimer.svg)](https://coveralls.io/r/thorst/jquery-idletimer?branch=master) 8 | 9 | Demo 10 | -------- 11 | https://thorst.github.io/jquery-idletimer/index.html 12 | 13 | Download 14 | -------- 15 | * [Compressed ~3kb](https://raw.github.com/thorst/jquery-idletimer/master/dist/idle-timer.min.js) 16 | * [Uncompressed ~11kb](https://raw.github.com/thorst/jquery-idletimer/master/dist/idle-timer.js) 17 | 18 | Purpose 19 | ------- 20 | 21 | Fires a custom event when the user is "idle". Idle is defined by not... 22 | 23 | * moving the mouse 24 | * scrolling the mouse wheel 25 | * using the keyboard 26 | 27 | 28 | Usage 29 | ----- 30 | 31 | There are two ways to instantiate. Either statically, or on an element. Element bound timers 32 | will only watch for events inside of them. You may just want page-level activity, in which 33 | case you may set up your timers on `document`, `document.documentElement`, and `document.body`. 34 | Instantiate returns jQuery for chaining. 35 | 36 | ```javascript 37 | $(function() { 38 | // binds to document - shorthand 39 | $.idleTimer(); 40 | 41 | // binds to document - explicit 42 | $( document ).idleTimer(); 43 | 44 | // bind to different element 45 | $( "#myTextArea" ).idleTimer(); 46 | }); 47 | ``` 48 | 49 | Options 50 | ----- 51 | You can configure the settings several ways 52 | 53 | ```javascript 54 | $(function() { 55 | // idleTimer() with all defaults 56 | $( document ).idleTimer( ); 57 | 58 | // idleTimer() takes an optional numeric argument that defines just the idle timeout 59 | // timeout is in milliseconds 60 | $( document ).idleTimer( 10000 ); 61 | 62 | // idleTimer() takes an optional object argument that defines any/all setting 63 | $( document ).idleTimer( { 64 | timeout:10000, 65 | idle:true 66 | }); 67 | 68 | /* 69 | * Here are the possible settings 70 | * you can omit any or all of them 71 | */ 72 | 73 | // indicates if the user is idle 74 | idle [default:false] 75 | 76 | // the timeout period 77 | timeout [default:30000] 78 | 79 | // activity is any one of these events 80 | events [default:'mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove'] 81 | 82 | // If set, the use a localStorage key to sync activity across browser tabs/windows 83 | timerSyncId [default:null] 84 | }); 85 | ``` 86 | 87 | Events 88 | ----- 89 | When a users state changes a custom events get triggered. There are several parameters 90 | passed to your handler for you to use. 91 | 92 | ```javascript 93 | $(function() { 94 | $( document ).on( "idle.idleTimer", function(event, elem, obj){ 95 | // function you want to fire when the user goes idle 96 | }); 97 | 98 | $( document ).on( "active.idleTimer", function(event, elem, obj, triggerevent){ 99 | // function you want to fire when the user becomes active again 100 | }); 101 | 102 | /* 103 | * Here are the arguments 104 | */ 105 | // event 106 | // will be either idle.idleTimer or active.idleTimer 107 | // use event.stopPropagation(); to stop element from bubbling up to document 108 | 109 | // elem 110 | // is the element that the event was triggered on 111 | 112 | // obj 113 | // is a copy of the internal data used by idleTimer 114 | 115 | // triggerevent 116 | // is the initial event that triggered the element to become active 117 | // obviously for idle state this will be undefined 118 | }); 119 | ``` 120 | 121 | Methods 122 | ----- 123 | There are several methods to invoke 124 | 125 | ```javascript 126 | $(function() { 127 | // stop the timer, removes data, removes event bindings 128 | // to come back from this you will need to instantiate again 129 | // returns: jQuery 130 | $( document ).idleTimer("destroy"); 131 | 132 | // save remaining time, and stops the timer 133 | // returns: jQuery 134 | $( document ).idleTimer("pause"); 135 | 136 | // starts timer with remaining time 137 | // returns: jQuery 138 | $( document ).idleTimer("resume"); 139 | 140 | // restore initial idle state, and restart the timer 141 | // returns: jQuery 142 | $( document ).idleTimer("reset"); 143 | 144 | // get time left until idle, if idle return 0 145 | // returns: number 146 | $( document ).idleTimer("getRemainingTime"); 147 | 148 | // get time elapsed (in ms) since the user went idle/active 149 | // if idle, how have you been idle, if active, how long have you been active 150 | // returns: number 151 | $( document ).idleTimer("getElapsedTime"); 152 | 153 | // get time last active event fired 154 | // returns: number 155 | $( document ).idleTimer("getLastActiveTime"); 156 | 157 | // you can also query if it's "idle" or "active" 158 | // returns: bool 159 | $( document ).idleTimer("isIdle"); 160 | }); 161 | ``` 162 | Using multiple idle monitors 163 | ----- 164 | When using multiple idle monitors on the same element, a unique id needs be used for each one. 165 | 166 | ####Options 167 | ```javascript 168 | $(function() { 169 | // idleTimer() takes an optional string argument that allows using multiple timers on the same element 170 | $( document ).idleTimer( 10000, "someUniqueId" ); 171 | // equivalent 172 | $.idleTimer(10000, document, "someUniqueId"); 173 | }); 174 | ``` 175 | ####Methods 176 | ```javascript 177 | $(function() { 178 | var uniqueId = "someUniqueString"; 179 | $( document ).idleTimer("pause", uniqueId); // same for other methods like destroy, reset, ... 180 | }); 181 | ``` 182 | ####Events 183 | ```javascript 184 | $(function() { 185 | var uniqueId = "someUniqueString"; 186 | $( document ).on( "idle.idleTimer" + uniqueId, function(event, elem, obj){ // same for the active event 187 | // function you want to fire for the idle timer with matching unique id on that element 188 | }); 189 | }); 190 | ``` 191 | Pre-Req 192 | ------- 193 | jQuery 1.7 (tested with 1.11.0) 194 | 195 | Intended Browser Support 196 | ------- 197 | ####Desktop 198 | * >=IE8 199 | * Firefox n-1 200 | * Chrome n-1 201 | * Safari n 202 | 203 | ####Mobile 204 | * iOS n-1 205 | * Android (version?) 206 | * Windows Phone IEMobile/10.0 207 | 208 | Links 209 | ------- 210 | * [jQuery plugin repo listing](http://plugins.jquery.com/idle-timer/) 211 | * [cdn](http://cdnjs.com/libraries/jquery-idletimer/) 212 | * [nuget]() - coming soon 213 | * [Eric Hynds Idle Timeout plugin](https://github.com/ehynds/jquery-idle-timeout) 214 | (note we have similar functionality [here](http://thorst.github.io/jquery-idletimer/prod/demos/autologout.html)) 215 | 216 | Playground 217 | ------- 218 | * [js fiddle](http://jsfiddle.net/thorst/2aGL4/) 219 | * [css deck](http://cssdeck.com/labs/sosoro3m) 220 | 221 | Version History 222 | ------- 223 | | Version | Author | Released | Links | 224 | | --------------------------------------- |-----------------| ---------- | ----------------------------- | 225 | | [1.0.0](https://raw.github.com/thorst/jquery-idletimer/master/dist/idle-timer.1.0.0.min.js) | Todd Horst | 03/10/2014 | [Change Log](CHANGELOG.md) - [Breaking Changes](CHANGELOG.md#breaking-changes) | 226 | | [0.9.3](https://raw.github.com/thorst/jquery-idletimer/master/dist/idle-timer.0.9.3.min.js) | Mike Sherov | 08/04/2013 | 227 | 228 | 229 | Author History 230 | ------- 231 | * Nicholas C. Zakas (yui Version) [idleTimer Blog](http://www.nczonline.net/blog/2009/06/02/detecting-if-the-user-is-idle-with-javascript-and-yui-3/) | [Github Profile](https://github.com/nzakas) | [Github](https://github.com/nzakas/jstools/) 232 | * Paul Irish (initial jQuery Version) [idleTimer Blog](http://paulirish.com/2009/jquery-idletimer-plugin/) | [Github Profile](https://github.com/paulirish) | [Github](https://github.com/paulirish/jquery-idletimer/) 233 | * Mike Sherov (transfered from Paul) [Github Profile](https://github.com/mikesherov) 234 | * Todd Horst (transfered from Mike) [Github Profile](https://github.com/thorst) 235 | 236 | Bug? 237 | ------- 238 | Please create a [fiddle](http://jsfiddle.net/thorst/2aGL4/4/) and [submit a ticket](https://github.com/thorst/jquery-idletimer/issues/new) 239 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-idletimer", 3 | "homepage": "http://thorst.github.io/jquery-idletimer", 4 | "authors": [ 5 | "Todd Horst" 6 | ], 7 | "description": "Provides you a way to monitor user activity with a page", 8 | "main": "src/idle-timer.js", 9 | "moduleType": [ 10 | "globals" 11 | ], 12 | "keywords": [ 13 | "idle", 14 | "timer", 15 | "inactive" 16 | ], 17 | "license": "MIT", 18 | "ignore": [ 19 | "**/.*", 20 | "node_modules", 21 | "bower_components", 22 | "test", 23 | "tests" 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /demos/autologout.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 | 63 |
64 |

Concept

65 |

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 | 101 | 115 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /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 | 91 | 92 | 104 | 105 | 248 | 249 | 250 | 251 | -------------------------------------------------------------------------------- /demos/defaultbinding.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Jquery-idletimer 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 35 | 36 | 37 | 38 | 64 | 65 |
66 | 67 |

Concept

68 |

Here we use default bindings. Instead of explicitly telling idleTimer which element to bind to, it assumes document.

69 |

Some previous versions had issues mixing default binding with explicit calling on document.

70 |

This should no longer be the case. To test I've added 2 sets of buttons to show that they can be mixed.

71 |

The background color changes to show that it is returning a jQuery object

72 | 73 |
74 |
75 |

Document second timeout

76 |

default binding

77 |
78 | Pause 79 | Resume 80 | Elapsed 81 | Init 82 | Destroy 83 | 84 | Reset 85 | Last Active 86 | Remaining 87 | State 88 |
89 |

explicit binding

90 |
91 | Pause 92 | Resume 93 | Elapsed 94 | Init 95 | Destroy 96 | 97 | Reset 98 | Last Active 99 | Remaining 100 | State 101 |
102 |
103 |
104 | 105 |
106 | 107 | 108 | 109 | 110 |
111 | 112 | 127 | 128 | 188 | 189 | 321 | 322 | 453 | 454 | 455 | 456 | 457 | -------------------------------------------------------------------------------- /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 | 62 |
63 |

Concept

64 |

The idle timer is built on jQuery and provides two events: idle.idleTimer and active.idleTimer, which fire when the user's idle state has changed.

65 |

When you move your mouse over the page or start typing, you're considered "active".

66 |

On this page we have two idle timers. One for the entire document. Another for the text area on the right (or bottom if your on mobile).

67 |

I use explicit instantiation here

68 |
69 |
70 |

Document second timeout

71 |
72 | Pause 73 | Resume 74 | Elapsed 75 | Init 76 | Destroy 77 |
78 |
79 |
80 |
81 |

Element second timeout

82 |
83 | Reset 84 | Last Active 85 | Remaining 86 | State 87 |
88 |
89 |
90 |
91 |

Dialogs

92 | 93 | 96 | 99 | 100 | 116 |
117 | 135 | 271 | 272 | 394 | 395 | 396 | -------------------------------------------------------------------------------- /demos/respond.proxy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thorst/jquery-idletimer/d52354c25ff688fe16550338bbac9c69218b1326/demos/respond.proxy.gif -------------------------------------------------------------------------------- /demos/respond.proxy.js: -------------------------------------------------------------------------------- 1 | /*! Respond.js: min/max-width media query polyfill. Remote proxy (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ 2 | (function(win, doc, undefined){ 3 | var docElem = doc.documentElement, 4 | proxyURL = doc.getElementById("respond-proxy").href, 5 | redirectURL = (doc.getElementById("respond-redirect") || location).href, 6 | baseElem = doc.getElementsByTagName("base")[0], 7 | urls = [], 8 | refNode; 9 | 10 | function encode(url){ 11 | return win.encodeURIComponent(url); 12 | } 13 | 14 | function fakejax( url, callback ){ 15 | 16 | var iframe, 17 | AXO; 18 | 19 | // All hail Google http://j.mp/iKMI19 20 | // Behold, an iframe proxy without annoying clicky noises. 21 | if ( "ActiveXObject" in win ) { 22 | AXO = new ActiveXObject( "htmlfile" ); 23 | AXO.open(); 24 | AXO.write( '' ); 25 | AXO.close(); 26 | iframe = AXO.getElementById( "x" ); 27 | } else { 28 | iframe = doc.createElement( "iframe" ); 29 | iframe.style.cssText = "position:absolute;top:-99em"; 30 | docElem.insertBefore(iframe, docElem.firstElementChild || docElem.firstChild ); 31 | } 32 | 33 | iframe.src = checkBaseURL(proxyURL) + "?url=" + encode(redirectURL) + "&css=" + encode(checkBaseURL(url)); 34 | 35 | function checkFrameName() { 36 | var cssText; 37 | 38 | try { 39 | cssText = iframe.contentWindow.name; 40 | } 41 | catch (e) { } 42 | 43 | if (cssText) { 44 | // We've got what we need. Stop the iframe from loading further content. 45 | iframe.src = "about:blank"; 46 | iframe.parentNode.removeChild(iframe); 47 | iframe = null; 48 | 49 | 50 | // Per http://j.mp/kn9EPh, not taking any chances. Flushing the ActiveXObject 51 | if (AXO) { 52 | AXO = null; 53 | 54 | if (win.CollectGarbage) { 55 | win.CollectGarbage(); 56 | } 57 | } 58 | 59 | callback(cssText); 60 | } 61 | else{ 62 | win.setTimeout(checkFrameName, 100); 63 | } 64 | } 65 | 66 | win.setTimeout(checkFrameName, 500); 67 | } 68 | 69 | // http://stackoverflow.com/a/472729 70 | function checkBaseURL(href) { 71 | var el = document.createElement('div'), 72 | escapedURL = href.split('&').join('&'). 73 | split('<').join('<'). 74 | split('"').join('"'); 75 | 76 | el.innerHTML = 'x'; 77 | return el.firstChild.href; 78 | } 79 | 80 | function checkRedirectURL() { 81 | // IE6 & IE7 don't build out absolute urls in attributes. 82 | // So respond.proxy.gif remains relative instead of http://example.com/respond.proxy.gif. 83 | // This trickery resolves that issue. 84 | if (~ !redirectURL.indexOf(location.host)) { 85 | 86 | var fakeLink = doc.createElement("div"); 87 | 88 | fakeLink.innerHTML = ''; 89 | docElem.insertBefore(fakeLink, docElem.firstElementChild || docElem.firstChild ); 90 | 91 | // Grab the parsed URL from that dummy object 92 | redirectURL = fakeLink.firstChild.href; 93 | 94 | // Clean up 95 | fakeLink.parentNode.removeChild(fakeLink); 96 | fakeLink = null; 97 | } 98 | } 99 | 100 | function buildUrls(){ 101 | var links = doc.getElementsByTagName( "link" ); 102 | 103 | for( var i = 0, linkl = links.length; i < linkl; i++ ){ 104 | 105 | var thislink = links[i], 106 | href = links[i].href, 107 | extreg = (/^([a-zA-Z:]*\/\/(www\.)?)/).test( href ), 108 | ext = (baseElem && !extreg) || extreg; 109 | 110 | //make sure it's an external stylesheet 111 | if( thislink.rel.indexOf( "stylesheet" ) >= 0 && ext ){ 112 | (function( link ){ 113 | fakejax( href, function( css ){ 114 | link.styleSheet.rawCssText = css; 115 | respond.update(); 116 | } ); 117 | })( thislink ); 118 | } 119 | } 120 | 121 | 122 | } 123 | 124 | if( !respond.mediaQueriesSupported ){ 125 | checkRedirectURL(); 126 | buildUrls(); 127 | } 128 | 129 | })( window, document ); 130 | -------------------------------------------------------------------------------- /dist/idle-timer.0.9.3.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer - v0.9.2 - 2013-08-04 2 | * https://github.com/mikesherov/jquery-idletimer 3 | * Copyright (c) 2013 Paul Irish; Licensed MIT */ 4 | ( function( $ ) { 5 | 6 | $.idleTimer = function( firstParam, elem, opts ) { 7 | 8 | // defaults that are to be stored as instance props on the elem 9 | opts = $.extend( { 10 | startImmediately: true, //starts a timeout as soon as the timer is set up 11 | idle: false, //indicates if the user is idle 12 | enabled: true, //indicates if the idle timer is enabled 13 | timeout: 30000, //the amount of time (ms) before the user is considered idle 14 | events: "mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove" // activity is one of these events 15 | }, opts ); 16 | 17 | 18 | elem = elem || document; 19 | 20 | var jqElem = $( elem ), 21 | obj = jqElem.data("idleTimerObj") || {}, 22 | 23 | /* (intentionally not documented) 24 | * Toggles the idle state and fires an appropriate event. 25 | * @return {void} 26 | */ 27 | toggleIdleState = function( myelem ) { 28 | 29 | // curse you, mozilla setTimeout lateness bug! 30 | if ( typeof myelem === "number" ) { 31 | myelem = undefined; 32 | } 33 | 34 | var obj = $.data( myelem || elem, "idleTimerObj" ); 35 | 36 | //toggle the state 37 | obj.idle = !obj.idle; 38 | 39 | // reset timeout 40 | var elapsed = ( +new Date() ) - obj.olddate; 41 | obj.olddate = +new Date(); 42 | 43 | // handle Chrome always triggering idle after js alert or comfirm popup 44 | if ( obj.idle && ( elapsed < opts.timeout ) ) { 45 | obj.idle = false; 46 | clearTimeout( $.idleTimer.tId ); 47 | if ( opts.enabled ) { 48 | $.idleTimer.tId = setTimeout( toggleIdleState, opts.timeout ); 49 | } 50 | return; 51 | } 52 | 53 | // create a custom event, but first, store the new state on the element 54 | // and then append that string to a namespace 55 | var event = $.Event( $.data( elem, "idleTimer", obj.idle ? "idle" : "active" ) + ".idleTimer" ); 56 | $( elem ).trigger( event ); 57 | }, 58 | 59 | /** 60 | * Stops the idle timer. This removes appropriate event handlers 61 | * and cancels any pending timeouts. 62 | * @return {void} 63 | * @method stop 64 | * @static 65 | */ 66 | stop = function( jqElem ) { 67 | 68 | var obj = jqElem.data("idleTimerObj") || {}; 69 | 70 | //set to disabled 71 | obj.enabled = false; 72 | 73 | //clear any pending timeouts 74 | clearTimeout( obj.tId ); 75 | 76 | //detach the event handlers 77 | jqElem.off(".idleTimer"); 78 | }; 79 | 80 | obj.olddate = obj.olddate || +new Date(); 81 | 82 | if ( typeof firstParam === "number" ) { 83 | opts.timeout = firstParam; 84 | } else if ( firstParam === "destroy" ) { 85 | stop( jqElem ); 86 | return this; 87 | } else if ( firstParam === "getElapsedTime" ) { 88 | return ( +new Date() ) - obj.olddate; 89 | } 90 | 91 | 92 | /* (intentionally not documented) 93 | * Handles a user event indicating that the user isn't idle. 94 | * @param {Event} event A DOM2-normalized event object. 95 | * @return {void} 96 | */ 97 | jqElem.on( $.trim( ( opts.events + " " ).split(" ").join(".idleTimer ") ), function() { 98 | var obj = $.data( this, "idleTimerObj" ); 99 | 100 | //clear any existing timeout 101 | clearTimeout( obj.tId ); 102 | 103 | //if the idle timer is enabled 104 | if ( obj.enabled ){ 105 | //if it's idle, that means the user is no longer idle 106 | if ( obj.idle ){ 107 | toggleIdleState( this ); 108 | } 109 | 110 | //set a new timeout 111 | obj.tId = setTimeout( toggleIdleState, obj.timeout ); 112 | } 113 | }); 114 | 115 | obj.idle = opts.idle; 116 | obj.enabled = opts.enabled; 117 | obj.timeout = opts.timeout; 118 | 119 | //set a timeout to toggle state. May wish to omit this in some situations 120 | if ( opts.startImmediately ) { 121 | obj.tId = setTimeout( toggleIdleState, obj.timeout ); 122 | } 123 | 124 | // assume the user is active for the first x seconds. 125 | jqElem.data( "idleTimer", "active" ); 126 | 127 | // store our instance on the object 128 | jqElem.data( "idleTimerObj", obj ); 129 | }; 130 | 131 | $.fn.idleTimer = function( firstParam, opts ) { 132 | // Allow omission of opts for backward compatibility 133 | if ( !opts ) { 134 | opts = {}; 135 | } 136 | 137 | if ( this[0] ){ 138 | return $.idleTimer( firstParam, this[0], opts ); 139 | } 140 | 141 | return this; 142 | }; 143 | 144 | })( jQuery ); 145 | -------------------------------------------------------------------------------- /dist/idle-timer.0.9.3.min.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer - v0.9.2 - 2013-08-04 2 | * https://github.com/mikesherov/jquery-idletimer 3 | * Copyright (c) 2013 Paul Irish; Licensed MIT */ 4 | (function(e){e.idleTimer=function(t,i,d){d=e.extend({startImmediately:!0,idle:!1,enabled:!0,timeout:3e4,events:"mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove"},d),i=i||document;var l=e(i),a=l.data("idleTimerObj")||{},o=function(t){"number"==typeof t&&(t=void 0);var l=e.data(t||i,"idleTimerObj");l.idle=!l.idle;var a=+new Date-l.olddate;if(l.olddate=+new Date,l.idle&&d.timeout>a)return l.idle=!1,clearTimeout(e.idleTimer.tId),d.enabled&&(e.idleTimer.tId=setTimeout(o,d.timeout)),void 0;var m=e.Event(e.data(i,"idleTimer",l.idle?"idle":"active")+".idleTimer");e(i).trigger(m)},m=function(e){var t=e.data("idleTimerObj")||{};t.enabled=!1,clearTimeout(t.tId),e.off(".idleTimer")};if(a.olddate=a.olddate||+new Date,"number"==typeof t)d.timeout=t;else{if("destroy"===t)return m(l),this;if("getElapsedTime"===t)return+new Date-a.olddate}l.on(e.trim((d.events+" ").split(" ").join(".idleTimer ")),function(){var t=e.data(this,"idleTimerObj");clearTimeout(t.tId),t.enabled&&(t.idle&&o(this),t.tId=setTimeout(o,t.timeout))}),a.idle=d.idle,a.enabled=d.enabled,a.timeout=d.timeout,d.startImmediately&&(a.tId=setTimeout(o,a.timeout)),l.data("idleTimer","active"),l.data("idleTimerObj",a)},e.fn.idleTimer=function(t,i){return i||(i={}),this[0]?e.idleTimer(t,this[0],i):this}})(jQuery); -------------------------------------------------------------------------------- /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.0.min.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer v1.0.0 2014-03-10 | https://github.com/thorst/jquery-idletimer | (c) 2014 Paul Irish | Licensed MIT */ 2 | !function(a){a.idleTimer=function(b,c){var d;"object"==typeof b?(d=b,b=null):"number"==typeof b&&(d={timeout:b},b=null),c=c||document,d=a.extend({idle:!1,timeout:3e4,events:"mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove"},d);var e=a(c),f=e.data("idleTimerObj")||{},g=function(b){var d=a.data(c,"idleTimerObj")||{};d.idle=!d.idle,d.olddate=+new Date;var e=a.Event((d.idle?"idle":"active")+".idleTimer");a(c).trigger(e,[c,a.extend({},d),b])},h=function(b){var d=a.data(c,"idleTimerObj")||{};if(null==d.remaining){if("mousemove"===b.type){if(b.pageX===d.pageX&&b.pageY===d.pageY)return;if("undefined"==typeof b.pageX&&"undefined"==typeof b.pageY)return;var e=+new Date-d.olddate;if(200>e)return}clearTimeout(d.tId),d.idle&&g(b),d.lastActive=+new Date,d.pageX=b.pageX,d.pageY=b.pageY,d.tId=setTimeout(g,d.timeout)}},i=function(){var b=a.data(c,"idleTimerObj")||{};b.idle=b.idleBackup,b.olddate=+new Date,b.lastActive=b.olddate,b.remaining=null,clearTimeout(b.tId),b.idle||(b.tId=setTimeout(g,b.timeout))},j=function(){var b=a.data(c,"idleTimerObj")||{};null==b.remaining&&(b.remaining=b.timeout-(+new Date-b.olddate),clearTimeout(b.tId))},k=function(){var b=a.data(c,"idleTimerObj")||{};null!=b.remaining&&(b.idle||(b.tId=setTimeout(g,b.remaining)),b.remaining=null)},l=function(){var b=a.data(c,"idleTimerObj")||{};clearTimeout(b.tId),e.removeData("idleTimerObj"),e.off("._idleTimer")},m=function(){var b=a.data(c,"idleTimerObj")||{};if(b.idle)return 0;if(null!=b.remaining)return b.remaining;var d=b.timeout-(+new Date-b.lastActive);return 0>d&&(d=0),d};if(null===b&&"undefined"!=typeof f.idle)return i(),e;if(null===b);else{if(null!==b&&"undefined"==typeof f.idle)return!1;if("destroy"===b)return l(),e;if("pause"===b)return j(),e;if("resume"===b)return k(),e;if("reset"===b)return i(),e;if("getRemainingTime"===b)return m();if("getElapsedTime"===b)return+new Date-f.olddate;if("getLastActiveTime"===b)return f.lastActive;if("isIdle"===b)return f.idle}return e.on(a.trim((d.events+" ").split(" ").join("._idleTimer ")),function(a){h(a)}),f=a.extend({},{olddate:+new Date,lastActive:+new Date,idle:d.idle,idleBackup:d.idle,timeout:d.timeout,remaining:null,tId:null,pageX:null,pageY:null}),f.idle||(f.tId=setTimeout(g,f.timeout)),a.data(c,"idleTimerObj",f),e},a.fn.idleTimer=function(b){return this[0]?a.idleTimer(b,this[0]):this}}(jQuery); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dist/idle-timer.1.0.1.min.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer v1.0.1 2014-03-21 | https://github.com/thorst/jquery-idletimer | (c) 2014 Paul Irish | Licensed MIT */ 2 | !function(a){a.idleTimer=function(b,c){var d;"object"==typeof b?(d=b,b=null):"number"==typeof b&&(d={timeout:b},b=null),c=c||document,d=a.extend({idle:!1,timeout:3e4,events:"mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove"},d);var e=a(c),f=e.data("idleTimerObj")||{},g=function(b){var d=a.data(c,"idleTimerObj")||{};d.idle=!d.idle,d.olddate=+new Date;var e=a.Event((d.idle?"idle":"active")+".idleTimer");a(c).trigger(e,[c,a.extend({},d),b])},h=function(b){var d=a.data(c,"idleTimerObj")||{};if(null==d.remaining){if("mousemove"===b.type){if(b.pageX===d.pageX&&b.pageY===d.pageY)return;if("undefined"==typeof b.pageX&&"undefined"==typeof b.pageY)return;var e=+new Date-d.olddate;if(200>e)return}clearTimeout(d.tId),d.idle&&g(b),d.lastActive=+new Date,d.pageX=b.pageX,d.pageY=b.pageY,d.tId=setTimeout(g,d.timeout)}},i=function(){var b=a.data(c,"idleTimerObj")||{};b.idle=b.idleBackup,b.olddate=+new Date,b.lastActive=b.olddate,b.remaining=null,clearTimeout(b.tId),b.idle||(b.tId=setTimeout(g,b.timeout))},j=function(){var b=a.data(c,"idleTimerObj")||{};null==b.remaining&&(b.remaining=b.timeout-(+new Date-b.olddate),clearTimeout(b.tId))},k=function(){var b=a.data(c,"idleTimerObj")||{};null!=b.remaining&&(b.idle||(b.tId=setTimeout(g,b.remaining)),b.remaining=null)},l=function(){var b=a.data(c,"idleTimerObj")||{};clearTimeout(b.tId),e.removeData("idleTimerObj"),e.off("._idleTimer")},m=function(){var b=a.data(c,"idleTimerObj")||{};if(b.idle)return 0;if(null!=b.remaining)return b.remaining;var d=b.timeout-(+new Date-b.lastActive);return 0>d&&(d=0),d};if(null===b&&"undefined"!=typeof f.idle)return i(),e;if(null===b);else{if(null!==b&&"undefined"==typeof f.idle)return!1;if("destroy"===b)return l(),e;if("pause"===b)return j(),e;if("resume"===b)return k(),e;if("reset"===b)return i(),e;if("getRemainingTime"===b)return m();if("getElapsedTime"===b)return+new Date-f.olddate;if("getLastActiveTime"===b)return f.lastActive;if("isIdle"===b)return f.idle}return e.on(a.trim((d.events+" ").split(" ").join("._idleTimer ")),function(a){h(a)}),f=a.extend({},{olddate:+new Date,lastActive:+new Date,idle:d.idle,idleBackup:d.idle,timeout:d.timeout,remaining:null,tId:null,pageX:null,pageY:null}),f.idle||(f.tId=setTimeout(g,f.timeout)),a.data(c,"idleTimerObj",f),e},a.fn.idleTimer=function(b){return this[0]?a.idleTimer(b,this[0]):this}}(jQuery); -------------------------------------------------------------------------------- /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.1.1.0.min.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer v1.1.0 2016-03-21 | https://github.com/thorst/jquery-idletimer | (c) 2016 Paul Irish | Licensed MIT */ 2 | !function(a){a.idleTimer=function(b,c){var d;"object"==typeof b?(d=b,b=null):"number"==typeof b&&(d={timeout:b},b=null),c=c||document,d=a.extend({idle:!1,timeout:3e4,events:"mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove"},d);var e=a(c),f=e.data("idleTimerObj")||{},g=function(b){var d=a.data(c,"idleTimerObj")||{};d.idle=!d.idle,d.olddate=+new Date;var e=a.Event((d.idle?"idle":"active")+".idleTimer");a(c).trigger(e,[c,a.extend({},d),b])},h=function(b){var d=a.data(c,"idleTimerObj")||{};if(("storage"!==b.type||b.originalEvent.key===d.timerSyncId)&&null==d.remaining){if("mousemove"===b.type){if(b.pageX===d.pageX&&b.pageY===d.pageY)return;if("undefined"==typeof b.pageX&&"undefined"==typeof b.pageY)return;var e=+new Date-d.olddate;if(200>e)return}clearTimeout(d.tId),d.idle&&g(b),d.lastActive=+new Date,d.pageX=b.pageX,d.pageY=b.pageY,"storage"!==b.type&&d.timerSyncId&&"undefined"!=typeof localStorage&&localStorage.setItem(d.timerSyncId,d.lastActive),d.tId=setTimeout(g,d.timeout)}},i=function(){var b=a.data(c,"idleTimerObj")||{};b.idle=b.idleBackup,b.olddate=+new Date,b.lastActive=b.olddate,b.remaining=null,clearTimeout(b.tId),b.idle||(b.tId=setTimeout(g,b.timeout))},j=function(){var b=a.data(c,"idleTimerObj")||{};null==b.remaining&&(b.remaining=b.timeout-(+new Date-b.olddate),clearTimeout(b.tId))},k=function(){var b=a.data(c,"idleTimerObj")||{};null!=b.remaining&&(b.idle||(b.tId=setTimeout(g,b.remaining)),b.remaining=null)},l=function(){var b=a.data(c,"idleTimerObj")||{};clearTimeout(b.tId),e.removeData("idleTimerObj"),e.off("._idleTimer")},m=function(){var b=a.data(c,"idleTimerObj")||{};if(b.idle)return 0;if(null!=b.remaining)return b.remaining;var d=b.timeout-(+new Date-b.lastActive);return 0>d&&(d=0),d};if(null===b&&"undefined"!=typeof f.idle)return i(),e;if(null===b);else{if(null!==b&&"undefined"==typeof f.idle)return!1;if("destroy"===b)return l(),e;if("pause"===b)return j(),e;if("resume"===b)return k(),e;if("reset"===b)return i(),e;if("getRemainingTime"===b)return m();if("getElapsedTime"===b)return+new Date-f.olddate;if("getLastActiveTime"===b)return f.lastActive;if("isIdle"===b)return f.idle}return e.on(a.trim((d.events+" ").split(" ").join("._idleTimer ")),function(a){h(a)}),d.timerSyncId&&a(window).bind("storage",h),f=a.extend({},{olddate:+new Date,lastActive:+new Date,idle:d.idle,idleBackup:d.idle,timeout:d.timeout,remaining:null,timerSyncId:d.timerSyncId,tId:null,pageX:null,pageY:null}),f.idle||(f.tId=setTimeout(g,f.timeout)),a.data(c,"idleTimerObj",f),e},a.fn.idleTimer=function(b){return this[0]?a.idleTimer(b,this[0]):this}}(jQuery); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /dist/idle-timer.1.1.1.min.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer v1.1.1 2020-06-25 | https://github.com/thorst/jquery-idletimer | (c) 2020 Paul Irish | Licensed MIT */ 2 | 3 | !function(c){c.idleTimer=function(e,n){var i;"object"==typeof e?(i=e,e=null):"number"==typeof e&&(i={timeout:e},e=null),n=n||document,i=c.extend({idle:!1,timeout:3e4,events:"mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove"},i);function t(e){var i=c.data(n,"idleTimerObj")||{};i.idle=!i.idle,i.olddate=+new Date;var t=c.Event((i.idle?"idle":"active")+".idleTimer");c(n).trigger(t,[n,c.extend({},i),e])}function r(e){var i=c.data(n,"idleTimerObj")||{};if(("storage"!==e.type||e.originalEvent.key===i.timerSyncId)&&null==i.remaining){if("mousemove"===e.type){if(e.pageX===i.pageX&&e.pageY===i.pageY)return;if(void 0===e.pageX&&void 0===e.pageY)return;if(new Date-i.olddate<200)return}clearTimeout(i.tId),i.idle&&t(e),i.lastActive=+new Date,i.pageX=e.pageX,i.pageY=e.pageY,"storage"!==e.type&&i.timerSyncId&&"undefined"!=typeof localStorage&&localStorage.setItem(i.timerSyncId,i.lastActive),i.tId=setTimeout(t,i.timeout)}}function l(){var e=c.data(n,"idleTimerObj")||{};e.idle=e.idleBackup,e.olddate=+new Date,e.lastActive=e.olddate,e.remaining=null,clearTimeout(e.tId),e.idle||(e.tId=setTimeout(t,e.timeout))}var a,d,o,u=c(n),m=u.data("idleTimerObj")||{};if(null===e&&void 0!==m.idle)return l(),u;if(null!==e){if(null!==e&&void 0===m.idle)return!1;if("destroy"===e)return o=c.data(n,"idleTimerObj")||{},clearTimeout(o.tId),u.removeData("idleTimerObj"),u.off("._idleTimer"),u;if("pause"===e)return null==(d=c.data(n,"idleTimerObj")||{}).remaining&&(d.remaining=d.timeout-(new Date-d.olddate),clearTimeout(d.tId)),u;if("resume"===e)return null!=(a=c.data(n,"idleTimerObj")||{}).remaining&&(a.idle||(a.tId=setTimeout(t,a.remaining)),a.remaining=null),u;if("reset"===e)return l(),u;if("getRemainingTime"===e)return function(){var e=c.data(n,"idleTimerObj")||{};if(e.idle)return 0;if(null!=e.remaining)return e.remaining;var i=e.timeout-(new Date-e.lastActive);return i<0&&(i=0),i}();if("getElapsedTime"===e)return new Date-m.olddate;if("getLastActiveTime"===e)return m.lastActive;if("isIdle"===e)return m.idle}return u.on((i.events+" ").split(" ").join("._idleTimer ").trim(),function(e){r(e)}),i.timerSyncId&&c(window).on("storage",r),(m=c.extend({},{olddate:+new Date,lastActive:+new Date,idle:i.idle,idleBackup:i.idle,timeout:i.timeout,remaining:null,timerSyncId:i.timerSyncId,tId:null,pageX:null,pageY:null})).idle||(m.tId=setTimeout(t,m.timeout)),c.data(n,"idleTimerObj",m),u},c.fn.idleTimer=function(e){return this[0]?c.idleTimer(e,this[0]):this}}(jQuery); -------------------------------------------------------------------------------- /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.min.js: -------------------------------------------------------------------------------- 1 | /*! Idle Timer v1.1.1 2020-06-25 | https://github.com/thorst/jquery-idletimer | (c) 2020 Paul Irish | Licensed MIT */ 2 | 3 | !function(c){c.idleTimer=function(e,n){var i;"object"==typeof e?(i=e,e=null):"number"==typeof e&&(i={timeout:e},e=null),n=n||document,i=c.extend({idle:!1,timeout:3e4,events:"mousemove keydown wheel DOMMouseScroll mousewheel mousedown touchstart touchmove MSPointerDown MSPointerMove"},i);function t(e){var i=c.data(n,"idleTimerObj")||{};i.idle=!i.idle,i.olddate=+new Date;var t=c.Event((i.idle?"idle":"active")+".idleTimer");c(n).trigger(t,[n,c.extend({},i),e])}function r(e){var i=c.data(n,"idleTimerObj")||{};if(("storage"!==e.type||e.originalEvent.key===i.timerSyncId)&&null==i.remaining){if("mousemove"===e.type){if(e.pageX===i.pageX&&e.pageY===i.pageY)return;if(void 0===e.pageX&&void 0===e.pageY)return;if(new Date-i.olddate<200)return}clearTimeout(i.tId),i.idle&&t(e),i.lastActive=+new Date,i.pageX=e.pageX,i.pageY=e.pageY,"storage"!==e.type&&i.timerSyncId&&"undefined"!=typeof localStorage&&localStorage.setItem(i.timerSyncId,i.lastActive),i.tId=setTimeout(t,i.timeout)}}function l(){var e=c.data(n,"idleTimerObj")||{};e.idle=e.idleBackup,e.olddate=+new Date,e.lastActive=e.olddate,e.remaining=null,clearTimeout(e.tId),e.idle||(e.tId=setTimeout(t,e.timeout))}var a,d,o,u=c(n),m=u.data("idleTimerObj")||{};if(null===e&&void 0!==m.idle)return l(),u;if(null!==e){if(null!==e&&void 0===m.idle)return!1;if("destroy"===e)return o=c.data(n,"idleTimerObj")||{},clearTimeout(o.tId),u.removeData("idleTimerObj"),u.off("._idleTimer"),u;if("pause"===e)return null==(d=c.data(n,"idleTimerObj")||{}).remaining&&(d.remaining=d.timeout-(new Date-d.olddate),clearTimeout(d.tId)),u;if("resume"===e)return null!=(a=c.data(n,"idleTimerObj")||{}).remaining&&(a.idle||(a.tId=setTimeout(t,a.remaining)),a.remaining=null),u;if("reset"===e)return l(),u;if("getRemainingTime"===e)return function(){var e=c.data(n,"idleTimerObj")||{};if(e.idle)return 0;if(null!=e.remaining)return e.remaining;var i=e.timeout-(new Date-e.lastActive);return i<0&&(i=0),i}();if("getElapsedTime"===e)return new Date-m.olddate;if("getLastActiveTime"===e)return m.lastActive;if("isIdle"===e)return m.idle}return u.on((i.events+" ").split(" ").join("._idleTimer ").trim(),function(e){r(e)}),i.timerSyncId&&c(window).on("storage",r),(m=c.extend({},{olddate:+new Date,lastActive:+new Date,idle:i.idle,idleBackup:i.idle,timeout:i.timeout,remaining:null,timerSyncId:i.timerSyncId,tId:null,pageX:null,pageY:null})).idle||(m.tId=setTimeout(t,m.timeout)),c.data(n,"idleTimerObj",m),u},c.fn.idleTimer=function(e){return this[0]?c.idleTimer(e,this[0]):this}}(jQuery); -------------------------------------------------------------------------------- /idle-timer.jquery.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "idle-timer", 3 | "title": "Idle Timer", 4 | "description": "provides you a way to monitor user activity with a page.", 5 | "version": "1.1.1", 6 | "homepage": "https://github.com/thorst/jquery-idletimer", 7 | "author": { 8 | "name": "Paul Irish", 9 | "email": "paul.irish@gmail.com" 10 | }, 11 | "maintainers": [ 12 | { 13 | "name": "Mike Sherov", 14 | "email": "mike.sherov@gmail.com" 15 | }, 16 | { 17 | "name": "Todd Horst", 18 | "email": "toddmhorst@yahoo.com" 19 | } 20 | ], 21 | "repository": { 22 | "type": "git", 23 | "url": "git://github.com/thorst/jquery-idletimer.git" 24 | }, 25 | "bugs": "https://github.com/thorst/jquery-idletimer/issues", 26 | "licenses": [ 27 | { 28 | "type": "MIT", 29 | "url": "https://github.com/thorst/jquery-idletimer/blob/master/LICENSE-MIT" 30 | } 31 | ], 32 | "dependencies": { 33 | "jquery": "1.7" 34 | }, 35 | "devDependencies": { 36 | "grunt": "^1.1.0", 37 | "grunt-contrib-concat": "^1.0.1", 38 | "grunt-contrib-jshint": "^2.1.0", 39 | "grunt-contrib-qunit": "^4.0.0", 40 | "grunt-contrib-uglify": "^4.0.1", 41 | "grunt-contrib-watch": "^1.1.0" 42 | }, 43 | "keywords": [] 44 | } 45 | -------------------------------------------------------------------------------- /jquery-idletimer.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "jquery-idletimer", "http://localhost:3751", "{70FF000C-3D87-429F-86E5-C99C2CA464E0}" 7 | ProjectSection(WebsiteProperties) = preProject 8 | UseIISExpress = "true" 9 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" 10 | Debug.AspNetCompiler.VirtualPath = "/localhost_3751" 11 | Debug.AspNetCompiler.PhysicalPath = "..\..\..\GitHub\jquery-idletimer\" 12 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3751\" 13 | Debug.AspNetCompiler.Updateable = "true" 14 | Debug.AspNetCompiler.ForceOverwrite = "true" 15 | Debug.AspNetCompiler.FixedNames = "false" 16 | Debug.AspNetCompiler.Debug = "True" 17 | Release.AspNetCompiler.VirtualPath = "/localhost_3751" 18 | Release.AspNetCompiler.PhysicalPath = "..\..\..\GitHub\jquery-idletimer\" 19 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_3751\" 20 | Release.AspNetCompiler.Updateable = "true" 21 | Release.AspNetCompiler.ForceOverwrite = "true" 22 | Release.AspNetCompiler.FixedNames = "false" 23 | Release.AspNetCompiler.Debug = "False" 24 | SlnRelativePath = "..\..\..\GitHub\jquery-idletimer\" 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {70FF000C-3D87-429F-86E5-C99C2CA464E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {70FF000C-3D87-429F-86E5-C99C2CA464E0}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /jquery-idletimer.v12.suo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thorst/jquery-idletimer/d52354c25ff688fe16550338bbac9c69218b1326/jquery-idletimer.v12.suo -------------------------------------------------------------------------------- /libs/jquery-loader.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | // Default to the local version. 3 | var path = '../libs/jquery/jquery.js'; 4 | // Get any jquery=___ param from the query string. 5 | var jqversion = location.search.match(/[?&]jquery=(.*?)(?=&|$)/); 6 | // If a version was specified, use that version from code.jquery.com. 7 | if (jqversion) { 8 | path = 'http://code.jquery.com/jquery-' + jqversion[1] + '.js'; 9 | } 10 | // This is the only time I'll ever use document.write, I promise! 11 | document.write(''); 12 | }()); 13 | -------------------------------------------------------------------------------- /libs/qunit/qunit.css: -------------------------------------------------------------------------------- 1 | /** 2 | * QUnit v1.4.0 - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2012 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | */ 10 | 11 | /** Font Family and Sizes */ 12 | 13 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 14 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; 15 | } 16 | 17 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 18 | #qunit-tests { font-size: smaller; } 19 | 20 | 21 | /** Resets */ 22 | 23 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 24 | margin: 0; 25 | padding: 0; 26 | } 27 | 28 | 29 | /** Header */ 30 | 31 | #qunit-header { 32 | padding: 0.5em 0 0.5em 1em; 33 | 34 | color: #8699a4; 35 | background-color: #0d3349; 36 | 37 | font-size: 1.5em; 38 | line-height: 1em; 39 | font-weight: normal; 40 | 41 | border-radius: 15px 15px 0 0; 42 | -moz-border-radius: 15px 15px 0 0; 43 | -webkit-border-top-right-radius: 15px; 44 | -webkit-border-top-left-radius: 15px; 45 | } 46 | 47 | #qunit-header a { 48 | text-decoration: none; 49 | color: #c2ccd1; 50 | } 51 | 52 | #qunit-header a:hover, 53 | #qunit-header a:focus { 54 | color: #fff; 55 | } 56 | 57 | #qunit-header label { 58 | display: inline-block; 59 | } 60 | 61 | #qunit-banner { 62 | height: 5px; 63 | } 64 | 65 | #qunit-testrunner-toolbar { 66 | padding: 0.5em 0 0.5em 2em; 67 | color: #5E740B; 68 | background-color: #eee; 69 | } 70 | 71 | #qunit-userAgent { 72 | padding: 0.5em 0 0.5em 2.5em; 73 | background-color: #2b81af; 74 | color: #fff; 75 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 76 | } 77 | 78 | 79 | /** Tests: Pass/Fail */ 80 | 81 | #qunit-tests { 82 | list-style-position: inside; 83 | } 84 | 85 | #qunit-tests li { 86 | padding: 0.4em 0.5em 0.4em 2.5em; 87 | border-bottom: 1px solid #fff; 88 | list-style-position: inside; 89 | } 90 | 91 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 92 | display: none; 93 | } 94 | 95 | #qunit-tests li strong { 96 | cursor: pointer; 97 | } 98 | 99 | #qunit-tests li a { 100 | padding: 0.5em; 101 | color: #c2ccd1; 102 | text-decoration: none; 103 | } 104 | #qunit-tests li a:hover, 105 | #qunit-tests li a:focus { 106 | color: #000; 107 | } 108 | 109 | #qunit-tests ol { 110 | margin-top: 0.5em; 111 | padding: 0.5em; 112 | 113 | background-color: #fff; 114 | 115 | border-radius: 15px; 116 | -moz-border-radius: 15px; 117 | -webkit-border-radius: 15px; 118 | 119 | box-shadow: inset 0px 2px 13px #999; 120 | -moz-box-shadow: inset 0px 2px 13px #999; 121 | -webkit-box-shadow: inset 0px 2px 13px #999; 122 | } 123 | 124 | #qunit-tests table { 125 | border-collapse: collapse; 126 | margin-top: .2em; 127 | } 128 | 129 | #qunit-tests th { 130 | text-align: right; 131 | vertical-align: top; 132 | padding: 0 .5em 0 0; 133 | } 134 | 135 | #qunit-tests td { 136 | vertical-align: top; 137 | } 138 | 139 | #qunit-tests pre { 140 | margin: 0; 141 | white-space: pre-wrap; 142 | word-wrap: break-word; 143 | } 144 | 145 | #qunit-tests del { 146 | background-color: #e0f2be; 147 | color: #374e0c; 148 | text-decoration: none; 149 | } 150 | 151 | #qunit-tests ins { 152 | background-color: #ffcaca; 153 | color: #500; 154 | text-decoration: none; 155 | } 156 | 157 | /*** Test Counts */ 158 | 159 | #qunit-tests b.counts { color: black; } 160 | #qunit-tests b.passed { color: #5E740B; } 161 | #qunit-tests b.failed { color: #710909; } 162 | 163 | #qunit-tests li li { 164 | margin: 0.5em; 165 | padding: 0.4em 0.5em 0.4em 0.5em; 166 | background-color: #fff; 167 | border-bottom: none; 168 | list-style-position: inside; 169 | } 170 | 171 | /*** Passing Styles */ 172 | 173 | #qunit-tests li li.pass { 174 | color: #5E740B; 175 | background-color: #fff; 176 | border-left: 26px solid #C6E746; 177 | } 178 | 179 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 180 | #qunit-tests .pass .test-name { color: #366097; } 181 | 182 | #qunit-tests .pass .test-actual, 183 | #qunit-tests .pass .test-expected { color: #999999; } 184 | 185 | #qunit-banner.qunit-pass { background-color: #C6E746; } 186 | 187 | /*** Failing Styles */ 188 | 189 | #qunit-tests li li.fail { 190 | color: #710909; 191 | background-color: #fff; 192 | border-left: 26px solid #EE5757; 193 | white-space: pre; 194 | } 195 | 196 | #qunit-tests > li:last-child { 197 | border-radius: 0 0 15px 15px; 198 | -moz-border-radius: 0 0 15px 15px; 199 | -webkit-border-bottom-right-radius: 15px; 200 | -webkit-border-bottom-left-radius: 15px; 201 | } 202 | 203 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 204 | #qunit-tests .fail .test-name, 205 | #qunit-tests .fail .module-name { color: #000000; } 206 | 207 | #qunit-tests .fail .test-actual { color: #EE5757; } 208 | #qunit-tests .fail .test-expected { color: green; } 209 | 210 | #qunit-banner.qunit-fail { background-color: #EE5757; } 211 | 212 | 213 | /** Result */ 214 | 215 | #qunit-testresult { 216 | padding: 0.5em 0.5em 0.5em 2.5em; 217 | 218 | color: #2b81af; 219 | background-color: #D2E0E6; 220 | 221 | border-bottom: 1px solid white; 222 | } 223 | 224 | /** Fixture */ 225 | 226 | #qunit-fixture { 227 | position: absolute; 228 | top: -10000px; 229 | left: -10000px; 230 | width: 1000px; 231 | height: 1000px; 232 | } 233 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-idletimer", 3 | "main": "dist/idle-timer.js", 4 | "version": "1.1.1", 5 | "engines": { 6 | "node": ">= 10.0.0" 7 | }, 8 | "scripts": { 9 | "test": "grunt qunit" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git://github.com/thorst/jquery-idletimer.git" 14 | }, 15 | "devDependencies": { 16 | "grunt": "^1.1.0", 17 | "grunt-contrib-concat": "^1.0.1", 18 | "grunt-contrib-jshint": "^2.1.0", 19 | "grunt-contrib-qunit": "^4.0.0", 20 | "grunt-contrib-uglify": "^4.0.1", 21 | "grunt-contrib-watch": "^1.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "browser": true, 14 | "quotmark": "double", 15 | "predef": ["jQuery"] 16 | } 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": true, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "unused": true, 11 | "boss": true, 12 | "eqnull": true, 13 | "browser": true, 14 | "quotmark": "double", 15 | "predef": [ 16 | "jQuery", 17 | "QUnit", 18 | "module", 19 | "test", 20 | "asyncTest", 21 | "expect", 22 | "start", 23 | "stop", 24 | "ok", 25 | "equal", 26 | "notEqual", 27 | "deepEqual", 28 | "notDeepEqual", 29 | "strictEqual", 30 | "notStrictEqual", 31 | "raises" 32 | ] 33 | } -------------------------------------------------------------------------------- /test/idle-timer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Idle Timer Test Suite 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 |

Idle Timer Test Suite

21 |

22 |
23 |

24 |
    25 |
    26 | lame test markup 27 | normal test markup 28 | awesome test markup 29 |
    30 | 31 | 32 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------