Source: ClientSideUserId/clientsideuserid.js

/**
 * @class
 * @name ClientSideUserId
 * @memberof ATInternet.Tracker.Plugins
 * @type {function}
 * @param parent {object} Instance of the Tag used
 * @description
 * Plugin to enable a visitor to be tracked by creating a user ID in cases where writing a third-party cookie is not possible.
 * <br />For example, this might be the case on an Intranet site,
 * or when the Safari browser is used (default settings),
 * or when a visitor browses using an Apple device like iPhone, iPad or iPod.
 * It is also possible to configure the plugin to force the use of a random or custom ID.
 * This ID can be found in the idclient variable, present in all hits when the plugin is active.
 * @public
 */
window['ATInternet']['Tracker']['Plugins']['ClientSideUserId'] = function (parent) {
    "use strict";

    var _config = {};
    var _userIdFromContext = undefined;
    var _userIdFromStorage = null;
    var _isClient = false;
    var _isOptOut = false;
    var _isFromCustomerContext = false;
    var _user = {
        userID: '',
        storageName: '',
        isFromContext: false,
        force: false
    };
    var _eventID = -1;

    // Set specific plugin configuration.
    // If global configuration already exists, set only undefined properties.
    parent.configPlugin('ClientSideUserId', dfltPluginCfg || {}, function (newConf) {
        _config = newConf;
    });

    /**
     * Check if general configuration implies ClientSideUserId mode activation.
     * <br />Note : konqueror|Lunascape|midori|OmniWeb browsers are managed like Safari
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @return {boolean}
     * @private
     */
    var _isClientConfiguration = function () {
        var isClient = false;
        if (_config.clientSideMode === 'required') {
            var userAgent = '';
            if (window.navigator) {
                userAgent = window.navigator.userAgent;
            }
            if ((/Safari/.test(userAgent) && !/Chrome/.test(userAgent))
                || /iPhone|iPod|iPad/.test(userAgent)) {
                isClient = true;
            }
        }
        else if (_config.clientSideMode === 'always') {
            isClient = true;
        }
        return isClient;
    };

    /**
     * Check if OPT-OUT mode is activated or not.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @return {boolean}
     * @private
     */
    var _isOptedOutMode = function () {
        var isOptedOut = false;
        var optedOut = ATInternet.Utils.optedOut;
        // 1. Warning, do this check first before recovering atoptedout
        if (optedOut === false) {
            parent.storage.del('atoptedout');
            if (parent.getParam('idclient') === _config.optOut) {
                parent.delParam('idclient');
            }
        }
        // 2. Recover atoptedout
        var storageOptedOut = parent.storage.get('atoptedout', true);
        if ((optedOut === true) || (storageOptedOut === _config.optOut)) {
            isOptedOut = true;
        }
        if (isOptedOut) {
            ATInternet.Utils.optedOut = true;
        }
        return isOptedOut;
    };

    /**
     * Init global context.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @private
     */
    var _initContext = function () {
        // Get current user IDs
        _userIdFromContext = parent.getContext('userIdentifier');
        _userIdFromStorage = parent.storage.get('atuserid');
        // Init context parameters
        _isClient = _isClientConfiguration();
        _isOptOut = _isOptedOutMode();
        _isFromCustomerContext = (typeof _userIdFromContext !== 'undefined');
    };

    /**
     * Init user object with "userID, storageName and isfromContext" values.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @private
     */
    var _initUser = function () {
        _user.storageName = _config.userIdStorageName;
        _user.isFromContext = false;
        _user.force = false;
        if (_isOptOut) {
            _user.userID = _config.optOut;
            _user.storageName = _config.optOutStorageName;
            _user.isFromContext = true;
            _user.force = true;
        }
        else if (parent.getConfig('disableCookie') || parent.getConfig('disableStorage')) {
            _user.userID = parent.getParam('idclient');
            _user.isFromContext = true;
        }
        else if (_isFromCustomerContext) {
            _user.userID = _userIdFromContext;
            _user.isFromContext = true;
        }
        else if (_userIdFromStorage !== null) {
            _user.userID = _userIdFromStorage;
        }
        else {
            var uuid = ATInternet.Utils.uuid();
            _user.userID = uuid.v4();
        }
    };

    /**
     * Create or update stored data value for identifier.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @private
     */
    var _setStorageValue = function () {
        if (_config.userIdExpirationMode === 'relative' || (_config.userIdExpirationMode === 'fixed' && _userIdFromStorage === null) || _user.isFromContext) {
            var expiration_date = new Date();
            expiration_date.setTime(expiration_date.getTime() + (_config.userIdCookieDuration * 24 * 60 * 60 * 1000));
            parent.storage.set(_user.storageName, _user.userID, {end: expiration_date, path: '/'}, _user.force);
            var data = parent.storage.get(_user.storageName, true);
            if (ATInternet.Utils.consent && !_user.isFromContext && _user.userID !== data) {
                parent.setParam('idclient', _user.userID + '-NO', {
                    multihit: true,
                    permanent: true,
                    hitType: ['all']
                });
            }
        }
    };

    /**
     * Set user ID in buffer and storage.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @private
     */
    var _setUserId = function () {
        parent.setParam('idclient', _user.userID, {multihit: true, permanent: true, hitType: ['all']});
        _setStorageValue();
    };

    /**
     * Get context and stored values according to priorities or generate a new GUID.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @private
     */
    var _run = function () {
        _initContext();
        if (_isClient || _isOptOut || _isFromCustomerContext) {
            _initUser();
            _setUserId();
        }
    };

    /**
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @description
     * React to OPT-OUT actions.
     * @param event {object}
     * @private
     */
    var _processEvent = function (event) {
        if (event) {
            var detail = event.detail;
            if (detail && (detail.name === 'clientsideuserid') && (detail.id === _eventID)) {
                _run();
            }
        }
    };

    /**
     * Launch plugin when all dependencies are loaded.
     * @memberof ATInternet.Tracker.Plugins.ClientSideUserId#
     * @function
     * @private
     */
    var _init = function () {
        var dependencies = ['Storage'];
        parent.plugins.waitForDependencies(dependencies, function () {
            var uuid = ATInternet.Utils.uuid();
            _eventID = parseInt(uuid.num(8));
            // Attach event in order to process OPT-OUT actions
            ATInternet.Utils.removeOptOutEvent(_processEvent);
            ATInternet.Utils.addOptOutEvent(_eventID, _processEvent);
            _run();
        });
    };

    // Initialise global process.
    _init();

    /**
     * [Object added by plugin {@link ATInternet.Tracker.Plugins.ClientSideUserId ClientSideUserId}] Tags to manage a user ID on client side mode.
     * @name clientSideUserId
     * @inner
     * @type {object}
     * @memberof ATInternet.Tracker.Tag
     * @property {function} set Tag helper, see details here {@link ATInternet.Tracker.Tag#clientSideUserId.set}
     * @public
     */
    parent.clientSideUserId = {};

    /**
     *[Helper added by plugin {@link ATInternet.Tracker.Plugins.ClientSideUserId ClientSideUserId}] Add a user ID to all hits "&idclient=" and store it in "atuserid" or "atoptedout".
     * @alias clientSideUserId.set
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @param id {string|number} User ID
     * @example
     * <pre><code class="javascript">tag.clientSideUserId.set('abc123');
     * </code></pre>
     * @public
     */
    parent.clientSideUserId.set = function (id) {
        if (!_isOptOut) {
            _user.userID = id;
            _user.isFromContext = true;
            _user.storageName = _config.userIdStorageName;
            _user.force = false;
            _setUserId();
        }
        /* @if debug */
        parent.debug('ClientSideUserId:clientSideUserId:set', 'DEBUG', 'method ended', {id: id});
        /* @endif */
    };

    /**
     *[Helper added by plugin {@link ATInternet.Tracker.Plugins.ClientSideUserId ClientSideUserId}] Force idclient storage (useful for consent page mode).
     * @alias clientSideUserId.store
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @example
     * <pre><code class="javascript">tag.clientSideUserId.store();
     * </code></pre>
     * @public
     */
    parent.clientSideUserId.store = function () {
        _user.isFromContext = true;
        _user.force = true;
        _setStorageValue();
        /* @if debug */
        parent.debug('ClientSideUserId:clientSideUserId:store', 'DEBUG', 'method ended', {id: _user.userID});
        /* @endif */
    };

    /**
     *[Helper added by plugin {@link ATInternet.Tracker.Plugins.ClientSideUserId ClientSideUserId}] Get user ID.
     * @alias clientSideUserId.get
     * @memberof! ATInternet.Tracker.Tag#
     * @function
     * @example
     * <pre><code class="javascript">tag.clientSideUserId.get();
     * </code></pre>
     * @return {string|number}
     * @public
     */
    parent.clientSideUserId.get = function () {
        /* @if debug */
        parent.debug('ClientSideUserId:clientSideUserId:get', 'DEBUG', 'method ended', {id: _user.userID});
        /* @endif */
        return _user.userID;
    };

    // For unit tests on private elements !!!
    /* @if test */
    var _this = this;
    _this._isClientConfiguration = _isClientConfiguration;
    _this._isOptedOutMode = _isOptedOutMode;
    _this._initContext = _initContext;
    _this._initUser = _initUser;
    _this._setStorageValue = _setStorageValue;
    _this._setUserId = _setUserId;
    _this._run = _run;
    _this._processEvent = _processEvent;
    _this._init = _init;
    _this._userIdCookieDuration = _config.userIdCookieDuration;
    _this._userIdExpirationMode = _config.userIdExpirationMode;
    _this._optOut = _config.optOut;
    _this._userIdStorageName = _config.userIdStorageName;
    _this._optOutStorageName = _config.optOutStorageName;
    _this._userIdFromContext = _userIdFromContext;
    _this._userIdFromStorage = _userIdFromStorage;
    _this.getThemAll = function () {
        _this._userIdCookieDuration = _config.userIdCookieDuration;
        _this._userIdExpirationMode = _config.userIdExpirationMode;
        _this._optOut = _config.optOut;
        _this._userIdStorageName = _config.userIdStorageName;
        _this._optOutStorageName = _config.optOutStorageName;
        _this._userIdFromContext = _userIdFromContext;
        _this._userIdFromStorage = _userIdFromStorage;
    };
    /* @endif */

};
window['ATInternet']['Tracker']['addPlugin']('ClientSideUserId');