Apr 22, 2010

Are you still there?

Script

(function($) {

    var options = {        
            // default options for this plugin
            idle: function() { },             // called when the user fails to respond to prompt
            idleLimit: 15 * 60 * 1000,        // 15 minutes
            message: 'Are you still there?',  // can be a jQuery object (e.g. $('#my-element-id'))
            promptDuration: 60 * 1000,        // 60 seconds
            requireButtonClick: false,        // when false, mouse/key messages cancel idle prompt

            // default options for dialog widget
            autoOpen: false,
            buttons: {
                'Yes I\'m Still Here': function() {
                    $(this).dialog('close');
                }
            },
            draggable: false,
            modal: true,
            resizable: false,
            title: 'Are You Still There?'            
        },

        // constants
        PLUGIN_NAME = 'areyoustillthere',
        IDLE_CHECK_POLLING_INTERVAL = 1000,
        DIALOG_ID = PLUGIN_NAME + '-dialog',
        PROGRESSBAR_CLASS = PLUGIN_NAME + '-progressbar',

        // private variables
        isStarted = false,
        dialogElement,
        progressBarElement,
        progressBarTimerId,
        idlePollTimerId,
        timeOfLastInteraction = new Date();

    // private functions
    function touchTimeOfLastInteraction() {

        timeOfLastInteraction = new Date();
        if (!options.requireButtonClick && dialogElement.dialog('isOpen')) {
            dialogElement.dialog('close');
        }

    }
    function showDialogIfRequired() {

        if (dialogElement.dialog('isOpen')) {
            return;
        }
        if (timeOfLastInteraction.getTime() + options.idleLimit > (new Date()).getTime()) {
            return;
        }
        dialogElement.dialog('open');

    }
    function decrementProgressBarValue() {

        var currentValue = progressBarElement.progressbar('value');
        if (currentValue === 0) {
            dialogElement.dialog('close');
            if (typeof(options.idle) === 'function') {
                options.idle();
            }
        } else {
            progressBarElement.progressbar('value', (currentValue - 1));
        }

    }
    function startProgressBarTimer() {

        if (progressBarTimerId) {
            return;
        }
        progressBarElement.progressbar('value', 100);
        var tickInterval = options.promptDuration / 100;
        progressBarTimerId = window.setInterval(decrementProgressBarValue, tickInterval);

    }
    function stopProgressBarTimer() {

        if (!progressBarTimerId) {
            return;
        }
        window.clearInterval(progressBarTimerId);
        progressBarTimerId = null;

    }
    function repositionDialog() {

        if (!dialogElement.dialog('isOpen')) {
            return;
        }
        // HACK: use setTimeout to queue a re-positioning for the next available
        // clock cycle.  without this hack, the positioning calculations appear
        // to use the pre-resized/scrolled dimensions/layout coordinates.
        window.setTimeout(function() {
            var position = dialogElement.dialog('option', 'position');
            dialogElement.dialog('option', 'position', position);
        }, 0);

    }
    function start(o) {

        // sanity checks
        if (isStarted) {
            throw 'Already started';
        }
        if (options.message instanceof jQuery && options.message.length > 1) {
            throw 'The "message" option can not contain more than 1 element';
        }

        $.extend(options, o);

        if (options.message instanceof jQuery) {
            dialogElement = options.message;
        } else {
            dialogElement = $('<div id="' + DIALOG_ID + '">' + options.message + '</div>');
        }

        dialogElement
            .dialog(options)
            .bind('dialogopen', startProgressBarTimer)
            .bind('dialogclose', stopProgressBarTimer);

        progressBarElement = $('<div class="' + PROGRESSBAR_CLASS + '"></div>')
            .appendTo(dialogElement)
            .progressbar();

        $(window.document)
            .bind('mousemove', touchTimeOfLastInteraction)
            .bind('mousedown', touchTimeOfLastInteraction)
            .bind('keydown', touchTimeOfLastInteraction);

        $(window)
            .bind('scroll', repositionDialog)
            .bind('resize', repositionDialog);

        idlePollTimerId = window.setInterval(showDialogIfRequired, IDLE_CHECK_POLLING_INTERVAL);

        isStarted = true;

    }
    function stop() {

        if (!isStarted) {
            throw 'Not started yet';
        }

        if (dialogElement.dialog('isOpen')) {
            dialogElement.dialog('close');
        }

        window.clearInterval(idlePollTimerId);
        idlePollTimerId = null;

        $(window)
            .unbind('scroll', repositionDialog)
            .unbind('resize', repositionDialog);

        $(window.document)
            .unbind('keydown', touchTimeOfLastInteraction)
            .unbind('mousedown', touchTimeOfLastInteraction)
            .unbind('mousemove', touchTimeOfLastInteraction);

        stopProgressBarTimer();
        progressBarElement
            .progressbar('destroy')
            .remove();

        dialogElement
            .unbind('dialogclose', stopProgressBarTimer)
            .unbind('dialogopen', startProgressBarTimer)
            .dialog('destroy');

        if (!(options.message instanceof jQuery)) {
            dialogElement.remove();
        }

        isStarted = false;

    }

    // public api
    if (!$[PLUGIN_NAME]) {
        $[PLUGIN_NAME] =  {
            start: start,
            stop: stop,
            isStarted: function() {
                return isStarted;
            }
        };
    }

}(jQuery));

Usage

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Untitled Page</title>
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" />
    <!--<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/ui-lightness/jquery-ui.css" />-->
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.js"></script>
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.0/jquery-ui.js"></script>
    <script type="text/javascript" src="jquery.areyoustillthere.js"></script>
    <script type="text/javascript">
        $(function() {
            $.areyoustillthere.start({
                title: 'this is a better title',
                idle: function() {
                    $.areyoustillthere.stop();
                    // TODO: redirect to a sign-out page
                }
            });
        });
    </script>
</head>
<body>
</body>
</html>

No comments:

Post a Comment