How To: Timeout (Client Side) using postMessage (HTML5) with Countdown alert

Page 1 of 2 12 LastLast
  1. #1

    How To: Timeout (Client Side) using postMessage (HTML5) with Countdown alert

    It's my first Englis How To... I hope can help others. Sorry my English isn't very good. Corrections are welcome!

    Finally I can do TimeOut at client side... you need a browser with postMessage (HTML5) compatibility.

    My first implementation was create an mouse and keyboard detection. But I'm using tab panel, and any tab panel is an iFrame then my script only works in the master page, but not in any tab. Then I'm trying to catch mouse and keyboard events in any iFrame and send this to master and run the script in the master page, but this actions was blocked by browsers to prevent hacking actions.

    Surfing into the web I found in HTML the postMessage that is used to send message between windows and iFrames.
    Here an example how it works: http://robertnyman.com/2010/03/18/po...s-and-iframes/

    Then now I can catch the events in any iFrame but the action is run directly in the master page.

    NOTE: I'm working with MVC

    1: Create a TimeOut.js into Scripts folder file with this content:

    // Session Timeout client side. Master and Detail detection.
    /**
    * Detecting in Master and Detail, Mouse and Keyboard events.
    * Using postMessage (HTML5)
    * @autor   Camilo Martinez [Equiman], http://gplus.to/equiman
    * @created 2012-04-11
    * @updated 2012-04-22
    * @licence CC BY-SA http://creativecommons.org/licenses/by-sa/3.0/
    */
    
    // Add this into Master Page (TabPanelContain):
    // <DocumentReady Handler="masterTimeOut();" />
    var masterTimeOut = function () {
    
        //Convert minutes value indicated in TimeOut del Web.Config to seconds
        var min = 0;
        var sec = 0;
        var timer = null;
        var vis = false;
    
        doTimer = function () {
            // At least 1 min, show the countdown window
            if ((min - sec) < 60) {
    
                updateTime();
    
                if (frmTimeOut.hidden) {
                    vis = true;
                    frmTimeOut.setVisible(true);
                }
            }
            else {
                if (!frmTimeOut.hidden) {
                    vis = false;
                    frmTimeOut.setVisible(false);
                }
            }
    
            // Check the countdown counter
            if ((min - sec) > 0) {
                sec++;
                // Repeat the process each minute
                timer = setTimeout('doTimer()', 1000);
            }
            else {
                // When countdown is finish, redirect to Login page
                top.location.replace(GetNewPath('/Home/Login/'));
            }
        };
    
        stopCount = function () {
            if (vis === true) {
                //2 seconds Idle (no detect events, because showing alert window is detected as mousemove)
                if ((min - sec) < 58) {
                    vis = false;
                }
            }
            else {
                // Convert minutes to second, minus 10 to be sure Client TimeOut occurs first than Server Timeout
                min = (document.getElementById('txtMinTimeOut').value * 60) - 10;
                sec = 0;
                clearTimeout(timer);
                doTimer();
            }
        };
    
        updateTime = function () {
            var message = 'Inactivity was detected. Your session will expire in';
            var time = (min - sec);
            var unity = 'seconds.';
    
            Ext.getCmp('lblText').setText(message + ' ' + time + ' ' + unity);
        };
    
        // Start counter on Load
        document.onload = function () {
            stopCount();
            return false;
        };
        // Star counter when mouse move
        document.onmousemove = function () {
            stopCount();
            return false;
        };
        // Star counter when key is pressed
        document.onkeypress = function () {
            stopCount();
            return false;
        };
    
        // Read and event when is send from an iFrame
        function displayMessage(e) {
            if ((e.origin.split(":", 2)[0] + ":" + e.origin.split(":", 2)[1]) === (GetNewPath("/").split(":", 2)[0] + ":" + GetNewPath("/").split(":", 2)[1])) {
                // If the iFrame send any of this events start counter
                switch (e.data) {
                    case "onload":
                    case "onmousemove":
                    case "onkeypress":
                        stopCount();
                }
            }
        };
    
        if (window.addEventListener) {
            // For standards-compliant web browsers
            window.addEventListener("message", displayMessage, false);
        }
        else {
            window.attachEvent("onmessage", displayMessage);
        };
    };
    
    
    // Add this into any iFrame Page:
    // <DocumentReady Handler="detailTimeOut();" />
    var detailTimeOut = function () {
        // Start counter on Load
        document.onload = function () {
            top.postMessage("onload", GetNewPath("/"));
            return false;
        };
        // Star counter when mouse move
        document.onmousemove = function () {
            top.postMessage("onmousemove", GetNewPath("/"));
            return false;
        };
        // Star counter when key is pressed
        document.onkeypress = function () {
            top.postMessage("onkeypress", GetNewPath("/"));
            return false;
        };
    };
    
    
    // Get the Path when use VirtualPath in .Net or IIS
    var GetNewPath = function (relative_path) {
        var url = window.location.href;
    
        if (url.substring(url.length - 1, url.length) == '/') {
            url = url.substring(0, url.length - 1);
        }
    
        var url_parts = url.split('/');
    
        if (relative_path.substring(0, 1) != '/') {
            url_parts[url_parts.length - 2] = relative_path;
            url_parts[url_parts.length - 1] = '';
        }
        else {
            url_parts[url_parts.length - 2] = relative_path.substring(1);
            url_parts[url_parts.length - 1] = '';
        }
    
        var new_page_absolute_path = url_parts.join('/');
    
        if (new_page_absolute_path.substring(new_page_absolute_path.length - 1, new_page_absolute_path.length) == '/') {
            new_page_absolute_path = new_page_absolute_path.substring(0, new_page_absolute_path.length - 1);
        }
    
        return new_page_absolute_path;
    };
    2: Add the timeout to Web.Config (in minutes). Into <system.web> add:
    <sessionState timeout="5"></sessionState>
    3: Read into master page the TimeOutValue.

    Add Namespace System.Web
    <%@ Page Language="C#" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>
    <%@ Register Assembly="Ext.Net" Namespace="Ext.Net" TagPrefix="ext" %>
    <%@ Import Namespace="System.Web" %>
    Into <head> tag read the TimeOut time and send to Hidden TextField:
    <script runat="server">
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack && !X.IsAjaxRequest)
            {
                txtMinTimeOut.Value = Session.Timeout;
            }
        }
    </script>
    Into <body><form> tags tead the TimeOut.js, when Document Ready load the masterTimeout() and create the Hidden Text Field:
    <asp:ScriptManager ID="smmaster" EnableScriptLocalization="true" runat="server">
        <Scripts>
             <asp:ScriptReference Path="~/Scripts/TimeOut.js" />
        </Scripts>
    </asp:ScriptManager>
    <ext:ResourceManager ID="ResourceManagerMaster2" runat="server">
        <Listeners>
            <DocumentReady Handler="masterTimeOut();" />
        </Listeners>
    </ext:ResourceManager>
    
    <ext:Hidden ID="txtMinTimeOut" runat="server" />
    Create a Window to show the countdown alert, before </form> tag:
    <ext:Window 
        ID="frmTimeOut" 
        runat="server" 
        Width="235" 
        Height="50" 
        Modal="false"
        Resizable="False"
        Hidden="true"
        Closable="false"
        Layout="FormLayout"
        Draggable="false"
        Padding="12">
        <Items>
            <ext:Label ID="lblText" runat="server" HideLabel="true" Text="" AutoWidth="true" />
        </Items>
    </ext:Window>
    4: In any iFrame (page loaded into Iframe or Tab Panel) add Into <body><form> tags tead the TimeOut.js, when Document Ready load the detailTimeout():

    <asp:ScriptManager ID="smDetail" EnableScriptLocalization="true" runat="server">
    <Scripts>
        <asp:ScriptReference Path="~/Scripts/TimeOut.js" />
    </Scripts>
    </asp:ScriptManager>
    <ext:ResourceManager ID="ResourceManager1" runat="server">
        <Listeners>
            <DocumentReady Handler="detailTimeOut();" />
        </Listeners>
    </ext:ResourceManager>
    And it's Done!!!
    Last edited by equiman; Nov 13, 2014 at 1:29 PM.
  2. #2

    problem...

    Hi... my english is not very good.... but i working in aspx and windows forms and when the redirect page for inactivity the program redirect to /Home/Login and i need to redirect to /Login.aspx

    thanks for your help about this redirection...
  3. #3
    OK... I think the difference is that you are working in a project without MVC.

    If you use virtual path, only need to chage:

    top.location.replace(GetNewPath('/Home/Login/'));
    To:

    top.location.replace(GetNewPath('/Login.aspx'));
    If you aren't use virtual path:

    use this:

    top.location.replace(Login.aspx');
    DP: I speak spanish too.
  4. #4
    Here is a new version of TimeOt.js file:

    // Session Timeout client side. Master and Detail detection.
    /**
    * Detecting in Master and Detail, Mouse and Keyboard events.
    * Using postMessage (HTML5)
    * @autor   Camilo Martinez [Equiman], http://gplus.to/equiman
    * @created 2012-04-11
    * @updated 2012-04-26
    * @licence CC BY-SA http://creativecommons.org/licenses/by-sa/3.0/
    */
    
    // URL Redirection
    var timeOutSession = function () {
        top.location.replace(GetNewPath('/Home/Timeout/'));
    };
     
    // Add this into Master Page (TabPanelContain):
    // <DocumentReady Handler="masterTimeOut();" />
    var masterTimeOut = function () {
     
        //Convert minutes value indicated in TimeOut del Web.Config to seconds
        var min = 0;
        var sec = 0;
        var timer = null;
        var vis = false;
     
        doTimer = function () {
            // At least 1 min, show the countdown window
            if ((min - sec) < 60) {
     
                updateTime();
     
                if (frmTimeOut.hidden) {
                    vis = true;
                    frmTimeOut.setVisible(true);
                }
            }
            else {
                if (!frmTimeOut.hidden) {
                    vis = false;
                    frmTimeOut.setVisible(false);
                }
            }
     
            // Check the countdown counter
            if ((min - sec) > 0) {
                sec++;
                // Repeat the process each minute
                timer = setTimeout('doTimer()', 1000);
            }
            else {
                // When countdown is finish, redirect to Login page
                timeOutSession();
            }
        };
     
        stopCount = function () {
            if (vis === true) {
                //2 seconds Idle (no detect events, because showing alert window is detected as mousemove)
                if ((min - sec) < 58) {
                    vis = false;
                }
            }
            else {
                // Convert minutes to second, minus 10 to be sure Client TimeOut occurs first than Server Timeout
                min = (document.getElementById('txtMinTimeOut').value * 60) - 10;
                sec = 0;
                clearTimeout(timer);
                doTimer();
            }
        };
     
        updateTime = function () {
            var message = 'Inactivity was detected. Your session will expire in';
            var time = (min - sec);
            var unity = 'seconds.';
     
            Ext.getCmp('lblText').setText(message + ' ' + time + ' ' + unity);
        };
     
        // Start counter on Load
        document.onload = function () {
            stopCount();
            return false;
        };
        // Star counter when mouse move
        document.onmousemove = function () {
            stopCount();
            return false;
        };
        // Star counter when key is pressed
        document.onkeypress = function () {
            stopCount();
            return false;
        };
     
        // Read and event when is send from an iFrame
        function displayMessage(e) {
            if ((e.origin.split(":", 2)[0] + ":" + e.origin.split(":", 2)[1]) === (GetNewPath("/").split(":", 2)[0] + ":" + GetNewPath("/").split(":", 2)[1])) {
                // If the iFrame send any of this events start counter
                switch (e.data) {
                    case "onload":
                    case "onmousemove":
                    case "onkeypress":
                     case "simulated":
                        stopCount();
                }
            }
        };
     
        if (window.addEventListener) {
            // For standards-compliant web browsers
            window.addEventListener("message", displayMessage, false);
        }
        else {
            window.attachEvent("onmessage", displayMessage);
        };
    };
     
     
    // Add this into any iFrame Page:
    // <DocumentReady Handler="detailTimeOut();" />
    var detailTimeOut = function () {
        // Start counter on Load
        document.onload = function () {
            top.postMessage("onload", GetNewPath("/"));
            return false;
        };
        // Star counter when mouse move
        document.onmousemove = function () {
            top.postMessage("onmousemove", GetNewPath("/"));
            return false;
        };
        // Star counter when key is pressed
        document.onkeypress = function () {
            top.postMessage("onkeypress", GetNewPath("/"));
            return false;
        };
        // Simulated Event
        simulatedEvent = function () {
            top.postMessage("simulated", GetNewPath("/"));
            return false;
        };
    };
     
    // Get the Path when use VirtualPath in .Net or IIS
    var GetNewPath = function (relative_path) {
        var url = window.location.href;
     
        if (url.substring(url.length - 1, url.length) == '/') {
            url = url.substring(0, url.length - 1);
        }
     
        var url_parts = url.split('/');
     
        if (relative_path.substring(0, 1) != '/') {
            url_parts[url_parts.length - 2] = relative_path;
            url_parts[url_parts.length - 1] = '';
        }
        else {
            url_parts[url_parts.length - 2] = relative_path.substring(1);
            url_parts[url_parts.length - 1] = '';
        }
     
        var new_page_absolute_path = url_parts.join('/');
     
        if (new_page_absolute_path.substring(new_page_absolute_path.length - 1, new_page_absolute_path.length) == '/') {
            new_page_absolute_path = new_page_absolute_path.substring(0, new_page_absolute_path.length - 1);
        }
     
        return new_page_absolute_path;
    };
    I create a function separated with the redirect location (now you can use it with other events), and add a simulatedEvent to reset the timer with other event, like an automatic reload or whatever you need.
    Last edited by equiman; Nov 13, 2014 at 1:30 PM.
  5. #5
    I found a bug with document.onkeypress. Here is a new version of TimeOut.js

    // Session Timeout client side. Master and Detail detection.
    /**
    * Detecting in Master and Detail, Mouse and Keyboard events.
    * Using postMessage (HTML5)
    * @autor   Camilo Martinez [Equiman], http://gplus.to/equiman
    * @created 2012-04-11
    * @updated 2012-05-02
    * @licence CC BY-SA http://creativecommons.org/licenses/by-sa/3.0/
    */
     
    // URL Redirection
    var timeOutSession = function () {
        top.location.replace(GetNewPath('/Home/Timeout/'));
    };
      
    // Add this into Master Page (TabPanelContain):
    // <DocumentReady Handler="masterTimeOut();" />
    var masterTimeOut = function () {
      
        //Convert minutes value indicated in TimeOut del Web.Config to seconds
        var min = 0;
        var sec = 0;
        var timer = null;
        var vis = false;
      
        doTimer = function () {
            // At least 1 min, show the countdown window
            if ((min - sec) < 60) {
      
                updateTime();
      
                if (frmTimeOut.hidden) {
                    vis = true;
                    frmTimeOut.setVisible(true);
                }
            }
            else {
                if (!frmTimeOut.hidden) {
                    vis = false;
                    frmTimeOut.setVisible(false);
                }
            }
      
            // Check the countdown counter
            if ((min - sec) > 0) {
                sec++;
                // Repeat the process each minute
                timer = setTimeout('doTimer()', 1000);
            }
            else {
                // When countdown is finish, redirect to Login page
                timeOutSession();
            }
        };
      
        stopCount = function () {
            if (vis === true) {
                //2 seconds Idle (no detect events, because showing alert window is detected as mousemove)
                if ((min - sec) < 58) {
                    vis = false;
                }
            }
            else {
                // Convert minutes to second, minus 10 to be sure Client TimeOut occurs first than Server Timeout
                min = (document.getElementById('txtMinTimeOut').value * 60) - 10;
                sec = 0;
                clearTimeout(timer);
                doTimer();
            }
        };
      
        updateTime = function () {
            var message = 'Inactivity was detected. Your session will expire in';
            var time = (min - sec);
            var unity = 'seconds.';
      
            Ext.getCmp('lblText').setText(message + ' ' + time + ' ' + unity);
        };
      
        // Start counter on Load
        document.onload = function () {
            stopCount();
            //return false;
        };
        // Star counter when mouse move
        document.onmousemove = function () {
            stopCount();
            //return false;
        };
        // Star counter when key is pressed
        document.onkeypress = function () {
            stopCount();
            //return false;
        };
      
        // Read and event when is send from an iFrame
        function displayMessage(e) {
            if ((e.origin.split(":", 2)[0] + ":" + e.origin.split(":", 2)[1]) === (GetNewPath("/").split(":", 2)[0] + ":" + GetNewPath("/").split(":", 2)[1])) {
                // If the iFrame send any of this events start counter
                switch (e.data) {
                    case "onload":
                    case "onmousemove":
                    case "onkeypress":
                     case "simulated":
                        stopCount();
                }
            }
        };
      
        if (window.addEventListener) {
            // For standards-compliant web browsers
            window.addEventListener("message", displayMessage, false);
        }
        else {
            window.attachEvent("onmessage", displayMessage);
        };
    };
      
      
    // Add this into any iFrame Page:
    // <DocumentReady Handler="detailTimeOut();" />
    var detailTimeOut = function () {
        // Start counter on Load
        document.onload = function () {
            top.postMessage("onload", GetNewPath("/"));
            return false;
        };
        // Star counter when mouse move
        document.onmousemove = function () {
            top.postMessage("onmousemove", GetNewPath("/"));
            //return false;
        };
        // Star counter when key is pressed
        document.onkeypress = function () {
            top.postMessage("onkeypress", GetNewPath("/"));
            //return false;
        };
        // Simulated Event
        simulatedEvent = function () {
            top.postMessage("simulated", GetNewPath("/"));
            //return false;
        };
    };
      
    // Get the Path when use VirtualPath in .Net or IIS
    var GetNewPath = function (relative_path) {
        var url = window.location.href;
      
        if (url.substring(url.length - 1, url.length) == '/') {
            url = url.substring(0, url.length - 1);
        }
      
        var url_parts = url.split('/');
      
        if (relative_path.substring(0, 1) != '/') {
            url_parts[url_parts.length - 2] = relative_path;
            url_parts[url_parts.length - 1] = '';
        }
        else {
            url_parts[url_parts.length - 2] = relative_path.substring(1);
            url_parts[url_parts.length - 1] = '';
        }
      
        var new_page_absolute_path = url_parts.join('/');
      
        if (new_page_absolute_path.substring(new_page_absolute_path.length - 1, new_page_absolute_path.length) == '/') {
            new_page_absolute_path = new_page_absolute_path.substring(0, new_page_absolute_path.length - 1);
        }
      
        return new_page_absolute_path;
    };
    Last edited by equiman; Nov 13, 2014 at 1:27 PM.
  6. #6
    Hi @equiman,

    Thanks for sharing! Looks very good!

    By the way, you might be interested to look at the MessageBus feature which is presented in Ext.NET v2.
    https://examples2.ext.net/#/MessageBus/Basic/Simple/
    https://examples2.ext.net/#/MessageBus/Basic/Complex/
  7. #7
    Thanks @Daniil I see MessageBus after I finish this code. basically is the same that postMessage but without exit from Ext.

    By the moment I use postMessage because I cant use Ext2 (beta) in my current projects.
    I'm waiting for the release version to update this code... and use all the new Graphs.


    By the way... did you know how can I reset the C# timer in server side defined in webconfig?
    I likely to send a reset to this timer too.
    Last edited by equiman; Jun 13, 2012 at 3:51 PM.
  8. #8
    Quote Originally Posted by equiman View Post
    By the way... did you know how can I reset the C# timer in server side defined in webconfig?
    I likely to send a reset to this timer too.
    Do you mean this timeout timer?
    <sessionState timeout="5"></sessionState>
  9. #9
    Yes, exactly this timer.
  10. #10
    Well, I think any request to a page resets this timeout.

    It is a bit unclear for me why do you need to reset it on server (I doubt it is possible). Please clarify.
Page 1 of 2 12 LastLast

Similar Threads

  1. How to add HTML5 video tag in side form panel
    By arunsathyan in forum 1.x Help
    Replies: 2
    Last Post: Feb 15, 2022, 8:12 PM
  2. Replies: 4
    Last Post: Mar 19, 2010, 11:35 AM
  3. Replies: 0
    Last Post: Sep 17, 2009, 8:04 AM
  4. Ext.Msg.Alert from client script error
    By rthiney in forum 1.x Help
    Replies: 3
    Last Post: Jul 09, 2009, 3:51 PM
  5. Replies: 2
    Last Post: Jul 09, 2009, 2:35 PM

Tags for this Thread

Posting Permissions