Source: MobileAnalyticsSession.js

var AMA = global.AMA;
AMA.Storage = require('./StorageClients/LocalStorage.js');
AMA.StorageKeys = require('./StorageClients/StorageKeys.js');
AMA.Util = require('./MobileAnalyticsUtilities.js');
/**
 * @name AMA.Session
 * @namespace AMA.Session
 * @constructor
 * @param {Object=}     [options=] - A configuration map for the Session
 * @param {string=}     [options.sessionId=Utilities.GUID()]- A sessionId for session.
 * @param {string=}     [options.appId=new Date().toISOString()] - The start Timestamp (default now).
 * @param {number=}     [options.sessionLength=600000] - Length of session in Milliseconds (default 10 minutes).
 * @param {AMA.Session.ExpirationCallback=}   [options.expirationCallback] - Callback Function for when a session expires
 * @param {AMA.Client.Logger=} [options.logger=] - Object containing javascript style logger functions (passing console
 *                                                 will output to browser dev consoles)
 */
/**
 * @callback AMA.Session.ExpirationCallback
 * @param {AMA.Session} session
 * @returns {boolean|int} - Returns either true to extend the session by the sessionLength or an int with the number of
 *                          seconds to extend the session.  All other values will clear the session from storage.
 */
AMA.Session = (function () {
    'use strict';
    /**
     * @lends AMA.Session
     */
    var Session = function (options) {
        this.options = options || {};
        this.options.logger = this.options.logger || {};
        this.logger = {
            log: this.options.logger.log || AMA.Util.NOP,
            info: this.options.logger.info || AMA.Util.NOP,
            warn: this.options.logger.warn || AMA.Util.NOP,
            error: this.options.logger.error || AMA.Util.NOP
        };
        this.logger.log = this.logger.log.bind(this.options.logger);
        this.logger.info = this.logger.info.bind(this.options.logger);
        this.logger.warn = this.logger.warn.bind(this.options.logger);
        this.logger.error = this.logger.error.bind(this.options.logger);
        this.logger.log('[Function:(AWS.MobileAnalyticsClient)Session Constructor]' +
            (options ? '\noptions:' + JSON.stringify(options) : ''));
        this.options.expirationCallback = this.options.expirationCallback || AMA.Util.NOP;
        this.id = this.options.sessionId || AMA.Util.GUID();
        this.sessionLength = this.options.sessionLength || 600000; //Default session length is 10 minutes
        //Suffix the AMA.Storage Keys with Session Id to ensure proper scope
        this.StorageKeys = {
            'SESSION_ID': AMA.StorageKeys.SESSION_ID + this.id,
            'SESSION_EXPIRATION': AMA.StorageKeys.SESSION_EXPIRATION + this.id,
            'SESSION_START_TIMESTAMP': AMA.StorageKeys.SESSION_START_TIMESTAMP + this.id
        };
        this.startTimestamp = this.options.startTime ||
            this.options.storage.get(this.StorageKeys.SESSION_START_TIMESTAMP) ||
            new Date().toISOString();
        this.expirationDate = parseInt(this.options.storage.get(this.StorageKeys.SESSION_EXPIRATION), 10);
        if (isNaN(this.expirationDate)) {
            this.expirationDate = (new Date().getTime() + this.sessionLength);
        }
        this.options.storage.set(this.StorageKeys.SESSION_ID, this.id);
        this.options.storage.set(this.StorageKeys.SESSION_EXPIRATION, this.expirationDate);
        this.options.storage.set(this.StorageKeys.SESSION_START_TIMESTAMP, this.startTimestamp);
        this.sessionTimeoutReference = setTimeout(this.expireSession.bind(this), this.sessionLength);
    };

    /**
     * Expire session and clear session
     * @param {expirationCallback=} Callback function to call when sessions expire
     */
    Session.prototype.expireSession = function (expirationCallback) {
        this.logger.log('[Function:(Session).expireSession]');
        expirationCallback = expirationCallback || this.options.expirationCallback;
        var shouldExtend = expirationCallback(this);
        if (typeof shouldExtend === 'boolean' && shouldExtend) {
            shouldExtend = this.options.sessionLength;
        }
        if (typeof shouldExtend === 'number') {
            this.extendSession(shouldExtend);
        } else {
            this.clearSession();
        }
    };

    /**
     * Clear session from storage system
     */
    Session.prototype.clearSession = function () {
        this.logger.log('[Function:(Session).clearSession]');
        clearTimeout(this.sessionTimeoutReference);
        this.options.storage.delete(this.StorageKeys.SESSION_ID);
        this.options.storage.delete(this.StorageKeys.SESSION_EXPIRATION);
        this.options.storage.delete(this.StorageKeys.SESSION_START_TIMESTAMP);
    };



    /**
     * Extend session by adding to the expiration timestamp
     * @param {int} [sessionExtensionLength=sessionLength] - The number of milliseconds to add to the expiration date
     *                                                       (session length by default).
     */
    Session.prototype.extendSession = function (sessionExtensionLength) {
        this.logger.log('[Function:(Session).extendSession]' +
                        (sessionExtensionLength ? '\nsessionExtensionLength:' + sessionExtensionLength : ''));
        sessionExtensionLength = sessionExtensionLength || this.sessionLength;
        this.setSessionTimeout(this.expirationDate + parseInt(sessionExtensionLength, 10));
    };

    /**
     * @param {string} [stopDate=now] - The ISO Date String to set the stopTimestamp to (now for default).
     */
    Session.prototype.stopSession = function (stopDate) {
        this.logger.log('[Function:(Session).stopSession]' +  (stopDate ? '\nstopDate:' + stopDate : ''));
        this.stopTimestamp = stopDate || new Date().toISOString();
    };

    /**
     * Reset session timeout to expire in a given number of seconds
     * @param {int} [milliseconds=sessionLength] - The number of milliseconds until the session should expire (from now). 
     */
    Session.prototype.resetSessionTimeout = function (milliseconds) {
        this.logger.log('[Function:(Session).resetSessionTimeout]' +
                        (milliseconds ? '\nmilliseconds:' + milliseconds : ''));
        milliseconds = milliseconds || this.sessionLength;
        this.setSessionTimeout(new Date().getTime() + milliseconds);
    };

    /**
     * Setter for the session timeout
     * @param {int} timeout - epoch timestamp
     */
    Session.prototype.setSessionTimeout = function (timeout) {
        this.logger.log('[Function:(Session).setSessionTimeout]' +  (timeout ? '\ntimeout:' + timeout : ''));
        clearTimeout(this.sessionTimeoutReference);
        this.expirationDate = timeout;
        this.options.storage.set(this.StorageKeys.SESSION_EXPIRATION, this.expirationDate);
        this.sessionTimeoutReference = setTimeout(this.expireSession.bind(this),
            this.expirationDate - (new Date()).getTime());
    };
    return Session;
}());

module.exports = AMA.Session;