You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@atlas.apache.org by ni...@apache.org on 2021/08/31 15:25:46 UTC

[atlas] 02/02: ATLAS-4378: UI - Implement session timeout on Atlas UI.

This is an automated email from the ASF dual-hosted git repository.

nixon pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/atlas.git

commit 5deac6277cb5e0676e811f682da2a7692a9f628e
Author: prasad pawar <pr...@freestoneinfotech.com>
AuthorDate: Thu Aug 26 18:59:12 2021 +0530

    ATLAS-4378: UI - Implement session timeout on Atlas UI.
---
 dashboardv2/public/css/scss/override.scss          |   6 +
 .../js/external_lib/idealTimeout/store.min.js      |   2 +
 dashboardv2/public/js/main.js                      |  27 +-
 dashboardv2/public/js/utils/Globals.js             |   1 +
 dashboardv2/public/js/utils/Utils.js               | 271 ++++++++++++++++++++-
 dashboardv3/public/css/scss/override.scss          |   6 +
 .../js/external_lib/idealTimeout/store.min.js      |   2 +
 dashboardv3/public/js/main.js                      |  27 +-
 dashboardv3/public/js/utils/Globals.js             |   1 +
 dashboardv3/public/js/utils/Utils.js               | 267 +++++++++++++++++++-
 10 files changed, 604 insertions(+), 6 deletions(-)

diff --git a/dashboardv2/public/css/scss/override.scss b/dashboardv2/public/css/scss/override.scss
index c24b0c6..549ae3b 100644
--- a/dashboardv2/public/css/scss/override.scss
+++ b/dashboardv2/public/css/scss/override.scss
@@ -575,4 +575,10 @@ div.columnmanager-dropdown-container {
     ul {
         list-style: disc;
     }
+}
+
+.ideal-timeout {
+    .modal-content {
+        border-radius: 0px !important;
+    }
 }
\ No newline at end of file
diff --git a/dashboardv2/public/js/external_lib/idealTimeout/store.min.js b/dashboardv2/public/js/external_lib/idealTimeout/store.min.js
new file mode 100644
index 0000000..7334a7e
--- /dev/null
+++ b/dashboardv2/public/js/external_lib/idealTimeout/store.min.js
@@ -0,0 +1,2 @@
+/* Copyright (c) 2010-2013 Marcus Westin */
+"use strict";(function(e,t){typeof define=="function"&&define.amd?define([],t):typeof exports=="object"?module.exports=t():e.store=t()})(this,function(){function o(){try{return r in t&&t[r]}catch(e){return!1}}var e={},t=window,n=t.document,r="localStorage",i="script",s;e.disabled=!1,e.version="1.3.17",e.set=function(e,t){},e.get=function(e,t){},e.has=function(t){return e.get(t)!==undefined},e.remove=function(e){},e.clear=function(){},e.transact=function(t,n,r){r==null&&(r=n,n=null),n==nu [...]
\ No newline at end of file
diff --git a/dashboardv2/public/js/main.js b/dashboardv2/public/js/main.js
index 7c8bbb4..f2ea1d8 100644
--- a/dashboardv2/public/js/main.js
+++ b/dashboardv2/public/js/main.js
@@ -178,7 +178,8 @@ require.config({
         'jstree': 'libs/jstree/jstree.min',
         'jquery-steps': 'libs/jquery-steps/jquery.steps.min',
         'dropzone': 'libs/dropzone/js/dropzone-amd-module',
-        'lossless-json': 'libs/lossless-json/lossless-json'
+        'lossless-json': 'libs/lossless-json/lossless-json',
+        'store': 'external_lib/idealTimeout/store.min'
     },
 
     /**
@@ -199,11 +200,12 @@ require(['App',
     'collection/VEntityList',
     'collection/VTagList',
     'utils/Enums',
+    'utils/Utils',
     'utils/Overrides',
     'bootstrap',
     'd3',
     'select2'
-], function(App, Router, Helper, CommonViewFunction, Globals, UrlLinks, VEntityList, VTagList, Enums) {
+], function(App, Router, Helper, CommonViewFunction, Globals, UrlLinks, VEntityList, VTagList, Enums, Utils) {
     var that = this;
     this.asyncFetchCounter = 5 + (Enums.addOnEntities.length + 1);
     // entity
@@ -286,6 +288,27 @@ require(['App',
                 if (response['atlas.tasks.enabled'] !== undefined) {
                     Globals.isTasksEnabled = response['atlas.tasks.enabled'];
                 }
+                if (response['atlas.session.timeout.secs']) { Globals.idealTimeoutSeconds = response['atlas.session.timeout.secs']; }
+                /*  Atlas idealTimeout 
+       redirectUrl: url to redirect after timeout
+       idealTimeLimit: timeout in seconds
+       activityEvents: ideal keyboard mouse events
+       dialogDisplayLimit: show popup before timeout in seconds
+       */
+                $(document).ready(function() {
+                    $(document).idleTimeout({
+                        redirectUrl: Utils.getBaseUrl(window.location.pathname) + '/index.html?action=timeout', // redirect to this url
+                        idleTimeLimit: Globals.idealTimeoutSeconds, // 900 seconds
+                        activityEvents: 'click keypress scroll wheel mousemove', // separate each event with a space
+                        dialogDisplayLimit: 10, // Time to display the warning dialog before logout (and optional callback) in seconds
+                        sessionKeepAliveTimer: false, // Set to false to disable pings.
+                        onModalKeepAlive: function() {
+                            CommonViewFunction.userDataFetch({
+                                url: UrlLinks.sessionApiUrl()
+                            })
+                        }
+                    });
+                });
             }
             --that.asyncFetchCounter;
             startApp();
diff --git a/dashboardv2/public/js/utils/Globals.js b/dashboardv2/public/js/utils/Globals.js
index b0dc5cd..8fe8ebc 100644
--- a/dashboardv2/public/js/utils/Globals.js
+++ b/dashboardv2/public/js/utils/Globals.js
@@ -49,6 +49,7 @@ define(["require"], function(require) {
 
     Globals.isDebugMetricsEnabled = false;
     Globals.isTasksEnabled = false;
+    Globals.idealTimeoutSeconds = 900;
 
     return Globals;
 });
\ No newline at end of file
diff --git a/dashboardv2/public/js/utils/Utils.js b/dashboardv2/public/js/utils/Utils.js
index f426b6c..f2498d8 100644
--- a/dashboardv2/public/js/utils/Utils.js
+++ b/dashboardv2/public/js/utils/Utils.js
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums', 'moment', 'moment-timezone', 'pnotify.buttons', 'pnotify.confirm'], function(require, Globals, pnotify, Messages, Enums, moment) {
+define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums', 'moment', 'store', 'modules/Modal', 'moment-timezone', 'pnotify.buttons', 'pnotify.confirm'], function(require, Globals, pnotify, Messages, Enums, moment, store, Modal) {
     'use strict';
 
     var Utils = {};
@@ -952,5 +952,274 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums',
         }
         return dateValue;
     }
+    //------------------------------------------------idleTimeout-----------------------------
+    $.fn.idleTimeout = function(userRuntimeConfig) {
+
+        //##############################
+        //## Public Configuration Variables
+        //##############################
+        var defaultConfig = {
+                redirectUrl: Utils.getBaseUrl(window.location.pathname) + '/index.html?action=timeout', // redirect to this url on logout. Set to "redirectUrl: false" to disable redirect
+
+                // idle settings
+                idleTimeLimit: Globals.idealTimeoutSeconds, // 'No activity' time limit in seconds. 1200 = 20 Minutes
+                idleCheckHeartbeat: 2, // Frequency to check for idle timeouts in seconds
+
+                // optional custom callback to perform before logout
+                customCallback: false, // set to false for no customCallback
+                // customCallback:    function () {    // define optional custom js function
+                // perform custom action before logout
+                // },
+
+                // configure which activity events to detect
+                // http://www.quirksmode.org/dom/events/
+                // https://developer.mozilla.org/en-US/docs/Web/Reference/Events
+                activityEvents: 'click keypress scroll wheel mousewheel mousemove', // separate each event with a space
+
+                // warning dialog box configuration
+                enableDialog: true, // set to false for logout without warning dialog
+                dialogDisplayLimit: 10, // Time to display the warning dialog before logout (and optional callback) in seconds. 180 = 3 Minutes
+                dialogTitle: 'Your session is about to expire!', // also displays on browser title bar
+                dialogText: 'Your session is about to expire.',
+                dialogTimeRemaining: 'You will be logged out in ',
+                dialogStayLoggedInButton: 'Stay Logged In',
+                dialogLogOutNowButton: 'Logout',
+
+                // error message if https://github.com/marcuswestin/store.js not enabled
+                errorAlertMessage: 'Please disable "Private Mode", or upgrade to a modern browser. Or perhaps a dependent file missing. Please see: https://github.com/marcuswestin/store.js',
+
+                // server-side session keep-alive timer
+                sessionKeepAliveTimer: 600, // ping the server at this interval in seconds. 600 = 10 Minutes. Set to false to disable pings
+                sessionKeepAliveUrl: window.location.href // set URL to ping - does not apply if sessionKeepAliveTimer: false
+            },
+
+            //##############################
+            //## Private Variables
+            //##############################
+            currentConfig = $.extend(defaultConfig, userRuntimeConfig), // merge default and user runtime configuration
+            origTitle = document.title, // save original browser title
+            activityDetector,
+            startKeepSessionAlive, stopKeepSessionAlive, keepSession, keepAlivePing, // session keep alive
+            idleTimer, remainingTimer, checkIdleTimeout, checkIdleTimeoutLoop, startIdleTimer, stopIdleTimer, // idle timer
+            openWarningDialog, dialogTimer, checkDialogTimeout, startDialogTimer, stopDialogTimer, isDialogOpen, destroyWarningDialog, countdownDisplay, // warning dialog
+            logoutUser;
+
+        //##############################
+        //## Public Functions
+        //##############################
+        // trigger a manual user logout
+        // use this code snippet on your site's Logout button: $.fn.idleTimeout().logout();
+        this.logout = function() {
+            store.set('idleTimerLoggedOut', true);
+        };
+
+        //##############################
+        //## Private Functions
+        //##############################
+
+        //----------- KEEP SESSION ALIVE FUNCTIONS --------------//
+        startKeepSessionAlive = function() {
+
+            keepSession = function() {
+                $.get(currentConfig.sessionKeepAliveUrl);
+                startKeepSessionAlive();
+            };
+
+            keepAlivePing = setTimeout(keepSession, (currentConfig.sessionKeepAliveTimer * 1000));
+        };
+
+        stopKeepSessionAlive = function() {
+            clearTimeout(keepAlivePing);
+        };
+
+        //----------- ACTIVITY DETECTION FUNCTION --------------//
+        activityDetector = function() {
+
+            $('body').on(currentConfig.activityEvents, function() {
+
+                if (!currentConfig.enableDialog || (currentConfig.enableDialog && isDialogOpen() !== true)) {
+                    startIdleTimer();
+                    $('#activity').effect('shake'); // added for demonstration page
+                }
+            });
+        };
+
+        //----------- IDLE TIMER FUNCTIONS --------------//
+        checkIdleTimeout = function() {
+
+            var timeIdleTimeout = (store.get('idleTimerLastActivity') + (currentConfig.idleTimeLimit * 1000));
+
+            if ($.now() > timeIdleTimeout) {
+
+                if (!currentConfig.enableDialog) { // warning dialog is disabled
+                    logoutUser(); // immediately log out user when user is idle for idleTimeLimit
+                } else if (currentConfig.enableDialog && isDialogOpen() !== true) {
+                    openWarningDialog();
+                    startDialogTimer(); // start timing the warning dialog
+                }
+            } else if (store.get('idleTimerLoggedOut') === true) { //a 'manual' user logout?
+                logoutUser();
+            } else {
+
+                if (currentConfig.enableDialog && isDialogOpen() === true) {
+                    destroyWarningDialog();
+                    stopDialogTimer();
+                }
+            }
+        };
+
+        startIdleTimer = function() {
+            stopIdleTimer();
+            store.set('idleTimerLastActivity', $.now());
+            checkIdleTimeoutLoop();
+        };
+
+        checkIdleTimeoutLoop = function() {
+            checkIdleTimeout();
+            idleTimer = setTimeout(checkIdleTimeoutLoop, (currentConfig.idleCheckHeartbeat * 1000));
+        };
+
+        stopIdleTimer = function() {
+            clearTimeout(idleTimer);
+        };
+
+        //----------- WARNING DIALOG FUNCTIONS --------------//
+        openWarningDialog = function() {
+
+
+            var dialogContent = "<div id='idletimer_warning_dialog'><p>" + currentConfig.dialogText + "</p><p style='display:inline'>" + currentConfig.dialogTimeRemaining + ": <div style='display:inline' id='countdownDisplay'></div> secs.</p></div>";
+
+            var that = this,
+                modalObj = {
+                    title: currentConfig.dialogTitle,
+                    htmlContent: dialogContent,
+                    okText: "Stay Signed-in",
+                    cancelText: 'Logout',
+                    mainClass: 'modal-lg',
+                    allowCancel: true,
+                    okCloses: false,
+                    escape: false,
+                    cancellable: true,
+                    width: "500px",
+                    mainClass: "ideal-timeout"
+                };
+            var modal = new Modal(modalObj);
+            modal.open();
+            modal.on('ok', function() {
+                if (userRuntimeConfig && userRuntimeConfig.onModalKeepAlive) {
+                    userRuntimeConfig.onModalKeepAlive(); //hit session API
+                }
+                destroyWarningDialog();
+                modal.close();
+                stopDialogTimer();
+                startIdleTimer();
+                CommonViewFunction.userDataFetch({
+                    url: UrlLinks.sessionApiUrl()
+                })
+
+            });
+            modal.on('closeModal', function() {
+                logoutUser();
+            });
+
+            countdownDisplay();
+
+            // document.title = currentConfig.dialogTitle;
+
+            if (currentConfig.sessionKeepAliveTimer) {
+                stopKeepSessionAlive();
+            }
+        };
+
+        checkDialogTimeout = function() {
+            var timeDialogTimeout = (store.get('idleTimerLastActivity') + (currentConfig.idleTimeLimit * 1000) + (currentConfig.dialogDisplayLimit * 1000));
+
+            if (($.now() > timeDialogTimeout) || (store.get('idleTimerLoggedOut') === true)) {
+                logoutUser();
+            }
+        };
+
+        startDialogTimer = function() {
+            dialogTimer = setInterval(checkDialogTimeout, (currentConfig.idleCheckHeartbeat * 1000));
+        };
+
+        stopDialogTimer = function() {
+            clearInterval(dialogTimer);
+            clearInterval(remainingTimer);
+        };
+
+        isDialogOpen = function() {
+            var dialogOpen = $("#idletimer_warning_dialog").is(":visible");
+
+            if (dialogOpen === true) {
+                return true;
+            }
+            return false;
+        };
+
+        destroyWarningDialog = function() {
+            if (currentConfig.sessionKeepAliveTimer) {
+                startKeepSessionAlive();
+            }
+        };
+
+        countdownDisplay = function() {
+            var dialogDisplaySeconds = currentConfig.dialogDisplayLimit,
+                mins, secs;
+
+            remainingTimer = setInterval(function() {
+                mins = Math.floor(dialogDisplaySeconds / 60); // minutes
+                if (mins < 10) { mins = '0' + mins; }
+                secs = dialogDisplaySeconds - (mins * 60); // seconds
+                if (secs < 10) { secs = '0' + secs; }
+                $('#countdownDisplay').html(mins + ':' + secs);
+                dialogDisplaySeconds -= 1;
+            }, 1000);
+        };
+
+        //----------- LOGOUT USER FUNCTION --------------//
+        logoutUser = function() {
+            store.set('idleTimerLoggedOut', true);
+
+            if (currentConfig.sessionKeepAliveTimer) {
+                stopKeepSessionAlive();
+            }
+
+            if (currentConfig.customCallback) {
+                currentConfig.customCallback();
+            }
+
+            if (currentConfig.redirectUrl) {
+                window.location.href = currentConfig.redirectUrl;
+            }
+        };
+
+        //###############################
+        // Build & Return the instance of the item as a plugin
+        // This is your construct.
+        //###############################
+        return this.each(function() {
+
+            if (store.enabled) {
+
+                store.set('idleTimerLastActivity', $.now());
+                store.set('idleTimerLoggedOut', false);
+
+                activityDetector();
+
+                if (currentConfig.sessionKeepAliveTimer) {
+                    startKeepSessionAlive();
+                }
+
+                startIdleTimer();
+
+            } else {
+                alert(currentConfig.errorAlertMessage);
+            }
+
+        });
+    };
+
+    //------------------------------------------------
     return Utils;
 });
\ No newline at end of file
diff --git a/dashboardv3/public/css/scss/override.scss b/dashboardv3/public/css/scss/override.scss
index c1841eb..2c3ea78 100644
--- a/dashboardv3/public/css/scss/override.scss
+++ b/dashboardv3/public/css/scss/override.scss
@@ -577,4 +577,10 @@ div.columnmanager-dropdown-container {
     ul {
         list-style: disc;
     }
+}
+
+.ideal-timeout {
+    .modal-content {
+        border-radius: 0px !important;
+    }
 }
\ No newline at end of file
diff --git a/dashboardv3/public/js/external_lib/idealTimeout/store.min.js b/dashboardv3/public/js/external_lib/idealTimeout/store.min.js
new file mode 100644
index 0000000..7334a7e
--- /dev/null
+++ b/dashboardv3/public/js/external_lib/idealTimeout/store.min.js
@@ -0,0 +1,2 @@
+/* Copyright (c) 2010-2013 Marcus Westin */
+"use strict";(function(e,t){typeof define=="function"&&define.amd?define([],t):typeof exports=="object"?module.exports=t():e.store=t()})(this,function(){function o(){try{return r in t&&t[r]}catch(e){return!1}}var e={},t=window,n=t.document,r="localStorage",i="script",s;e.disabled=!1,e.version="1.3.17",e.set=function(e,t){},e.get=function(e,t){},e.has=function(t){return e.get(t)!==undefined},e.remove=function(e){},e.clear=function(){},e.transact=function(t,n,r){r==null&&(r=n,n=null),n==nu [...]
\ No newline at end of file
diff --git a/dashboardv3/public/js/main.js b/dashboardv3/public/js/main.js
index 374641e..97e177c 100644
--- a/dashboardv3/public/js/main.js
+++ b/dashboardv3/public/js/main.js
@@ -207,7 +207,8 @@ require.config({
         'jstree': 'libs/jstree/jstree.min',
         'jquery-steps': 'libs/jquery-steps/jquery.steps.min',
         'dropzone': 'libs/dropzone/js/dropzone-amd-module',
-        'lossless-json': 'libs/lossless-json/lossless-json'
+        'lossless-json': 'libs/lossless-json/lossless-json',
+        'store': 'external_lib/idealTimeout/store.min'
     },
 
     /**
@@ -228,11 +229,12 @@ require(['App',
     'collection/VEntityList',
     'collection/VTagList',
     'utils/Enums',
+    'utils/Utils',
     'utils/Overrides',
     'bootstrap',
     'd3',
     'select2'
-], function(App, Router, Helper, CommonViewFunction, Globals, UrlLinks, VEntityList, VTagList, Enums) {
+], function(App, Router, Helper, CommonViewFunction, Globals, UrlLinks, VEntityList, VTagList, Enums, Utils) {
     var that = this;
     this.asyncFetchCounter = 5 + (Enums.addOnEntities.length + 1);
     // entity
@@ -315,6 +317,27 @@ require(['App',
                 if (response['atlas.tasks.enabled'] !== undefined) {
                     Globals.isTasksEnabled = response['atlas.tasks.enabled'];
                 }
+                if (response['atlas.session.timeout.secs']) { Globals.idealTimeoutSeconds = response['atlas.session.timeout.secs']; }
+                /*  Atlas idealTimeout 
+       redirectUrl: url to redirect after timeout
+       idealTimeLimit: timeout in seconds
+       activityEvents: ideal keyboard mouse events
+       dialogDisplayLimit: show popup before timeout in seconds
+       */
+                $(document).ready(function() {
+                    $(document).idleTimeout({
+                        redirectUrl: Utils.getBaseUrl(window.location.pathname) + '/index.html?action=timeout', // redirect to this url
+                        idleTimeLimit: Globals.idealTimeoutSeconds, // 900 seconds
+                        activityEvents: 'click keypress scroll wheel mousemove', // separate each event with a space
+                        dialogDisplayLimit: 10, // Time to display the warning dialog before logout (and optional callback) in seconds
+                        sessionKeepAliveTimer: false, // Set to false to disable pings.
+                        onModalKeepAlive: function() {
+                            CommonViewFunction.userDataFetch({
+                                url: UrlLinks.sessionApiUrl()
+                            })
+                        }
+                    });
+                });
             }
             --that.asyncFetchCounter;
             startApp();
diff --git a/dashboardv3/public/js/utils/Globals.js b/dashboardv3/public/js/utils/Globals.js
index b0dc5cd..8fe8ebc 100644
--- a/dashboardv3/public/js/utils/Globals.js
+++ b/dashboardv3/public/js/utils/Globals.js
@@ -49,6 +49,7 @@ define(["require"], function(require) {
 
     Globals.isDebugMetricsEnabled = false;
     Globals.isTasksEnabled = false;
+    Globals.idealTimeoutSeconds = 900;
 
     return Globals;
 });
\ No newline at end of file
diff --git a/dashboardv3/public/js/utils/Utils.js b/dashboardv3/public/js/utils/Utils.js
index 1a73b7c..c3122fc 100644
--- a/dashboardv3/public/js/utils/Utils.js
+++ b/dashboardv3/public/js/utils/Utils.js
@@ -16,7 +16,7 @@
  * limitations under the License.
  */
 
-define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums', 'moment', 'moment-timezone', 'pnotify.buttons', 'pnotify.confirm'], function(require, Globals, pnotify, Messages, Enums, moment) {
+define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums', 'moment', 'store', 'modules/Modal', 'moment-timezone', 'pnotify.buttons', 'pnotify.confirm'], function(require, Globals, pnotify, Messages, Enums, moment, store, Modal) {
     'use strict';
 
     var Utils = {};
@@ -964,5 +964,270 @@ define(['require', 'utils/Globals', 'pnotify', 'utils/Messages', 'utils/Enums',
         }
         return dateValue;
     }
+    //------------------------------------------------idleTimeout-----------------------------
+    $.fn.idleTimeout = function(userRuntimeConfig) {
+
+        //##############################
+        //## Public Configuration Variables
+        //##############################
+        var defaultConfig = {
+                redirectUrl: Utils.getBaseUrl(window.location.pathname) + '/index.html?action=timeout', // redirect to this url on logout. Set to "redirectUrl: false" to disable redirect
+
+                // idle settings
+                idleTimeLimit: Globals.idealTimeoutSeconds, // 'No activity' time limit in seconds. 1200 = 20 Minutes
+                idleCheckHeartbeat: 2, // Frequency to check for idle timeouts in seconds
+
+                // optional custom callback to perform before logout
+                customCallback: false, // set to false for no customCallback
+                // customCallback:    function () {    // define optional custom js function
+                // perform custom action before logout
+                // },
+
+                // configure which activity events to detect
+                // http://www.quirksmode.org/dom/events/
+                // https://developer.mozilla.org/en-US/docs/Web/Reference/Events
+                activityEvents: 'click keypress scroll wheel mousewheel mousemove', // separate each event with a space
+
+                // warning dialog box configuration
+                enableDialog: true, // set to false for logout without warning dialog
+                dialogDisplayLimit: 10, // Time to display the warning dialog before logout (and optional callback) in seconds. 180 = 3 Minutes
+                dialogTitle: 'Your session is about to expire!', // also displays on browser title bar
+                dialogText: 'Your session is about to expire.',
+                dialogTimeRemaining: 'You will be logged out in ',
+                dialogStayLoggedInButton: 'Stay Logged In',
+                dialogLogOutNowButton: 'Logout',
+
+                // error message if https://github.com/marcuswestin/store.js not enabled
+                errorAlertMessage: 'Please disable "Private Mode", or upgrade to a modern browser. Or perhaps a dependent file missing. Please see: https://github.com/marcuswestin/store.js',
+
+                // server-side session keep-alive timer
+                sessionKeepAliveTimer: 600, // ping the server at this interval in seconds. 600 = 10 Minutes. Set to false to disable pings
+                sessionKeepAliveUrl: window.location.href // set URL to ping - does not apply if sessionKeepAliveTimer: false
+            },
+
+            //##############################
+            //## Private Variables
+            //##############################
+            currentConfig = $.extend(defaultConfig, userRuntimeConfig), // merge default and user runtime configuration
+            origTitle = document.title, // save original browser title
+            activityDetector,
+            startKeepSessionAlive, stopKeepSessionAlive, keepSession, keepAlivePing, // session keep alive
+            idleTimer, remainingTimer, checkIdleTimeout, checkIdleTimeoutLoop, startIdleTimer, stopIdleTimer, // idle timer
+            openWarningDialog, dialogTimer, checkDialogTimeout, startDialogTimer, stopDialogTimer, isDialogOpen, destroyWarningDialog, countdownDisplay, // warning dialog
+            logoutUser;
+
+        //##############################
+        //## Public Functions
+        //##############################
+        // trigger a manual user logout
+        // use this code snippet on your site's Logout button: $.fn.idleTimeout().logout();
+        this.logout = function() {
+            store.set('idleTimerLoggedOut', true);
+        };
+
+        //##############################
+        //## Private Functions
+        //##############################
+
+        //----------- KEEP SESSION ALIVE FUNCTIONS --------------//
+        startKeepSessionAlive = function() {
+
+            keepSession = function() {
+                $.get(currentConfig.sessionKeepAliveUrl);
+                startKeepSessionAlive();
+            };
+
+            keepAlivePing = setTimeout(keepSession, (currentConfig.sessionKeepAliveTimer * 1000));
+        };
+
+        stopKeepSessionAlive = function() {
+            clearTimeout(keepAlivePing);
+        };
+
+        //----------- ACTIVITY DETECTION FUNCTION --------------//
+        activityDetector = function() {
+
+            $('body').on(currentConfig.activityEvents, function() {
+
+                if (!currentConfig.enableDialog || (currentConfig.enableDialog && isDialogOpen() !== true)) {
+                    startIdleTimer();
+                    $('#activity').effect('shake'); // added for demonstration page
+                }
+            });
+        };
+
+        //----------- IDLE TIMER FUNCTIONS --------------//
+        checkIdleTimeout = function() {
+
+            var timeIdleTimeout = (store.get('idleTimerLastActivity') + (currentConfig.idleTimeLimit * 1000));
+
+            if ($.now() > timeIdleTimeout) {
+
+                if (!currentConfig.enableDialog) { // warning dialog is disabled
+                    logoutUser(); // immediately log out user when user is idle for idleTimeLimit
+                } else if (currentConfig.enableDialog && isDialogOpen() !== true) {
+                    openWarningDialog();
+                    startDialogTimer(); // start timing the warning dialog
+                }
+            } else if (store.get('idleTimerLoggedOut') === true) { //a 'manual' user logout?
+                logoutUser();
+            } else {
+
+                if (currentConfig.enableDialog && isDialogOpen() === true) {
+                    destroyWarningDialog();
+                    stopDialogTimer();
+                }
+            }
+        };
+
+        startIdleTimer = function() {
+            stopIdleTimer();
+            store.set('idleTimerLastActivity', $.now());
+            checkIdleTimeoutLoop();
+        };
+
+        checkIdleTimeoutLoop = function() {
+            checkIdleTimeout();
+            idleTimer = setTimeout(checkIdleTimeoutLoop, (currentConfig.idleCheckHeartbeat * 1000));
+        };
+
+        stopIdleTimer = function() {
+            clearTimeout(idleTimer);
+        };
+
+        //----------- WARNING DIALOG FUNCTIONS --------------//
+        openWarningDialog = function() {
+
+
+            var dialogContent = "<div id='idletimer_warning_dialog'><p>" + currentConfig.dialogText + "</p><p style='display:inline'>" + currentConfig.dialogTimeRemaining + ": <div style='display:inline' id='countdownDisplay'></div> secs.</p></div>";
+
+            var that = this,
+                modalObj = {
+                    title: currentConfig.dialogTitle,
+                    htmlContent: dialogContent,
+                    okText: "Stay Signed-in",
+                    cancelText: 'Logout',
+                    mainClass: 'modal-lg',
+                    allowCancel: true,
+                    okCloses: false,
+                    escape: false,
+                    cancellable: true,
+                    width: "500px",
+                    mainClass: "ideal-timeout"
+                };
+            var modal = new Modal(modalObj);
+            modal.open();
+            modal.on('ok', function() {
+                if (userRuntimeConfig && userRuntimeConfig.onModalKeepAlive) {
+                    userRuntimeConfig.onModalKeepAlive(); //hit session API
+                }
+                destroyWarningDialog();
+                modal.close();
+                stopDialogTimer();
+                startIdleTimer();
+            });
+            modal.on('closeModal', function() {
+                logoutUser();
+            });
+
+            countdownDisplay();
+
+            // document.title = currentConfig.dialogTitle;
+
+            if (currentConfig.sessionKeepAliveTimer) {
+                stopKeepSessionAlive();
+            }
+        };
+
+        checkDialogTimeout = function() {
+            var timeDialogTimeout = (store.get('idleTimerLastActivity') + (currentConfig.idleTimeLimit * 1000) + (currentConfig.dialogDisplayLimit * 1000));
+
+            if (($.now() > timeDialogTimeout) || (store.get('idleTimerLoggedOut') === true)) {
+                logoutUser();
+            }
+        };
+
+        startDialogTimer = function() {
+            dialogTimer = setInterval(checkDialogTimeout, (currentConfig.idleCheckHeartbeat * 1000));
+        };
+
+        stopDialogTimer = function() {
+            clearInterval(dialogTimer);
+            clearInterval(remainingTimer);
+        };
+
+        isDialogOpen = function() {
+            var dialogOpen = $("#idletimer_warning_dialog").is(":visible");
+
+            if (dialogOpen === true) {
+                return true;
+            }
+            return false;
+        };
+
+        destroyWarningDialog = function() {
+            if (currentConfig.sessionKeepAliveTimer) {
+                startKeepSessionAlive();
+            }
+        };
+
+        countdownDisplay = function() {
+            var dialogDisplaySeconds = currentConfig.dialogDisplayLimit,
+                mins, secs;
+
+            remainingTimer = setInterval(function() {
+                mins = Math.floor(dialogDisplaySeconds / 60); // minutes
+                if (mins < 10) { mins = '0' + mins; }
+                secs = dialogDisplaySeconds - (mins * 60); // seconds
+                if (secs < 10) { secs = '0' + secs; }
+                $('#countdownDisplay').html(mins + ':' + secs);
+                dialogDisplaySeconds -= 1;
+            }, 1000);
+        };
+
+        //----------- LOGOUT USER FUNCTION --------------//
+        logoutUser = function() {
+            store.set('idleTimerLoggedOut', true);
+
+            if (currentConfig.sessionKeepAliveTimer) {
+                stopKeepSessionAlive();
+            }
+
+            if (currentConfig.customCallback) {
+                currentConfig.customCallback();
+            }
+
+            if (currentConfig.redirectUrl) {
+                window.location.href = currentConfig.redirectUrl;
+            }
+        };
+
+        //###############################
+        // Build & Return the instance of the item as a plugin
+        // This is your construct.
+        //###############################
+        return this.each(function() {
+
+            if (store.enabled) {
+
+                store.set('idleTimerLastActivity', $.now());
+                store.set('idleTimerLoggedOut', false);
+
+                activityDetector();
+
+                if (currentConfig.sessionKeepAliveTimer) {
+                    startKeepSessionAlive();
+                }
+
+                startIdleTimer();
+
+            } else {
+                alert(currentConfig.errorAlertMessage);
+            }
+
+        });
+    };
+
+    //------------------------------------------------
     return Utils;
 });
\ No newline at end of file