Source: Utils/utils.js

/**
 * @class
 * @classdesc Plugin with utils methods.
 * @name Utils
 * @memberof ATInternet.Tracker.Plugins
 * @type {function}
 * @param parent {object} Instance of the Tag used
 * @description
 * This plugin shares specific feature methods to our helper plugins.
 * For example plugins Page, Clicks and OnSiteAds have common usages.
 * @public
 */
window['ATInternet']['Tracker']['Plugins']['Utils'] = function (parent) {
    "use strict";
    var self = this;

    // storage for the querystrings processed, key is the hash of the querystring, value is an object with all querystring parameters
    var _queryStringValues = {};

    /**
     * [Object added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Methods to .
     * @name utils
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @type {object}
     * @property {function} get Tag helper, see details here {@link ATInternet.Tracker.Tag#utils.getQueryStringValue}
     * @property {function} getPrivate Tag helper, see details here {@link ATInternet.Tracker.Tag#utils.getLocation}
     * @public
     */
    parent.utils = {};


    /**
     * Get the value of a queryString variable
     * @name getQueryStringValue
     * @memberof ATInternet.Tracker.Plugins.Utils#
     * @function
     * @param name {string} Variable that you want to get
     * @param str {string} String in which we will look the wanted variable (considered as queryString format)
     * @return string|null|undefined {string|null|undefined} This method returns a string if the variable exists with a value,
     * it returns null if the variable is not present (or not correct for a queryString) or
     * it returns undefined if the variable exists but doesn't have any value (ex : '&variable=')
     * @public
     */
    parent.utils.getQueryStringValue = self.getQueryStringValue = function (name, str) {
        var hash = ATInternet.Utils.hashcode(str).toString();
        if (!_queryStringValues[hash]) {
            _queryStringValues[hash] = {};
            var regex = new RegExp('[&#?]{1}([^&=#?]*)=([^&#]*)?', 'g'),
                match = regex.exec(str);
            if (match !== null) {
                while (match !== null) {
                    _queryStringValues[hash][match[1]] = match[2];
                    match = regex.exec(str);
                }
            }
        }
        if (_queryStringValues[hash].hasOwnProperty(name)) {
            return _queryStringValues[hash][name];
        } else {
            return null;
        }
    };

    /**
     * Check an iterative property in an object and build a specific string from it
     * @name manageChapters
     * @memberof ATInternet.Tracker.Plugins.Utils#
     * @function
     * @param tagObject {object} Object containing properties to check
     * @param key {string} Key word to check
     * @param length {number} Number of occurence possible
     * @return {string}
     * @example
     * <pre>
     * var myObject = {
     *     chapter1:'one',
     *     chapter2:'two'
     * }
     * var chapters = manageChapters(myObject, 'chapter', 2);
     * // chapters = 'one::two::'
     * </pre>
     * @public
     */
    self.manageChapters = function (tagObject, key, length) {
        var config = parent.getConfig('ignoreEmptyChapterValue');
        var chapt = '';
        if (tagObject) {
            length = parseInt(length, 10);
            for (var i = 1; i < length + 1; i++) {
                var chapterName = tagObject[key + i] || '';
                if (config) {
                    chapt += chapterName ? chapterName + '::' : '';
                } else {
                    chapt += tagObject.hasOwnProperty(key + i) ? chapterName + '::' : '';
                }
            }
        }
        return chapt;
    };

    /**
     * Get document level to use from global configuration or the default one (document.)
     * @name getDocumentLevel
     * @memberof ATInternet.Tracker.Plugins.Utils#
     * @function
     * @return {object}
     * @public
     */
    self.getDocumentLevel = function () {
        var documentLevel = parent.getConfig('documentLevel');
        if (documentLevel.indexOf('.') < 0) {
            return (window[documentLevel] || document);
        } else {
            var tab = documentLevel.split('.');
            return (window[tab[0]][tab[1]] || document);
        }
    };

    /**
     * Get the current location (URL associated with the document level configured)
     * @name getLocation
     * @memberof ATInternet.Tracker.Plugins.Utils#
     * @function
     * @return {string}
     * @public
     */
    parent.utils.getLocation = self.getLocation = function () {
        var obj = self.getDocumentLevel();
        return obj.location.href;
    };

    //////////////////////////////  dispatch  ///////////////////////////////////

    parent.dispatchIndex = {};
    parent.dispatchStack = [];
    parent.dispatchEventFor = {};
    var ExecWaitingSpecificDispatchCount = 0;

    /**
     * [Function added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Add a helper plugin into a list telling which ones have been used before dispatching
     * @name dispatchSubscribe
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param helperName {string} Plugin name to add
     * @return {boolean}
     * @private
     */
    parent.dispatchSubscribe = function (helperName) {
        if (!parent.dispatchIndex[helperName]) {
            parent.dispatchStack.push(helperName);
            parent.dispatchIndex[helperName] = true;
            return true;
        }
        return false;
    };

    /**
     * [Function added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Return a boolean which tell if the helper plugin concerned is waiting for a dispatch
     * @name dispatchSubscribed
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param helperName {string} Plugin name to check
     * @return {boolean}
     * @private
     */
    parent.dispatchSubscribed = function (helperName) {
        return parent.dispatchIndex[helperName] === true;
    };

    /**
     * [Function added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Add a specific event for dispatching
     * @name addSpecificDispatchEventFor
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param helperName {string} Plugin name to check
     * @return {boolean}
     * @private
     */
    parent.addSpecificDispatchEventFor = function (helperName) {
        if (!parent.dispatchEventFor[helperName]) {
            parent.dispatchEventFor[helperName] = true;
            ExecWaitingSpecificDispatchCount++;
            return true;
        }
        return false;
    };

    /**
     * Test if specific dispatch are being processed
     * @memberof ATInternet.Tracker.Plugins.Utils#
     * @function
     * @return {boolean}
     * @private
     */
    var _IsExecWaitingSpecificDispatch = function () {
        return ExecWaitingSpecificDispatchCount !== 0;
    };

    /**
     * [Function added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Process a specific event for dispatching
     * @name processSpecificDispatchEventFor
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param helperName {string} Plugin name to check
     * @private
     */
    parent.processSpecificDispatchEventFor = function (helperName) {
        if (parent.dispatchEventFor[helperName]) {
            parent.dispatchEventFor[helperName] = false;
            ExecWaitingSpecificDispatchCount--;
            if (ExecWaitingSpecificDispatchCount === 0) {
                parent.dispatchEventFor = {};
                parent['emit']('Tracker:Plugin:SpecificEvent:Exec:Complete', {lvl: 'INFO'});
            }
        }
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Dispatch all tags.
     * @name dispatch
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param callback {function} Callback to execute
     * @public
     */
    parent.dispatch = function (callback) {
        var _doDispatch = function () {
            var name = '';
            var param = null;
            while (parent.dispatchStack.length > 0) {
                name = parent.dispatchStack.pop();
                if (parent.dispatchStack.length === 0) {
                    param = callback
                }
                parent[name]['onDispatch'](param);
            }
            parent.dispatchIndex = {};
            parent.delContext(undefined, 'customObject');
            /* @if debug */
            parent.debug('Utils:dispatch', 'DEBUG', 'method ended');
            /* @endif */
        };
        var _processDispatch = function () {
            if (!parent.plugins.isExecWaitingLazyloading()) {
                _doDispatch();
            } else {
                // it manages both cases, lazyload plugins can finish loading after some lazyload triggers or the contrary
                parent['onTrigger']('Tracker:Plugin:Lazyload:Exec:Complete', function () {
                    _doDispatch();
                }, true);
            }
        };
        if (!_IsExecWaitingSpecificDispatch()) {
            _processDispatch();
        } else {
            parent['onTrigger']('Tracker:Plugin:SpecificEvent:Exec:Complete', function () {
                _processDispatch();
            }, true);
        }
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Dispatch all tags (with automatic redirect management).
     * @name dispatchRedirect
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param tagObject {object} Tag object, used here to tell how we have to do the redirection (url/target)
     * @return {boolean}
     * @public
     */
    parent.dispatchRedirect = function (tagObject) {
        var preservePropagation = true;
        var callback = null;
        if (tagObject) {
            var eventObject = null;
            if (tagObject && tagObject.hasOwnProperty('event')) {
                eventObject = tagObject.event || window.event;
            }
            if (!ATInternet.Utils.isTabOpeningAction(eventObject) && tagObject.elem) {
                tagObject.elem.timeoutonly = true;
                parent.plugins.exec('TechClicks', 'manageClick', [tagObject.elem, eventObject], function (data) {
                    preservePropagation = data;
                });
            }
            callback = tagObject.callback;
        }
        parent.dispatch(callback);
        return preservePropagation;
    };

    //////////////////////////////  dispatch  ///////////////////////////////////

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Manage send depending on Safari previewing and Chrome/IE prerendering.
     * @name manageSend
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param callback {function} Function to execute when not in prerendering mode or on "visibilityChangeEvent"
     * @private
     */
    var _manageSend = parent.manageSend = function (callback) {
        if (!ATInternet.Utils.isPreview() || parent.getConfig('preview')) {
            if (!ATInternet.Utils.isPrerender(function (event) {
                    callback(event);
                })) {
                callback();
            }
        }
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Merge Tag object with the corresponding permanent buffered object if exists.
     * @name processTagObject
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param hitParam {string} Hit parameter
     * @param hitType {array} Hit types
     * @param tagObject {object} Tag object
     * @return {object}
     * @private
     */
    parent.processTagObject = function (hitParam, hitType, tagObject) {
        var bufferObject = parent.getParam(hitParam, true);
        if (bufferObject && bufferObject.options['permanent']) {
            var isValidType = false;
            var bufferObjectType = bufferObject.options['hitType'] || [];
            for (var i = 0; i < bufferObjectType.length; i++) {
                if (ATInternet.Utils.arrayIndexOf(hitType.concat('all'), bufferObjectType[i]) !== -1) {
                    isValidType = true;
                    break;
                }
            }
            if (isValidType) {
                tagObject = ATInternet.Utils.completeFstLevelObj((bufferObject.value || {}), tagObject, true);
            }
        }
        return tagObject;
    };

    /**
     * [Helper added by plugin {@link ATInternet.Tracker.Plugins.Utils Utils}] Merge object from Context with the corresponding buffered object if exists before sending hit.
     * @name processContextObjectAndSendHit
     * @memberof ATInternet.Tracker.Tag
     * @inner
     * @function
     * @param hitParam {string} Hit parameter
     * @param paramOptions {object} Parameter options
     * @param contextObject {object} Context object
     * @param callback {function} Callback to execute
     * @private
     */
    parent.processContextObjectAndSendHit = function (hitParam, paramOptions, contextObject, callback) {
        var bufferObject = parent.getParam(hitParam, true);
        if (bufferObject) {
            var isValidType = false;
            var bufferObjectType = bufferObject.options['hitType'] || [];
            for (var i = 0; i < bufferObjectType.length; i++) {
                if (ATInternet.Utils.arrayIndexOf(paramOptions.hitType.concat('all'), bufferObjectType[i]) !== -1) {
                    isValidType = true;
                    break;
                }
            }
            if (isValidType) {
                var clonedBufferObject = ATInternet.Utils.cloneSimpleObject(bufferObject);
                clonedBufferObject.value = ATInternet.Utils.completeFstLevelObj((clonedBufferObject.value || {}), contextObject, true);
                parent.setParam(hitParam, clonedBufferObject.value, {
                    hitType: paramOptions.hitType,
                    encode: paramOptions.encode,
                    separator: paramOptions.separator,
                    truncate: paramOptions.truncate
                });
                _manageSend(function () {
                    parent.sendHit(null, [['hitType', paramOptions.hitType]], callback, null, true);
                });
                var isPermanent = bufferObject.options['permanent'];
                if (isPermanent) {
                    parent.setParam(hitParam, bufferObject.value, bufferObject.options);
                }
            }
            else {
                parent.setParam(hitParam, contextObject, {
                    hitType: paramOptions.hitType,
                    encode: paramOptions.encode,
                    separator: paramOptions.separator,
                    truncate: paramOptions.truncate
                });
                _manageSend(function () {
                    parent.sendHit(null, [['hitType', paramOptions.hitType]], callback, null, true);
                });
                parent.setParam(hitParam, bufferObject.value, bufferObject.options);
            }
        }
        else {
            parent.setParam(hitParam, contextObject, {
                hitType: paramOptions.hitType,
                encode: paramOptions.encode,
                separator: paramOptions.separator,
                truncate: paramOptions.truncate
            });
            _manageSend(function () {
                parent.sendHit(null, [['hitType', paramOptions.hitType]], callback, null, true);
            });
        }
    };

    /* @if test */
    self.getQueryStringValuesHash = function () {
        return _queryStringValues
    };
    /* @endif */
};

window['ATInternet']['Tracker']['addPlugin']('Utils');