| 5 |
lars |
1 |
/*
|
|
|
2 |
* bootstrap-session-timeout
|
|
|
3 |
* www.orangehilldev.com
|
|
|
4 |
*
|
|
|
5 |
* Copyright (c) 2014 Vedran Opacic
|
|
|
6 |
* Licensed under the MIT license.
|
|
|
7 |
*/
|
|
|
8 |
|
|
|
9 |
(function($) {
|
|
|
10 |
/*jshint multistr: true */
|
|
|
11 |
'use strict';
|
|
|
12 |
$.sessionTimeout = function(options) {
|
|
|
13 |
var defaults = {
|
|
|
14 |
title: 'Your Session is About to Expire!',
|
|
|
15 |
message: 'Your session is about to expire.',
|
|
|
16 |
logoutButton: 'Logout',
|
|
|
17 |
keepAliveButton: 'Stay Connected',
|
|
|
18 |
keepAliveUrl: '/keep-alive',
|
|
|
19 |
ajaxType: 'POST',
|
|
|
20 |
ajaxData: '',
|
|
|
21 |
redirUrl: '/timed-out',
|
|
|
22 |
logoutUrl: '/log-out',
|
|
|
23 |
warnAfter: 900000, // 15 minutes
|
|
|
24 |
redirAfter: 1200000, // 20 minutes
|
|
|
25 |
keepAliveInterval: 5000,
|
|
|
26 |
keepAlive: true,
|
|
|
27 |
ignoreUserActivity: false,
|
|
|
28 |
onStart: false,
|
|
|
29 |
onWarn: false,
|
|
|
30 |
onRedir: false,
|
|
|
31 |
countdownMessage: false,
|
|
|
32 |
countdownBar: false,
|
|
|
33 |
countdownSmart: false
|
|
|
34 |
};
|
|
|
35 |
|
|
|
36 |
var opt = defaults,
|
|
|
37 |
timer,
|
|
|
38 |
countdown = {};
|
|
|
39 |
|
|
|
40 |
// Extend user-set options over defaults
|
|
|
41 |
if (options) {
|
|
|
42 |
opt = $.extend(defaults, options);
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
// Some error handling if options are miss-configured
|
|
|
46 |
if (opt.warnAfter >= opt.redirAfter) {
|
|
|
47 |
console.error('Bootstrap-session-timeout plugin is miss-configured. Option "redirAfter" must be equal or greater than "warnAfter".');
|
|
|
48 |
return false;
|
|
|
49 |
}
|
|
|
50 |
|
|
|
51 |
// Unless user set his own callback function, prepare bootstrap modal elements and events
|
|
|
52 |
if (typeof opt.onWarn !== 'function') {
|
|
|
53 |
// If opt.countdownMessage is defined add a coundown timer message to the modal dialog
|
|
|
54 |
var countdownMessage = opt.countdownMessage ?
|
|
|
55 |
'<p>' + opt.countdownMessage.replace(/{timer}/g, '<span class="countdown-holder"></span>') + '</p>' : '';
|
|
|
56 |
var coundownBarHtml = opt.countdownBar ?
|
|
|
57 |
'<div class="progress"> \
|
|
|
58 |
<div class="progress-bar progress-bar-striped countdown-bar active" role="progressbar" style="min-width: 15px; width: 100%;"> \
|
|
|
59 |
<span class="countdown-holder"></span> \
|
|
|
60 |
</div> \
|
|
|
61 |
</div>' : '';
|
|
|
62 |
|
|
|
63 |
// Create timeout warning dialog
|
|
|
64 |
$('body').append('<div class="modal fade" id="session-timeout-dialog"> \
|
|
|
65 |
<div class="modal-dialog"> \
|
|
|
66 |
<div class="modal-content"> \
|
|
|
67 |
<div class="modal-header"> \
|
|
|
68 |
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> \
|
|
|
69 |
<h4 class="modal-title">' + opt.title + '</h4> \
|
|
|
70 |
</div> \
|
|
|
71 |
<div class="modal-body"> \
|
|
|
72 |
<p>' + opt.message + '</p> \
|
|
|
73 |
' + countdownMessage + ' \
|
|
|
74 |
' + coundownBarHtml + ' \
|
|
|
75 |
</div> \
|
|
|
76 |
<div class="modal-footer"> \
|
|
|
77 |
<button id="session-timeout-dialog-logout" type="button" class="btn btn-default">' + opt.logoutButton + '</button> \
|
|
|
78 |
<button id="session-timeout-dialog-keepalive" type="button" class="btn btn-primary" data-dismiss="modal">' + opt.keepAliveButton + '</button> \
|
|
|
79 |
</div> \
|
|
|
80 |
</div> \
|
|
|
81 |
</div> \
|
|
|
82 |
</div>');
|
|
|
83 |
|
|
|
84 |
// "Logout" button click
|
|
|
85 |
$('#session-timeout-dialog-logout').on('click', function() {
|
|
|
86 |
window.location = opt.logoutUrl;
|
|
|
87 |
});
|
|
|
88 |
// "Stay Connected" button click
|
|
|
89 |
$('#session-timeout-dialog').on('hide.bs.modal', function() {
|
|
|
90 |
// Restart session timer
|
|
|
91 |
startSessionTimer();
|
|
|
92 |
});
|
|
|
93 |
}
|
|
|
94 |
|
|
|
95 |
// Reset timer on any of these events
|
|
|
96 |
if (!opt.ignoreUserActivity) {
|
|
|
97 |
var mousePosition = [-1, -1];
|
|
|
98 |
$(document).on('keyup mouseup mousemove touchend touchmove', function(e) {
|
|
|
99 |
if (e.type === 'mousemove') {
|
|
|
100 |
// Solves mousemove even when mouse not moving issue on Chrome:
|
|
|
101 |
// https://code.google.com/p/chromium/issues/detail?id=241476
|
|
|
102 |
if (e.clientX === mousePosition[0] && e.clientY === mousePosition[1]) {
|
|
|
103 |
return;
|
|
|
104 |
}
|
|
|
105 |
mousePosition[0] = e.clientX;
|
|
|
106 |
mousePosition[1] = e.clientY;
|
|
|
107 |
}
|
|
|
108 |
startSessionTimer();
|
|
|
109 |
|
|
|
110 |
// If they moved the mouse not only reset the counter
|
|
|
111 |
// but remove the modal too!
|
|
|
112 |
if ($('#session-timeout-dialog').length > 0 &&
|
|
|
113 |
$('#session-timeout-dialog').data('bs.modal') &&
|
|
|
114 |
$('#session-timeout-dialog').data('bs.modal').isShown) {
|
|
|
115 |
// http://stackoverflow.com/questions/11519660/twitter-bootstrap-modal-backdrop-doesnt-disappear
|
|
|
116 |
$('#session-timeout-dialog').modal('hide');
|
|
|
117 |
$('body').removeClass('modal-open');
|
|
|
118 |
$('div.modal-backdrop').remove();
|
|
|
119 |
|
|
|
120 |
}
|
|
|
121 |
});
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
// Keeps the server side connection live, by pingin url set in keepAliveUrl option.
|
|
|
125 |
// KeepAlivePinged is a helper var to ensure the functionality of the keepAliveInterval option
|
|
|
126 |
var keepAlivePinged = false;
|
|
|
127 |
|
|
|
128 |
function keepAlive() {
|
|
|
129 |
if (!keepAlivePinged) {
|
|
|
130 |
// Ping keepalive URL using (if provided) data and type from options
|
|
|
131 |
$.ajax({
|
|
|
132 |
type: opt.ajaxType,
|
|
|
133 |
url: opt.keepAliveUrl,
|
|
|
134 |
data: opt.ajaxData
|
|
|
135 |
});
|
|
|
136 |
keepAlivePinged = true;
|
|
|
137 |
setTimeout(function() {
|
|
|
138 |
keepAlivePinged = false;
|
|
|
139 |
}, opt.keepAliveInterval);
|
|
|
140 |
}
|
|
|
141 |
}
|
|
|
142 |
|
|
|
143 |
function startSessionTimer() {
|
|
|
144 |
// Clear session timer
|
|
|
145 |
clearTimeout(timer);
|
|
|
146 |
if (opt.countdownMessage || opt.countdownBar) {
|
|
|
147 |
startCountdownTimer('session', true);
|
|
|
148 |
}
|
|
|
149 |
|
|
|
150 |
if (typeof opt.onStart === 'function') {
|
|
|
151 |
opt.onStart(opt);
|
|
|
152 |
}
|
|
|
153 |
|
|
|
154 |
// If keepAlive option is set to "true", ping the "keepAliveUrl" url
|
|
|
155 |
if (opt.keepAlive) {
|
|
|
156 |
keepAlive();
|
|
|
157 |
}
|
|
|
158 |
|
|
|
159 |
// Set session timer
|
|
|
160 |
timer = setTimeout(function() {
|
|
|
161 |
// Check for onWarn callback function and if there is none, launch dialog
|
|
|
162 |
if (typeof opt.onWarn !== 'function') {
|
|
|
163 |
$('#session-timeout-dialog').modal('show');
|
|
|
164 |
} else {
|
|
|
165 |
opt.onWarn(opt);
|
|
|
166 |
}
|
|
|
167 |
// Start dialog timer
|
|
|
168 |
startDialogTimer();
|
|
|
169 |
}, opt.warnAfter);
|
|
|
170 |
}
|
|
|
171 |
|
|
|
172 |
function startDialogTimer() {
|
|
|
173 |
// Clear session timer
|
|
|
174 |
clearTimeout(timer);
|
|
|
175 |
if (!$('#session-timeout-dialog').hasClass('in') && (opt.countdownMessage || opt.countdownBar)) {
|
|
|
176 |
// If warning dialog is not already open and either opt.countdownMessage
|
|
|
177 |
// or opt.countdownBar are set start countdown
|
|
|
178 |
startCountdownTimer('dialog', true);
|
|
|
179 |
}
|
|
|
180 |
// Set dialog timer
|
|
|
181 |
timer = setTimeout(function() {
|
|
|
182 |
// Check for onRedir callback function and if there is none, launch redirect
|
|
|
183 |
if (typeof opt.onRedir !== 'function') {
|
|
|
184 |
window.location = opt.redirUrl;
|
|
|
185 |
} else {
|
|
|
186 |
opt.onRedir(opt);
|
|
|
187 |
}
|
|
|
188 |
}, (opt.redirAfter - opt.warnAfter));
|
|
|
189 |
}
|
|
|
190 |
|
|
|
191 |
function startCountdownTimer(type, reset) {
|
|
|
192 |
// Clear countdown timer
|
|
|
193 |
clearTimeout(countdown.timer);
|
|
|
194 |
|
|
|
195 |
if (type === 'dialog' && reset) {
|
|
|
196 |
// If triggered by startDialogTimer start warning countdown
|
|
|
197 |
countdown.timeLeft = Math.floor((opt.redirAfter - opt.warnAfter) / 1000);
|
|
|
198 |
} else if (type === 'session' && reset) {
|
|
|
199 |
// If triggered by startSessionTimer start full countdown
|
|
|
200 |
// (this is needed if user doesn't close the warning dialog)
|
|
|
201 |
countdown.timeLeft = Math.floor(opt.redirAfter / 1000);
|
|
|
202 |
}
|
|
|
203 |
// If opt.countdownBar is true, calculate remaining time percentage
|
|
|
204 |
if (opt.countdownBar && type === 'dialog') {
|
|
|
205 |
countdown.percentLeft = Math.floor(countdown.timeLeft / ((opt.redirAfter - opt.warnAfter) / 1000) * 100);
|
|
|
206 |
} else if (opt.countdownBar && type === 'session') {
|
|
|
207 |
countdown.percentLeft = Math.floor(countdown.timeLeft / (opt.redirAfter / 1000) * 100);
|
|
|
208 |
}
|
|
|
209 |
// Set countdown message time value
|
|
|
210 |
var countdownEl = $('.countdown-holder');
|
|
|
211 |
var secondsLeft = countdown.timeLeft >= 0 ? countdown.timeLeft : 0;
|
|
|
212 |
if (opt.countdownSmart) {
|
|
|
213 |
var minLeft = Math.floor(secondsLeft / 60);
|
|
|
214 |
var secRemain = secondsLeft % 60;
|
|
|
215 |
var countTxt = minLeft > 0 ? minLeft + 'm' : '';
|
|
|
216 |
if (countTxt.length > 0) {
|
|
|
217 |
countTxt += ' ';
|
|
|
218 |
}
|
|
|
219 |
countTxt += secRemain + 's';
|
|
|
220 |
countdownEl.text(countTxt);
|
|
|
221 |
} else {
|
|
|
222 |
countdownEl.text(secondsLeft + "s");
|
|
|
223 |
}
|
|
|
224 |
|
|
|
225 |
// Set countdown message time value
|
|
|
226 |
if (opt.countdownBar) {
|
|
|
227 |
$('.countdown-bar').css('width', countdown.percentLeft + '%');
|
|
|
228 |
}
|
|
|
229 |
|
|
|
230 |
// Countdown by one second
|
|
|
231 |
countdown.timeLeft = countdown.timeLeft - 1;
|
|
|
232 |
countdown.timer = setTimeout(function() {
|
|
|
233 |
// Call self after one second
|
|
|
234 |
startCountdownTimer(type);
|
|
|
235 |
}, 1000);
|
|
|
236 |
}
|
|
|
237 |
|
|
|
238 |
// Start session timer
|
|
|
239 |
startSessionTimer();
|
|
|
240 |
|
|
|
241 |
};
|
|
|
242 |
})(jQuery);
|