/* globals _, JSON */
hqDefine('analytics/js/utils', function () {
    'use strict';

    /**
     * A helper function for for tracking google analytics events after a click
     * on an element in the dom.
     *
     * @param {(object|string)} element - A DOM element, jQuery object, or selector.
     * @param {function} trackFunction -
     *      A function that takes a single optional callback.
     *      If called without the callback, this function should
     *      record an event. If called with a callback, this
     *      function should record an event and call the
     *      callback when finished.
     */
    var trackClickHelper = function (element, trackFunction) {
        var eventHandler = function (event) {
            var $this = $(this);
            if (event.metaKey || event.ctrlKey || event.which === 2  // which === 2 for middle click
                || event.currentTarget.target && event.currentTarget !== "_self") {
                // The current page isn't being redirected so just track the click
                // and don't prevent the default click action.
                trackFunction();
            } else {
                // Track how many trackLinkHelper-related handlers
                // this event has, so we only actually click
                // once, after they're all complete.
                var $target = $(event.delegateTarget);
                var count = $target.data("track-link-count") || 0;
                count++;
                $target.data("track-link-count", count);

                event.preventDefault();
                var callbackCalled = false;
                var callback = function () {
                    if (!callbackCalled) {
                        var $target = $(event.delegateTarget);
                        var count = $target.data("track-link-count");
                        count--;
                        $target.data("track-link-count", count);
                        if (!count) {
                            document.location = $this.attr('href');
                        }
                        callbackCalled = true;
                    }
                };
                // callback might not get executed if, say, gtag can't be reached.
                setTimeout(callback, 2000);
                trackFunction(callback);
            }
        };
        if (typeof element === 'string') {
            $(element).on('click', eventHandler);
        } else {
            if (element.nodeType){
                element = $(element);
            }
            element.click(eventHandler);
        }
    };

    /**
     * Inserts a <script async src="srcUrl" type="text/javascript"></script>
     * tag into the DOM.
     * @param {string} srcUrl
     */
    var insertAsyncScript = function (srcUrl) {
        setTimeout(function(){
            var d = document,
                f = d.getElementsByTagName('script')[0],
                s = d.createElement('script');
            s.type = 'text/javascript';
            s.async = true;
            s.src = srcUrl;
            f.parentNode.insertBefore(s, f);
        }, 1);
    };

    /**
     * Makes sure an analytics callback ONCE is called even if the command fails
     * @param {function} callback
     * @param {integer} timeout - (optional)
     * @returns {function} once callback
     */
    var createSafeCallback = function (callback, timeout) {
        var oneTimeCallback = callback;
        if (_.isFunction(callback)){
            oneTimeCallback = _.once(callback);
            setInterval(oneTimeCallback, timeout ? timeout : 2000);
        }
        return oneTimeCallback;
    };

    return {
        trackClickHelper: trackClickHelper,
        insertAsyncScript: insertAsyncScript,
        createSafeCallback: createSafeCallback,
    };
});

@@ -2640,22 +2640,16 @@
 %3Cscript 
-async 
 src=%22src
@@ -2735,21 +2735,173 @@
 tring%7D s
-rcUrl
+criptSrc%0A     * @param %7Bfunction%7D loggingFn - logs on success or failure of script%0A     * @param %7Bobject%7D options - (optional) options added on to the script
 %0A     */
@@ -2911,29 +2911,24 @@
   var insert
-Async
 Script = fun
@@ -2935,21 +2935,44 @@
 ction (s
-rcUrl
+criptSrc, loggingFn, options
 ) %7B%0A    
@@ -3015,16 +3015,18 @@
    var d
+oc
  = docum
@@ -3047,20 +3047,35 @@
        f
+irstScriptTag
  = d
+oc
 .getElem
@@ -3119,20 +3119,27 @@
        s
+cript
  = d
+oc
 .createE
@@ -3169,16 +3169,21 @@
        s
+cript
 .type = 
@@ -3214,16 +3214,21 @@
        s
+cript
 .async =
@@ -3251,16 +3251,21 @@
    s
+cript
 .src = s
 rcUr
@@ -3264,73 +3264,609 @@
  = s
-rcUrl;%0A            f.parentNode.insertBefore(s, f);%0A        %7D, 1)
+criptSrc;%0A            _.each(options %7C%7C %7B%7D, function(val, key) %7B%0A                script%5Bkey%5D = val;%0A            %7D);%0A            script.addEventListener('error', function () %7B%0A                loggingFn(scriptSrc, %22Failed to Load Script - Check Adblocker%22);%0A            %7D);%0A            script.addEventListener('load', function () %7B%0A                loggingFn(scriptSrc, %22Loaded Script%22);%0A            %7D);%0A            firstScriptTag.parentNode.insertBefore(script, firstScriptTag);%0A        %7D, 1);%0A    %7D;%0A%0A    var getDateHash = function () %7B%0A        var e = 3e5;%0A        return Math.ceil(new Date() / e) * e
 ;%0A  
@@ -4458,29 +4458,24 @@
       insert
-Async
 Script: inse
@@ -4480,13 +4480,8 @@
 sert
-Async
 Scri
@@ -4532,16 +4532,50 @@
 llback,%0A
+        getDateHash: getDateHash,%0A
     %7D;%0A%7D

update async script load method to handle failure. include hash generator