vendor: 17,655 bytes, lines 1-564
1/*** Directives and services for responding to idle users in AngularJS 2* @author Mike Grabski <[email protected]> 3* @version v1.3.2 4* @link https://github.com/HackedByChinese/ng-idle.git 5* @license MIT 6*/ 7(function(window, angular, undefined) { 8'use strict'; 9angular.module('ngIdle', ['ngIdle.keepalive', 'ngIdle.idle', 'ngIdle.countdown', 'ngIdle.title', 'ngIdle.localStorage']); 10angular.module('ngIdle.keepalive', []) 11 .provider('Keepalive', function() { 12 var options = { 13 http: null, 14 interval: 10 * 60 15 }; 16 17 this.http = function(value) { 18 if (!value) throw new Error('Argument must be a string containing a URL, or an object containing the HTTP request configuration.'); 19 if (angular.isString(value)) { 20 value = { 21 url: value, 22 method: 'GET' 23 }; 24 } 25 26 value.cache = false; 27 28 options.http = value; 29 }; 30 31 var setInterval = this.interval = function(seconds) { 32 seconds = parseInt(seconds); 33 34 if (isNaN(seconds) || seconds <= 0) throw new Error('Interval must be expressed in seconds and be greater than 0.'); 35 options.interval = seconds; 36 }; 37 38 this.$get = ['$rootScope', '$log', '$interval', '$http', 39 function($rootScope, $log, $interval, $http) { 40 41 var state = { 42 ping: null 43 }; 44 45 function handleResponse(response) { 46 $rootScope.$broadcast('KeepaliveResponse', response.data, response.status); 47 } 48 49 function ping() { 50 $rootScope.$broadcast('Keepalive'); 51 52 if (angular.isObject(options.http)) { 53 $http(options.http) 54 .then(handleResponse) 55 .catch(handleResponse); 56 } 57 } 58 59 return { 60 _options: function() { 61 return options; 62 }, 63 setInterval: setInterval, 64 start: function() { 65 $interval.cancel(state.ping); 66 67 state.ping = $interval(ping, options.interval * 1000); 68 return state.ping; 69 }, 70 stop: function() { 71 $interval.cancel(state.ping); 72 }, 73 ping: function() { 74 ping(); 75 } 76 }; 77 } 78 ]; 79 }); 80 81angular.module('ngIdle.idle', ['ngIdle.keepalive', 'ngIdle.localStorage']) 82 .provider('Idle', function() { 83 var options = { 84 idle: 20 * 60, // in seconds (default is 20min) 85 timeout: 30, // in seconds (default is 30sec) 86 autoResume: 'idle', // lets events automatically resume (unsets idle state/resets warning) 87 interrupt: 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove scroll', 88 windowInterrupt: null, 89 keepalive: true 90 }; 91 92 /** 93 * Sets the number of seconds a user can be idle before they are considered timed out. 94 * @param {Number|Boolean} seconds A positive number representing seconds OR 0 or false to disable this feature. 95 */ 96 var setTimeout = this.timeout = function(seconds) { 97 if (seconds === false) options.timeout = 0; 98 else if (angular.isNumber(seconds) && seconds >= 0) options.timeout = seconds; 99 else throw new Error('Timeout must be zero or false to disable the feature, or a positive integer (in seconds) to enable it.'); 100 }; 101 102 this.interrupt = function(events) { 103 options.interrupt = events; 104 }; 105 106 this.windowInterrupt = function(events) { 107 options.windowInterrupt = events; 108 }; 109 110 var setIdle = this.idle = function(seconds) { 111 if (seconds <= 0) throw new Error('Idle must be a value in seconds, greater than 0.'); 112 113 options.idle = seconds; 114 }; 115 116 this.autoResume = function(value) { 117 if (value === true) options.autoResume = 'idle'; 118 else if (value === false) options.autoResume = 'off'; 119 else options.autoResume = value; 120 }; 121 122 this.keepalive = function(enabled) { 123 options.keepalive = enabled === true; 124 }; 125 126 this.$get = ['$interval', '$log', '$rootScope', '$document', 'Keepalive', 'IdleLocalStorage', '$window', 127 function($interval, $log, $rootScope, $document, Keepalive, LocalStorage, $window) { 128 var state = { 129 idle: null, 130 timeout: null, 131 idling: false, 132 running: false, 133 countdown: null, 134 idleIntervalStarted: null 135 }; 136 137 var id = new Date().getTime(); 138 139 function startKeepalive() { 140 if (!options.keepalive) return; 141 142 if (state.running) Keepalive.ping(); 143 144 Keepalive.start(); 145 } 146 147 function stopKeepalive() { 148 if (!options.keepalive) return; 149 150 Keepalive.stop(); 151 } 152 153 function toggleState() { 154 state.idling = !state.idling; 155 var name = state.idling ? 'IdleStart' : 'IdleEnd'; 156 157 if (state.idling) { 158 $rootScope.$broadcast(name); 159 stopKeepalive(); 160 if (options.timeout) { 161 state.countdown = options.timeout; 162 countdown(); 163 state.timeout = $interval(countdown, 1000, options.timeout, false); 164 } 165 } else { 166 startKeepalive(); 167 $rootScope.$broadcast(name); 168 } 169 170 $interval.cancel(state.idle); 171 } 172 173 function countdown() { 174 175 // check not called when no longer idling 176 // possible with multiple tabs 177 if(!state.idling){ 178 return; 179 } 180 181 // countdown has expired, so signal timeout 182 if (state.countdown <= 0) { 183 timeout(); 184 return; 185 } 186 187 // countdown hasn't reached zero, so warn and decrement 188 $rootScope.$broadcast('IdleWarn', state.countdown); 189 state.countdown--; 190 } 191 192 function interrupted(anotherTab) { 193 $rootScope.$broadcast('IdleInterrupt', anotherTab); 194 } 195 196 function timeout() { 197 stopKeepalive(); 198 $interval.cancel(state.idle); 199 $interval.cancel(state.timeout); 200 201 state.idling = true; 202 state.running = false; 203 state.countdown = 0; 204 205 $rootScope.$broadcast('IdleTimeout'); 206 } 207 208 function changeOption(self, fn, value) { 209 var reset = self.running(); 210 211 self.unwatch(); 212 fn(value); 213 if (reset) self.watch(); 214 } 215 216 function getExpiry() { 217 var obj = LocalStorage.get('expiry'); 218 219 return obj && obj.time ? new Date(obj.time) : null; 220 } 221 222 function setExpiry(date) { 223 if (!date) LocalStorage.remove('expiry'); 224 else LocalStorage.set('expiry', {id: id, time: date}); 225 } 226 227 var svc = { 228 _options: function() { 229 return options; 230 }, 231 _getNow: function() { 232 return new Date(); 233 }, 234 getIdle: function(){ 235 return options.idle; 236 }, 237 getTimeout: function(){ 238 return options.timeout; 239 }, 240 setIdle: function(seconds) { 241 changeOption(this, setIdle, seconds); 242 }, 243 setTimeout: function(seconds) { 244 changeOption(this, setTimeout, seconds); 245 }, 246 isExpired: function() { 247 var expiry = getExpiry(); 248 return expiry !== null && expiry <= this._getNow(); 249 }, 250 running: function() { 251 return state.running; 252 }, 253 getTimeUntilIdle: function () { 254 if (state.idleIntervalStarted) { 255 var endTime = state.idleIntervalStarted.getTime() + (options.idle * 1000); 256 return endTime - (new Date()).getTime(); 257 } else { 258 return null; 259 } 260 }, 261 lastActivity: function () { 262 return state.idleIntervalStarted; 263 }, 264 idling: function() { 265 return state.idling; 266 }, 267 watch: function(noExpiryUpdate) { 268 $interval.cancel(state.idle); 269 $interval.cancel(state.timeout); 270 271 // calculate the absolute expiry date, as added insurance against a browser sleeping or paused in the background 272 var timeout = !options.timeout ? 0 : options.timeout; 273 if (!noExpiryUpdate) setExpiry(new Date(new Date().getTime() + ((options.idle + timeout) * 1000))); 274 275 276 if (state.idling) toggleState(); // clears the idle state if currently idling 277 else if (!state.running) startKeepalive(); // if about to run, start keep alive 278 279 state.running = true; 280 state.idleIntervalStarted = new Date(); 281 state.idle = $interval(toggleState, options.idle * 1000, 0, false); 282 }, 283 unwatch: function() { 284 $interval.cancel(state.idle); 285 $interval.cancel(state.timeout); 286 287 state.idling = false; 288 state.running = false; 289 setExpiry(null); 290 291 stopKeepalive(); 292 }, 293 interrupt: function(anotherTab) { 294 if (!state.running) return; 295 296 if (options.timeout && this.isExpired()) { 297 timeout(); 298 return; 299 } else { 300 interrupted(anotherTab); 301 } 302 303 // note: you can no longer auto resume once we exceed the expiry; you will reset state by calling watch() manually 304 if (anotherTab || options.autoResume === 'idle' || (options.autoResume === 'notIdle' && !state.idling)) this.watch(anotherTab); 305 } 306 }; 307 308 var lastMove = { 309 clientX: null, 310 clientY: null, 311 swap: function(event) { 312 var last = {clientX: this.clientX, clientY: this.clientY}; 313 this.clientX = event.clientX; 314 this.clientY = event.clientY; 315 return last; 316 }, 317 hasMoved: function(event) { 318 var last = this.swap(event); 319 if (this.clientX === null || event.movementX || event.movementY) return true; 320 else if (last.clientX != event.clientX || last.clientY != event.clientY) return true; 321 else return false; 322 } 323 }; 324 325 $document.find('html').on(options.interrupt, function(event) { 326 if (event.type === 'mousemove' && event.originalEvent && event.originalEvent.movementX === 0 && event.originalEvent.movementY === 0) { 327 return; // Fix for Chrome desktop notifications, triggering mousemove event. 328 } 329 330 if (event.type !== 'mousemove' || lastMove.hasMoved(event)) { 331 svc.interrupt(); 332 } 333 }); 334 335 if(options.windowInterrupt) { 336 var eventList = options.windowInterrupt.split(' '); 337 var fn = function() { 338 svc.interrupt(); 339 }; 340 341 for(var i=0; i<eventList.length; i++) { 342 if ($window.addEventListener) { 343 $window.addEventListener(eventList[i], fn, false); 344 $rootScope.$on('$destroy', function () { 345 $window.removeEventListener(eventList[i], fn, false); 346 }); 347 } else { 348 $window.attachEvent(eventList[i], fn); 349 $rootScope.$on('$destroy', function () { 350 $window.detachEvent(eventList[i], fn); 351 }); 352 } 353 } 354 } 355 356 var wrap = function(event) { 357 if (event.key === 'ngIdle.expiry' && event.newValue && event.newValue !== event.oldValue) { 358 var val = angular.fromJson(event.newValue); 359 if (val.id === id) return; 360 svc.interrupt(true); 361 } 362 }; 363 364 if ($window.addEventListener) { 365 $window.addEventListener('storage', wrap, false); 366 $rootScope.$on('$destroy', function () { 367 $window.removeEventListener('storage', wrap, false); 368 }); 369 } else if ($window.attachEvent){ 370 $window.attachEvent('onstorage', wrap); 371 $rootScope.$on('$destroy', function () { 372 $window.detachEvent('onstorage', wrap); 373 }); 374 } 375 376 return svc; 377 } 378 ]; 379 }); 380 381angular.module('ngIdle.countdown', ['ngIdle.idle']) 382 .directive('idleCountdown', ['Idle', function(Idle) { 383 return { 384 restrict: 'A', 385 scope: { 386 value: '=idleCountdown' 387 }, 388 link: function($scope) { 389 // Initialize the scope's value to the configured timeout. 390 $scope.value = Idle.getTimeout(); 391 392 $scope.$on('IdleWarn', function(e, countdown) { 393 $scope.$evalAsync(function() { 394 $scope.value = countdown; 395 }); 396 }); 397 398 $scope.$on('IdleTimeout', function() { 399 $scope.$evalAsync(function() { 400 $scope.value = 0; 401 }); 402 }); 403 } 404 }; 405 }]); 406 407angular.module('ngIdle.title', []) 408 .provider('Title', function() { 409 var options = { 410 enabled: true 411 }; 412 413 var setEnabled = this.enabled = function(enabled) { 414 options.enabled = enabled === true; 415 }; 416 417 function padLeft(nr, n, str){ 418 return new Array(n-String(nr).length+1).join(str||'0')+nr; 419 } 420 421 this.$get = ['$document', '$interpolate', function($document, $interpolate) { 422 var state = { 423 original: null, 424 idle: '{{minutes}}:{{seconds}} until your session times out!', 425 timedout: 'Your session has expired.' 426 }; 427 428 return { 429 setEnabled: setEnabled, 430 isEnabled: function() { 431 return options.enabled; 432 }, 433 original: function(val) { 434 if (angular.isUndefined(val)) return state.original; 435 436 state.original = val; 437 }, 438 store: function(overwrite) { 439 if (overwrite || !state.original) state.original = this.value(); 440 }, 441 value: function(val) { 442 if (angular.isUndefined(val)) return $document[0].title; 443 444 $document[0].title = val; 445 }, 446 idleMessage: function(val) { 447 if (angular.isUndefined(val)) return state.idle; 448 449 state.idle = val; 450 }, 451 timedOutMessage: function(val) { 452 if (angular.isUndefined(val)) return state.timedout; 453 454 state.timedout = val; 455 }, 456 setAsIdle: function(countdown) { 457 this.store(); 458 459 var remaining = { totalSeconds: countdown }; 460 remaining.minutes = Math.floor(countdown/60); 461 remaining.seconds = padLeft(countdown - remaining.minutes * 60, 2); 462 463 this.value($interpolate(this.idleMessage())(remaining)); 464 }, 465 setAsTimedOut: function() { 466 this.store(); 467 468 this.value(this.timedOutMessage()); 469 }, 470 restore: function() { 471 if (this.original()) this.value(this.original()); 472 } 473 }; 474 }]; 475 }) 476 .directive('title', ['Title', function(Title) { 477 return { 478 restrict: 'E', 479 link: function($scope, $element, $attr) { 480 if (!Title.isEnabled() || $attr.idleDisabled) return; 481 482 Title.store(true); 483 484 $scope.$on('IdleStart', function() { 485 Title.original($element[0].innerText); 486 }); 487 488 $scope.$on('IdleWarn', function(e, countdown) { 489 Title.setAsIdle(countdown); 490 }); 491 492 $scope.$on('IdleEnd', function() { 493 Title.restore(); 494 }); 495 496 $scope.$on('IdleTimeout', function() { 497 Title.setAsTimedOut(); 498 }); 499 } 500 }; 501 }]); 502 503angular.module('ngIdle.localStorage', []) 504 .service('IdleStorageAccessor', ['$window', function($window) { 505 return { 506 get: function() { 507 return $window.localStorage; 508 } 509 } 510 }]) 511 .service('IdleLocalStorage', ['IdleStorageAccessor', function(IdleStorageAccessor) { 512 function AlternativeStorage() { 513 var storageMap = {}; 514 515 this.setItem = function (key, value) { 516 storageMap[key] = value; 517 }; 518 519 this.getItem = function (key) { 520 if(typeof storageMap[key] !== 'undefined' ) { 521 return storageMap[key]; 522 } 523 return null; 524 }; 525 526 this.removeItem = function (key) { 527 storageMap[key] = undefined; 528 }; 529 } 530 531 function getStorage() { 532 try { 533 var s = IdleStorageAccessor.get(); 534 s.setItem('ngIdleStorage', ''); 535 s.removeItem('ngIdleStorage'); 536 537 return s; 538 } catch(err) { 539 return new AlternativeStorage(); 540 } 541 } 542 543 // Safari, in Private Browsing Mode, looks like it supports localStorage but all calls to setItem 544 // throw QuotaExceededError. We're going to detect this and just silently drop any calls to setItem 545 // to avoid the entire page breaking, without having to do a check at each usage of Storage. 546 var storage = getStorage(); 547 548 return { 549 set: function(key, value) { 550 storage.setItem('ngIdle.'+key, angular.toJson(value)); 551 }, 552 get: function(key) { 553 return angular.fromJson(storage.getItem('ngIdle.'+key)); 554 }, 555 remove: function(key) { 556 storage.removeItem('ngIdle.'+key); 557 }, 558 _wrapped: function() { 559 return storage; 560 } 561 }; 562}]); 563 564})(window, window.angular);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.