var LeadDyno = LeadDyno || (function() {
  var _logger = null;

  var _tracking_id = null;

  var _fingerprintObj = {};
  var _fingerprint = null;

  var _is_mobile = false;

  var _proxy = null;

  var _url = 'https://collector.leaddyno.com/';

  var COOKIE_URL = 'LD_U';
  var COOKIE_REFERRER = 'LD_R';
  var COOKIE_TRACKING_ID = 'LD_T';
  var COOKIE_LEAD_DATA = 'LD_L';
  var COOKIE_HASH_DATA = 'LD_H';
  var COOKIE_PURCHASE_DATA = 'LD_P';
  var COOKIE_CANCEL_DATA = 'LD_X';
  var COOKIE_EXT_ID = 'LD_EXT';
  var COOKIE_STATE = 'LD_S';
  var COOKIE_GOOGLE = 'LD_G';

  var _ld_ready = false;
  var _readyHandlers = [];

  var _ld_iframe_mode = window.lead_dyno_iframe_mode == true;
  var _ld_cookie_prefix = window.lead_dyno_cookie_prefix || '';

  // Fingerprint code from https://raw.github.com/Valve/fingerprintjs/master/fingerprint.min.js
  // includes slight tweak to keep Fingerprint object in our own namespace
  !function(t,o){"use strict";var r=function(t){var r=Array.prototype.forEach,e=Array.prototype.map;this.each=function(t,e,a){if(null!==t)if(r&&t.forEach===r)t.forEach(e,a);else if(t.length===+t.length){for(var n=0,h=t.length;h>n;n++)if(e.call(a,t[n],n,t)==={})return}else for(var o in t)if(t.hasOwnProperty(o)&&e.call(a,t[o],o,t)==={})return},this.map=function(t,r,a){var n=[];return null==t?n:e&&t.map===e?t.map(r,a):(this.each(t,function(t,e,h){n[n.length]=r.call(a,t,e,h)}),n)},t&&(this.hasher=t)};r.prototype={get:function(){var r=[];r.push(navigator.userAgent),r.push(navigator.language),r.push(screen.colorDepth),r.push((new Date).getTimezoneOffset()),r.push(!!t.sessionStorage),r.push(this.hasLocalStorage());var e=this.map(navigator.plugins,function(t){var r=this.map(t,function(t){return[t.type,t.suffixes].join("~")}).join(",");return[t.name,t.description,r].join("::")},this).join(";");return r.push(e),this.hasher?this.hasher(r.join("###"),31):this.murmurhash3_32_gc(r.join("###"),31)},murmurhash3_32_gc:function(t,r){var e,a,n,h,o,i,s,c;for(e=3&t.length,a=t.length-e,n=r,o=3432918353,i=461845907,c=0;a>c;)s=255&t.charCodeAt(c)|(255&t.charCodeAt(++c))<<8|(255&t.charCodeAt(++c))<<16|(255&t.charCodeAt(++c))<<24,++c,s=4294967295&(65535&s)*o+((65535&(s>>>16)*o)<<16),s=s<<15|s>>>17,s=4294967295&(65535&s)*i+((65535&(s>>>16)*i)<<16),n^=s,n=n<<13|n>>>19,h=4294967295&5*(65535&n)+((65535&5*(n>>>16))<<16),n=(65535&h)+27492+((65535&(h>>>16)+58964)<<16);switch(s=0,e){case 3:s^=(255&t.charCodeAt(c+2))<<16;case 2:s^=(255&t.charCodeAt(c+1))<<8;case 1:s^=255&t.charCodeAt(c),s=4294967295&(65535&s)*o+((65535&(s>>>16)*o)<<16),s=s<<15|s>>>17,s=4294967295&(65535&s)*i+((65535&(s>>>16)*i)<<16),n^=s}return n^=t.length,n^=n>>>16,n=4294967295&2246822507*(65535&n)+((65535&2246822507*(n>>>16))<<16),n^=n>>>13,n=4294967295&3266489909*(65535&n)+((65535&3266489909*(n>>>16))<<16),n^=n>>>16,n>>>0},hasLocalStorage:function(){try{return!!t.localStorage}catch(r){return!0}}},o.Fingerprint=r}(window,_fingerprintObj);

  var hasher={}
  hasher.hash=function(){function n(n,r){var t=(65535&n)+(65535&r),e=(n>>16)+(r>>16)+(t>>16)
    return e<<16|65535&t}function r(n,r){return n<<r|n>>>32-r}function t(t,e,u,o,c,f){return n(r(n(n(e,t),n(o,f)),c),u)}function e(n,r,e,u,o,c,f){return t(r&e|~r&u,n,r,o,c,f)}function u(n,r,e,u,o,c,f){return t(r&u|e&~u,n,r,o,c,f)}function o(n,r,e,u,o,c,f){return t(r^e^u,n,r,o,c,f)}function c(n,r,e,u,o,c,f){return t(e^(r|~u),n,r,o,c,f)}function f(r,t){r[t>>5]|=128<<t%32,r[(t+64>>>9<<4)+14]=t
    var f,h,i,a,g,v=1732584193,l=-271733879,d=-1732584194,C=271733878
    for(f=0;f<r.length;f+=16)h=v,i=l,a=d,g=C,v=e(v,l,d,C,r[f],7,-680876936),C=e(C,v,l,d,r[f+1],12,-389564586),d=e(d,C,v,l,r[f+2],17,606105819),l=e(l,d,C,v,r[f+3],22,-1044525330),v=e(v,l,d,C,r[f+4],7,-176418897),C=e(C,v,l,d,r[f+5],12,1200080426),d=e(d,C,v,l,r[f+6],17,-1473231341),l=e(l,d,C,v,r[f+7],22,-45705983),v=e(v,l,d,C,r[f+8],7,1770035416),C=e(C,v,l,d,r[f+9],12,-1958414417),d=e(d,C,v,l,r[f+10],17,-42063),l=e(l,d,C,v,r[f+11],22,-1990404162),v=e(v,l,d,C,r[f+12],7,1804603682),C=e(C,v,l,d,r[f+13],12,-40341101),d=e(d,C,v,l,r[f+14],17,-1502002290),l=e(l,d,C,v,r[f+15],22,1236535329),v=u(v,l,d,C,r[f+1],5,-165796510),C=u(C,v,l,d,r[f+6],9,-1069501632),d=u(d,C,v,l,r[f+11],14,643717713),l=u(l,d,C,v,r[f],20,-373897302),v=u(v,l,d,C,r[f+5],5,-701558691),C=u(C,v,l,d,r[f+10],9,38016083),d=u(d,C,v,l,r[f+15],14,-660478335),l=u(l,d,C,v,r[f+4],20,-405537848),v=u(v,l,d,C,r[f+9],5,568446438),C=u(C,v,l,d,r[f+14],9,-1019803690),d=u(d,C,v,l,r[f+3],14,-187363961),l=u(l,d,C,v,r[f+8],20,1163531501),v=u(v,l,d,C,r[f+13],5,-1444681467),C=u(C,v,l,d,r[f+2],9,-51403784),d=u(d,C,v,l,r[f+7],14,1735328473),l=u(l,d,C,v,r[f+12],20,-1926607734),v=o(v,l,d,C,r[f+5],4,-378558),C=o(C,v,l,d,r[f+8],11,-2022574463),d=o(d,C,v,l,r[f+11],16,1839030562),l=o(l,d,C,v,r[f+14],23,-35309556),v=o(v,l,d,C,r[f+1],4,-1530992060),C=o(C,v,l,d,r[f+4],11,1272893353),d=o(d,C,v,l,r[f+7],16,-155497632),l=o(l,d,C,v,r[f+10],23,-1094730640),v=o(v,l,d,C,r[f+13],4,681279174),C=o(C,v,l,d,r[f],11,-358537222),d=o(d,C,v,l,r[f+3],16,-722521979),l=o(l,d,C,v,r[f+6],23,76029189),v=o(v,l,d,C,r[f+9],4,-640364487),C=o(C,v,l,d,r[f+12],11,-421815835),d=o(d,C,v,l,r[f+15],16,530742520),l=o(l,d,C,v,r[f+2],23,-995338651),v=c(v,l,d,C,r[f],6,-198630844),C=c(C,v,l,d,r[f+7],10,1126891415),d=c(d,C,v,l,r[f+14],15,-1416354905),l=c(l,d,C,v,r[f+5],21,-57434055),v=c(v,l,d,C,r[f+12],6,1700485571),C=c(C,v,l,d,r[f+3],10,-1894986606),d=c(d,C,v,l,r[f+10],15,-1051523),l=c(l,d,C,v,r[f+1],21,-2054922799),v=c(v,l,d,C,r[f+8],6,1873313359),C=c(C,v,l,d,r[f+15],10,-30611744),d=c(d,C,v,l,r[f+6],15,-1560198380),l=c(l,d,C,v,r[f+13],21,1309151649),v=c(v,l,d,C,r[f+4],6,-145523070),C=c(C,v,l,d,r[f+11],10,-1120210379),d=c(d,C,v,l,r[f+2],15,718787259),l=c(l,d,C,v,r[f+9],21,-343485551),v=n(v,h),l=n(l,i),d=n(d,a),C=n(C,g)
    return[v,l,d,C]}function h(n){var r,t="",e=32*n.length
    for(r=0;e>r;r+=8)t+=String.fromCharCode(n[r>>5]>>>r%32&255)
    return t}function i(n){var r,t=[]
    for(t[(n.length>>2)-1]=void 0,r=0;r<t.length;r+=1)t[r]=0
    var e=8*n.length
    for(r=0;e>r;r+=8)t[r>>5]|=(255&n.charCodeAt(r/8))<<r%32
    return t}function a(n){return h(f(i(n),8*n.length))}function g(n){var r,t,e="0123456789abcdef",u=""
    for(t=0;t<n.length;t+=1)r=n.charCodeAt(t),u+=e.charAt(r>>>4&15)+e.charAt(15&r)
    return u}function v(n){return unescape(encodeURIComponent(n))}function l(n){return a(v(n))}function d(n){return g(l(n))}return d}()

  _init();
    if(!_ld_iframe_mode) {
      main();
    }

    function main() {
      domReady(function () {
        documentReadyHandler();
      });
    }

    function documentReadyHandler() {
      if (_ld_ready) {
        return;
      }

      _ld_ready = true;

      setCookie(COOKIE_TRACKING_ID, _tracking_id);

      // class substitution code
      setInputValue('.leaddyno_visit_id', _tracking_id);
      setInputValue('.leaddyno_key', LeadDyno.key);

      // link variable substitution code
      eachNode(document.querySelectorAll('a'), function (anchor) {
        var url = anchor.getAttribute('href');
        if (url && typeof url == 'string') {
          if (url.indexOf('{{leaddyno.visit.id}}') > 0) {
            url = url.replace(/\{\{leaddyno\.visit\.id\}\}/gi, _tracking_id);
            anchor.setAttribute('href', url);
          }
          if (url.indexOf('{{leaddyno.key}}') > 0) {
            url = url.replace(/\{\{leaddyno\.key\}\}/gi, LeadDyno.key);
            anchor.setAttribute('href', url);
          }
        }
      });

      for (i = 0; i < _readyHandlers.length; i++) {
        _readyHandlers[i]();
      }

      initShareWidgets();
    }

    function onReady(handler) {
      if (_ld_ready) {
        handler();
      } else {
        _readyHandlers.push(handler);
      }
    }

    function isEmail(email) {
      return /\S+@\S+\.\S+/.test(email);
    }

    function _init() {
      if (_ld_iframe_mode) {
        try {
          if (window.addEventListener)
            // W3C DOM
            window.addEventListener('message', function (msg) {
              if (msg.data && msg.data.type == 'ld_send_tracking_id') {
                _tracking_id = msg.data.tracking_id;
                setCookie(COOKIE_TRACKING_ID, msg.data.tracking_id);
                main();
              }
            });
          else if (elem.attachEvent) {
            // IE DOM
            window.attachEvent('onmessage', function (msg) {
              if (msg.data && msg.data.type == 'ld_send_tracking_id') {
                _tracking_id = msg.data.tracking_id;
                setCookie(COOKIE_TRACKING_ID, msg.data.tracking_id);
                main();
              }
            });
          }
          window.top.postMessage({ type: 'ld_get_tracking_id' }, "*");
        } catch (e) {
          log("Could not establish iframe message handler! " + e);
        }
      } else {
        // singleton is not setup yet, so make sure to not call any methods on 'LeadDyno' object
        var cookieTrackingId = getCookie(COOKIE_TRACKING_ID);
        if (!cookieTrackingId) {
          if (!_tracking_id) {
            _tracking_id = generateUUID();
            setCookie(COOKIE_TRACKING_ID, _tracking_id);
          }
        } else {
          _tracking_id = cookieTrackingId;
        }
        try {
          if (window.addEventListener)
            // W3C DOM
            window.addEventListener('message', function (msg) {
              if (msg.data.type == 'ld_get_tracking_id') {
                msg.source.postMessage(
                  { type: 'ld_send_tracking_id', tracking_id: _tracking_id },
                  "*"
                );
              }
            });
          else if (elem.attachEvent) {
            // IE DOM
            window.attachEvent('onmessage', function (msg) {
              if (msg.data.type == 'ld_get_tracking_id') {
                msg.source.postMessage(
                  { type: 'ld_send_tracking_id', tracking_id: _tracking_id },
                  "*"
                );
              }
            });
          }
        } catch (e) {
          log("Could not establish enclosing window message handler! " + e);
        }
      }

      if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
      ) {
        _is_mobile = true;
      }

      _fingerprint = new _fingerprintObj.Fingerprint().get();
      if (_is_mobile || !_fingerprint || _fingerprint == 2147483647) {
        _fingerprint = "";
      }
    }

    function initShareWidgets() {

      var encodeIfDefined = function (val) {
        if (val) {
          return encode(val);
        } else {
          return "";
        }
      };

      var args = "&origin_url=" + encodeIfDefined(document.URL);
      args += "&key=" + encodeIfDefined(LeadDyno.key);
      args += "&channel=" + encodeIfDefined(LeadDyno.channel);
      args += "&title=" + encodeIfDefined(document.title);
      args += "&description=" + encodeIfDefined(getMetaContent("description"));
      args += "&visitor_id=" + encodeIfDefined(_tracking_id);
      args += "&og_title=" + encodeIfDefined(getMetaContent("og:title"));
      args += "&og_description=" + encodeIfDefined(getMetaContent("og:description"));
      args += "&og_image=" + encodeIfDefined(getMetaContent("og:image"));
      args += "&og_url=" + encodeIfDefined(getMetaContent("og:url"));
      args += "&ld_mail=" + encodeIfDefined(getMetaContent("ld:mail"));
      args += "&ld_mail_subject=" + encodeIfDefined(getMetaContent("ld:mail:subject"));
      args += "&ld_twitter=" + encodeIfDefined(getMetaContent("ld:tweet"));
      args += "&ld_facebook=" + encodeIfDefined(getMetaContent("ld:facebook"));
      args += "&ld_facebook_title=" + encodeIfDefined(getMetaContent("ld:facebook:title"));
      args += "&ld_pinterest=" + encodeIfDefined(getMetaContent("ld:pinterest"));
      args += "&ld_pinterest_img=" + encodeIfDefined(getMetaContent("ld:pinterest:img"));
      eachNode(document.querySelectorAll(".ld-share-link"), function(link){
        link.setAttribute('href', link.getAttribute('href') + args);
      });
    }

    function log(val) {
      if (_logger) {
        var message = "LeadDyno Log [" + new Date() + "] - " + val;
        _logger(message);
      }
    }

    function encode(val) {
      if (window.encodeURIComponent) {
        if (val !== null && typeof val === "object" && val.length > 0) {
          return encodeURIComponent(JSON.stringify(val));
        }
        return encodeURIComponent(val);
      } else {
        //noinspection JSDeprecatedSymbols
        return escape(val);
      }
    }

    function decode(val) {
      if (window.decodeURIComponent()) {
        return decodeURIComponent(val);
      } else {
        //noinspection JSDeprecatedSymbols
        return unescape(val);
      }
    }

    var jsonEncode = typeof JSON === 'object' && JSON.stringify ? JSON.stringify :
      function (obj) {
        var t = typeof (obj);
        if (t != "object" || obj === null) {
            // simple data type
            if (t == "string") obj = quoteString(obj);
            return String(obj);
        } else {
            // recurse array or object
            var n, v, json = [], arr = (obj && obj.constructor == Array);

            for (n in obj) {
                v = obj[n];
                t = typeof(v);
                if (obj.hasOwnProperty(n)) {
                    if (t == "string") v = quoteString(v); else if (t == "object" && v !== null) v = jsonEncode(v);
                    json.push((arr ? "" : quoteString(n+':')) + String(v));
                }
            }
            return (arr ? "[" : "{") + String(json) + (arr ? "]" : "}");
        }
      };

    var jsonDecode = typeof JSON === 'object' && JSON.parse ? JSON.parse : function (str) {
        return eval('(' + str + ')');
    };
    var escape = /["\\\x00-\x1f\x7f-\x9f]/g,
      meta = {
        '\b': '\\b',
        '\t': '\\t',
        '\n': '\\n',
        '\f': '\\f',
        '\r': '\\r',
        '"' : '\\"',
        '\\': '\\\\'
      };
    var quoteString = function (str) {
        if (str.match(escape)) {
            return '"' + str.replace(escape, function (a) {
                var c = meta[a];
                if (typeof c === 'string') {
                    return c;
                }
                c = a.charCodeAt();
                return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
            }) + '"';
        }
        return '"' + str + '"';
    };

    function processCookieName(name) {
        if(_ld_cookie_prefix) {
            return _ld_cookie_prefix + "_" + name;
        } else {
            return name;
        }
    }

    function getCookie(name) {
        name = processCookieName(name);
        log("Getting Cookie " + name);
        var i, x, y, c = document.cookie.split(";");
        for (i = 0; i < c.length; i++) {
            x = c[i].substr(0, c[i].indexOf("="));
            y = c[i].substr(c[i].indexOf("=") + 1);
            x = x.replace(/^\s+|\s+$/g, "");
            if (x == name) {
                return decode(y);
            }
        }
        return '';
    }

    function deleteCookie(name) {
      name = processCookieName(name);
      log("Deleting Cookie " + name);
        if (_proxy && _proxy.deleteCookie) {
            _proxy.deleteCookie(name);
        } else {
            document.cookie = name + '=; expires=Thu, 01 Jan 1970 00:00:01 GMT;path=/';
        }
    }

    function setCookie(name, val, timeout) {
      name = processCookieName(name);
      timeout = typeof timeout !== 'undefined' ? timeout : 100 * (86400 * 365); // defaults to 100 years
      timeout *= 1000; // ms to seconds
      log('Setting cookie  ' + name + ' to value ' + val);
      var d = new Date();
      d.setTime(d.getTime() + timeout);
      var cookie =
        name + '=' + encode(val) + ';expires=' + d.toUTCString() + ';path=/';
      if (LeadDyno && LeadDyno.domain) {
        cookie += ';domain=.' + LeadDyno.domain;
      }
      document.cookie = cookie;
    }

    function remote(action, args, cbFunction) {
      log('Invoking remote ' + action + ' with args ' + jsonEncode(args));

      if (!getCookie(COOKIE_TRACKING_ID)) {
        log('Tracking cookie is missing!');
        return;
      }

      var d = new Date();
      var cbName =
        '__LDCB_' + d.getTime() + '_' + Math.floor(Math.random() * 1000000);
      log('creating callback ' + cbName);

      window[cbName] = function (cbArgs) {
        log('In callback ' + cbName);
        if (cbFunction) {
          cbFunction(cbArgs);
        }
        if (args['onFinish']) {
          args['onFinish'](cbArgs);
        }
      };

      var argsStr = 'url=' + encode(getCookie(COOKIE_URL));
      argsStr += '&referrer=' + encode(getCookie(COOKIE_REFERRER));
      argsStr += '&agent=' + encode(navigator.userAgent);
      argsStr += '&js=true';
      argsStr += '&ld_t=' + encode(getCookie(COOKIE_TRACKING_ID));
      argsStr += '&ld_ext=' + encode(getCookie(COOKIE_EXT_ID));
      argsStr += '&ld_fp=' + encode(_fingerprint);
      argsStr += '&channel=' + encode(args.channel || LeadDyno.channel || '');
      argsStr += '&purchase_code=' + encode(args.purchase_code || '');
      argsStr += '&key=' + (args.key || LeadDyno.key || '');
      argsStr += '&callback=' + cbName;

      for (var key in args) {
        if (key != 'onFinish' && key != 'key') {
          argsStr += '&' + key + '=' + encode(args[key]);
        }
      }

      var source = _url + action + '?' + argsStr;

      if (_proxy && _proxy.ajax) {
        var res = _proxy.ajax(source);
        if (res == undefined || res == true) {
          window[cbName](); // call callback
        }
      } else {
        createScriptTag(source);

        log('Calling remote action ' + source);
      }
    }

    function createScriptTag(source) {
      var s = document.createElement('script');
      s.type = 'text/javascript';
      s.async = 'async';
      s.src = source;

      if (document.getElementsByTagName('head')[0]) {
        document.getElementsByTagName('head')[0].appendChild(s);
      } else {
        document.getElementsByTagName('body')[0].appendChild(s);
      }
    }

    function saveInfo() {
      if (getCookie(COOKIE_URL) == '' || isAffiliateLink()) {
        setCookie(COOKIE_URL, getCurrentUrl());
        setCookie(COOKIE_REFERRER, getCurrentReferrer()); // always update referrer on affiliate visit
      }
      if (getCookie(COOKIE_REFERRER) == '') {
        setCookie(COOKIE_REFERRER, getCurrentReferrer());
      }
      setCookie(COOKIE_TRACKING_ID, _tracking_id);
    }

    function cleanUpHash() {
      if (
        window.location.hash.indexOf('#_a_') == 0 ||
        window.location.hash.indexOf('#_l_') == 0
      ) {
        if (window.location.hash.indexOf('__') > 0) {
          window.location.hash = window.location.hash.substring(
            window.location.hash.indexOf('__') + 2,
            window.location.hash.length
          );
        } else {
          window.location.hash = '';
        }
      }
    }

    function getCurrentUrl() {
      if (_proxy && _proxy.url) {
        return _proxy.url;
      } else {
        return window.location.href;
      }
    }

    function getCurrentReferrer() {
      if (_proxy && _proxy.referrer) {
        return _proxy.referrer;
      } else {
        return document.referrer;
      }
    }

    function getQueryParam(url, name) {
      name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
      var query = url.substr(url.indexOf('?'));
      var hashIndex = query.indexOf('#');
      if (hashIndex > -1) {
        query = query.substr(0, hashIndex);
      }
      var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
        results = regex.exec(query);
      return results == null ? null : decodeURIComponent(results[1].replace(/\+/g, ' '));
    }

    function isNewVisit() {
      var s = getCookie(COOKIE_STATE);
      if (s == 'click') { // support legacy functionality
        setCookie(COOKIE_STATE, new Date(), 300);
        return true;
      } else if (s != '') {
        return false;
      } else {
        var d = new Date();
        setCookie(COOKIE_STATE, d.getTime(), 300);
        return true;
      }
    }

    function getHashTrackingCode(url) {
      if (url.indexOf("#_a_") >= 0 || url.indexOf("#_l_") >= 0) {
        var str = url.substring(url.indexOf("#_") + 2, url.length);
        if (str.indexOf("__") >= 0) {
          return str.substring(0, str.indexOf("__"));
        } else {
          return str;
        }
      }
      return null;
    }

    function isAffiliateLink() {
      var windowAfmc = getHashTrackingCode(getCurrentUrl()) || getQueryParam(getCurrentUrl(), "afmc");
      return windowAfmc != null;
    }

    function generateUUID() {
      var d = new Date().getTime();
      var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
          var r = (d + Math.random()*16)%16 | 0;
          d = Math.floor(d/16);
          return (c=='x' ? r : (r&0x7|0x8)).toString(16);
        });
      return uuid;
    }

    function uploadLead(args) {
      remote("lead", args, function () {
        deleteCookie(COOKIE_LEAD_DATA); // clear data cache on success
        setTimeout(function () { LeadDyno.updateGAInfo(args); }, 300);
      });
    }

    function uploadHash(args) {
      remote("hash", args, function () {
        deleteCookie(COOKIE_HASH_DATA); // clear data cache on success
        setTimeout(function () { LeadDyno.updateGAInfo(args); }, 300);
      });
    }

    function uploadPurchase(args) {
      remote("purchase", args, function () {
        deleteCookie(COOKIE_PURCHASE_DATA); // clear data cache on success
      });
    }

    function uploadCancellation(args) {
      remote("cancel", args, function () {
        deleteCookie(COOKIE_CANCEL_DATA); // clear data cache on success
      });
    }

    function uploadClick(args) {
      args = typeof args !== 'undefined' ? args : {};
      args['page_url'] = getCurrentUrl();
      args['page_referrer'] = getCurrentReferrer();
      remote('clickstream', args);
    }

    function uploadEvent(args) {
      args = typeof args !== 'undefined' ? args : {};
      args['page_url'] = getCurrentUrl();
      args['page_referrer'] = getCurrentReferrer();
      remote('event', args);
    }

    function uploadCachedData() {
      // possibly upload previously captured lead data
      var leadData = getCookie(COOKIE_LEAD_DATA);
      if (leadData) {
        uploadLead(jsonDecode(leadData));
      }

      // possibly upload previously captured hash data
      var hashData = getCookie(COOKIE_HASH_DATA);
      if (hashData) {
        uploadHash(jsonDecode(hashData));
      }

      // possibly upload previously captured purchase data
      var purchaseData = getCookie(COOKIE_PURCHASE_DATA);
      if (purchaseData) {
        uploadPurchase(jsonDecode(purchaseData));
      }

      // possibly upload previously captured cancellation data
      var cancelData = getCookie(COOKIE_CANCEL_DATA);
      if (cancelData) {
        uploadCancellation(jsonDecode(cancelData));
      }
    }

    function cleanupArgs(args, cb) {
      args = typeof args === 'function' ? { onFinish: args } : args;
      args = typeof args !== 'undefined' ? args : {};
      if (cb) {
        args['onFinish'] = cb;
      }
      if (args['key']) {
        LeadDyno.key = args['key'];
      }
      if (args['channel']) {
        LeadDyno.channel = args['channel'];
      }
      return args;
    }

    function drawTagScript() {
      log('drawing tag script');

      var s = _url + 'x?key=' + encode(LeadDyno.key);
      s += '&ld_fp=' + encode(_fingerprint);
      s += '&ld_t=' + encode(_tracking_id);
      s += '&ts=' + new Date().getTime();

      createScriptTag(s);
    }

    function associateShopifyCartIdAndReset() {
      var ldExtId = getCookie(COOKIE_EXT_ID);
      var cartToken = getCookie('cart');
      if (cartToken && ldExtId != cartToken) {
        setCookie(COOKIE_EXT_ID, cartToken);
        deleteCookie(COOKIE_STATE); // make sure visit goes up immediately with ext_id
      }
    }

    // Core singleton
    //noinspection JSUnusedGlobalSymbols
    return {
      // call on every page - tracks visitors
      recordVisit: function (args, cb, forceRemote) {
        if (_ld_iframe_mode) {
          return; // parent frame should establish visitor info
        }
        log('called recordVisit()');
        args = cleanupArgs(args, cb);

        if (window.lead_dyno_affiliate_code) {
          args['code'] = window.lead_dyno_affiliate_code;
        }
        if (window.lead_dyno_affiliate_email) {
          args['affiliate'] = window.lead_dyno_affiliate_email;
        }

        // see if we need to call visit again
        if (isNewVisit() || isAffiliateLink() || forceRemote) {
          saveInfo(); // save request info locally
          remote('visit', args, function () {
            //setTimeout(function(){LeadDyno.updateGAInfo(args);}, 300);
          });
          drawTagScript();
        } else {
          if (args['onFinish']) {
            args['onFinish']({ success: true, local: true });
          }
        }

        uploadCachedData();

        cleanUpHash();
        uploadClick();
      },

      // call on lead form submission
      recordLead: function (email, args, cb) {
        log('called recordLead()');
        args = cleanupArgs(args, cb);
        args.email = email;

        saveInfo(); // save request info locally

        setCookie(COOKIE_LEAD_DATA, jsonEncode(args));
        uploadLead(args);
      },

      recordHash: function (val, args, cb) {
        log('called recordHash()');
        args = cleanupArgs(args, cb);
        if (LeadDyno.active) {
          args.double_check = LeadDyno.key + '-' + val;
        }
        args.val = hasher.hash(LeadDyno.key + '-' + val);

        saveInfo(); // save request info locally

        setCookie(COOKIE_HASH_DATA, jsonEncode(args));
        uploadHash(args);
      },

      // call on purchase, such as going from trial to paid
      recordPurchase: function (email, args, cb) {
        log('called recordPurchase()');
        if (typeof email == 'string') {
          args = cleanupArgs(args, cb);
          args.email = email;
        } else {
          args = cleanupArgs(email, args);
        }
        args.purchase_code = args.purchase_code || _tracking_id;

        saveInfo(); // save request info locally

        setCookie(COOKIE_PURCHASE_DATA, jsonEncode(args));
        uploadPurchase(args);
      },

      // call when cancelling their service
      recordCancellation: function (email, args, cb) {
        log('called recordCancellation()');
        if (typeof email == 'string') {
          args = cleanupArgs(args, cb);
          args.email = email;
        } else {
          args = cleanupArgs(email, args);
        }
        args.purchase_code = args.purchase_code || _tracking_id;

        saveInfo(); // save request info locally

        setCookie(COOKIE_CANCEL_DATA, jsonEncode(args));
        uploadCancellation(args);
      },

      recordExternalTrackingId: function (ext_id) {
        setCookie(COOKIE_EXT_ID, ext_id);
        deleteCookie(COOKIE_STATE); // make sure visit goes up immediately with ext_id
        LeadDyno.recordVisit();
      },

      recordEvent: function (event, args, cb) {
        args = cleanupArgs(args, cb);

        var newArgs = {};
        for (var key in args) {
          if (key != 'onFinish') {
            newArgs['data_' + key] = args[key];
          }
        }
        newArgs['event'] = event;
        if (args['onFinish']) {
          newArgs['onFinish'] = args['onFinish'];
        }

        uploadEvent(newArgs);
      },

      updateGAInfo: function (args) {
        if (getCookie('__utmz') != '' && getCookie(COOKIE_GOOGLE) == '') {
          args = args || {};
          args['__utmz'] = getCookie('__utmz');
          args['__utma'] = getCookie('__utma');
          remote('ga', args, function () {
            setCookie(COOKIE_GOOGLE, '1');
          });
        }
      },

      initPaypal: function () {
        onReady(function () {
          eachNode(document.querySelectorAll('form'), function (f) {
            if (f.action &&
              typeof f.action == 'string' &&
              f.action.indexOf('paypal.com') > 0 &&
              f.action.indexOf('/cgi-bin/webscr') > 0
            ) {
              appendHiddenInput(f, 'custom', _tracking_id);
              appendHiddenInput(f, 'notify_url', _url + 'paypal_ipn?key=' + LeadDyno.key);
            }
          });

          eachNode(document.querySelectorAll('a'), function (anchor) {
            var url = anchor.getAttribute('href');
            if (url &&
              typeof url == 'string' &&
              url.indexOf('paypal.com') > 0 &&
              url.indexOf('/cgi-bin/webscr') > 0
            ) {
              url = url + '&custom=' + _tracking_id + '&notify_url=' + encode(_url + 'paypal_ipn?key=' + LeadDyno.key);
              anchor.setAttribute('href', url);
            }
          });
        });
      },

      initChargebee: function () {
        onReady(function () {
          eachNode(document.querySelectorAll('form'), function (f) {
            if (f.action &&
              typeof f.action == 'string' &&
              f.action.indexOf('chargebee.com') > 0
            ) {
              appendHiddenInput(f, 'affiliate_token', _tracking_id);
            }
          });
          eachNode(document.querySelectorAll('a'), function (anchor) {
            var url = anchor.getAttribute('href');
            if (url &&
              typeof url == 'string' &&
              url.indexOf('chargebee.com') > 0
            ) {
              var leadCharacter = url.indexOf('?') > 0 ? '&' : '?';
              url = url + leadCharacter + 'affiliate_token=' + _tracking_id;
              anchor.setAttribute('href', url);
            }
          });
        });
      },

      initShopify: function () {
        associateShopifyCartIdAndReset(); // always force a cart id association

        // If Shopify checkout object presence on page means that checkout process complete.
        // If so, associate Visitor with Lead by email.
        if (typeof Shopify !== 'undefined' &&
          typeof Shopify.checkout !== 'undefined'
        ) {
          LeadDyno.recordVisit(
            {
              email: Shopify.checkout.email,
              purchase_code: Shopify.checkout.order_id,
            },
            null,
            true
          );
        }

        // if no cart token exists initially, check if one comes into existence every 500 milliseconds
        var cartToken = getCookie('cart');
        if (cartToken == null || cartToken == '') {
          var interval = setInterval(function () {
            cartToken = getCookie('cart');
            if (cartToken != null && cartToken != '') {
              associateShopifyCartIdAndReset();
              LeadDyno.recordVisit();
              clearInterval(interval);
            }
          }, 500);
        }

        if (!_ld_ready) {
          // handle race condition with async loader
          domReady(function () {
            documentReadyHandler();
          });
        }
      },

      initWix: function () {
        window.wixDevelopersAnalytics.register(
          '76d77afc-0aa3-4b28-9ec1-2338fc9d22bf',
          function report(eventName, data) {
            if (eventName == 'Purchase') {
              // handle order first
              var orderId = data.orderId;
              var args = { orderId: orderId };
              remote('wix_purchase', args, function () {});

              // handle visitor first
              var ldExtId = getCookie(COOKIE_EXT_ID);
              if (ldExtId != orderId) {
                setCookie(COOKIE_EXT_ID, orderId);
                deleteCookie(COOKIE_STATE); // make sure visit goes up immediately with ext_id
              }
              LeadDyno.recordVisit();
            }
          }
        );
      },

      initEcwid: function () {
        if (window.Ecwid && Ecwid.OnOrderPlaced) {
          Ecwid.OnOrderPlaced.add(function (order) {
            var ldExtId = getCookie(COOKIE_EXT_ID);
            var orderNum = order.orderNumber;
            if (orderNum && ldExtId != orderNum) {
              setCookie(COOKIE_EXT_ID, orderNum);
              deleteCookie(COOKIE_STATE); // make sure visit goes up immediately with ext_id
            }
            LeadDyno.recordVisit();
          });
        }
      },

      autoWatch: function (callback) {
        onReady(function () {
          remote('/elc', {});

          function normalizeFormSelectors(formId) {
            if (!formId) {
              return [];
            }
            var selectors = [];
            var list = Array.isArray(formId) ? formId : [formId];
            eachNode(list, function (item) {
              if (typeof item !== 'string') {
                return;
              }
              var selector = item.trim();
              if (!selector) {
                return;
              }
              if (
                selector.charAt(0) !== '#' &&
                selector.charAt(0) !== '.' &&
                selector.indexOf(' ') === -1 &&
                selector.indexOf('[') === -1 &&
                selector.indexOf(':') === -1 &&
                selector.indexOf(',') === -1
              ) {
                selector = '#' + selector;
              }
              selectors.push(selector);
            });
            return selectors;
          }

          function addUniqueNode(list, node) {
            if (list.indexOf(node) === -1) {
              list.push(node);
            }
          }

          function isSubmitTrigger(target, container) {
            var el = target;
            while (el) {
              if (el.tagName) {
                var tag = el.tagName.toLowerCase();
                if (tag === 'button') {
                  return true;
                }
                if (tag === 'input') {
                  var type = (el.getAttribute('type') || '').toLowerCase();
                  if (type === 'submit' || type === 'button' || type === 'image') {
                    return true;
                  }
                }
              }
              if (el === container) {
                break;
              }
              el = el.parentNode;
            }
            return false;
          }

          function handleLeadCapture(container) {
            try {
              var firstNameInput = container.querySelector(
                'input[id*="first_name"], input[id*="firstname"]'
              );
              var lastNameInput = container.querySelector(
                'input[id*="last_name"], input[id*="lastname"]'
              );
              var firstName = firstNameInput ? firstNameInput.value : '';
              var lastName = lastNameInput ? lastNameInput.value : '';
              eachNode(
                container.querySelectorAll(
                  'input[type="email"], input[type="text"]'
                ),
                function (input) {
                  var val = input.value;
                  if (isEmail(val)) {
                    LeadDyno.recordLead(
                      val,
                      {
                        p: 'true',
                        first_name: firstName,
                        last_name: lastName,
                      },
                      callback
                    );
                  }
                }
              );
            } catch (e) {
              console.log(e);
            }
          }

          var watchTargets = [];
          eachNode(document.querySelectorAll('form'), function (form) {
            addUniqueNode(watchTargets, form);
          });

          eachNode(normalizeFormSelectors(LeadDyno.formId), function (selector) {
            eachNode(document.querySelectorAll(selector), function (node) {
              addUniqueNode(watchTargets, node);
            });
          });

          eachNode(watchTargets, function (container) {
            if (
              container.tagName &&
              container.tagName.toLowerCase &&
              container.tagName.toLowerCase() === 'form'
            ) {
              container.addEventListener('submit', function () {
                handleLeadCapture(container);
              });
              return;
            }

            container.addEventListener('click', function (event) {
              if (isSubmitTrigger(event.target, container)) {
                handleLeadCapture(container);
              }
            });
          });
        });
      },

      watch: function (selector, callback) {
        onReady(function () {
          delegateEvent(document, 'change', selector, function (target) {
            var val = target.value;
            if (isEmail(val)) {
              LeadDyno.recordLead(val, {}, callback);
            }
          });
        });
      },

      hash: function (selector, callback) {
        onReady(function () {
          delegateEvent(document, 'change', selector, function (target) {
            var val = target.value;
            if (/\S+@\S+\.\S+/.test(val)) {
              val = val.toLowerCase();
              setTimeout(function () {
                LeadDyno.recordHash(
                  val,
                  { check: LeadDyno.active ? val : '' },
                  callback
                );
              }, 500 * Math.random());
            }
          });
        });
      },

      ready: function (handler) {
        onReady(handler);
      },

      isReady: function () {
        return _ld_ready;
      },

      noop: function () {},

      devTools: {
        reset: function () {
          deleteCookie(COOKIE_URL);
          deleteCookie(COOKIE_REFERRER);
          deleteCookie(COOKIE_TRACKING_ID);
          deleteCookie(COOKIE_EXT_ID);
          deleteCookie(COOKIE_LEAD_DATA);
          deleteCookie(COOKIE_PURCHASE_DATA);
          deleteCookie(COOKIE_CANCEL_DATA);
          deleteCookie(COOKIE_STATE);
          deleteCookie(COOKIE_GOOGLE);
          _tracking_id = null;
          _init();
        },

        ping: function () {},

        uuid: function () {
          return generateUUID();
        },

        setLogger: function (logger) {
          _logger = logger;
        },

        getState: function () {
          return {
            COOKIE_URL: getCookie(COOKIE_URL),
            COOKIE_REFERRER: getCookie(COOKIE_REFERRER),
            COOKIE_TRACKING_ID: getCookie(COOKIE_TRACKING_ID),
            COOKIE_LEAD_DATA: getCookie(COOKIE_LEAD_DATA),
            COOKIE_PURCHASE_DATA: getCookie(COOKIE_PURCHASE_DATA),
            COOKIE_CANCEL_DATA: getCookie(COOKIE_CANCEL_DATA),
            COOKIE_STATE: getCookie(COOKIE_STATE),
            COOKIE_GOOGLE: getCookie(COOKIE_GOOGLE),
          };
        },

        setBrowserProxy: function (proxy) {
          _proxy = proxy;
        },

        setServerUrl: function (url) {
          _url = url;
          if (_url.charAt(_url.length - 1) != '/') {
            _url += '/';
          }
        },

        getTrackingId: function () {
          return _tracking_id;
        },

        getFingerprint: function () {
          return _fingerprint;
        },

        getJQuery: function () {
          return null;
        },

        getAffiliateCode: function () {
          return getHashTrackingCode(getCookie(COOKIE_URL));
        },

        isMobile: function () {
          return _is_mobile;
        },

        parseTrackingCode: function (url) {
          return getHashTrackingCode(url);
        },

        cleanUpHash: function () {
          cleanUpHash();
        },

        forceNewVisit: function () {
          deleteCookie(COOKIE_STATE);
        },

        getQueryParam: getQueryParam,
        log: log,
        jsonEncode: jsonEncode,
        jsonDecode: jsonDecode,
        hash: hasher.hash,
      },

      // ** DEPRECATED METHODS **

      // Ping the server to register a click
      click: function (args) {
        log('called click()');
        this.recordVisit(args);
      },

      // Register the user
      register: function (args) {
        log('called register()');
        args = typeof args !== 'undefined' ? args : {};
        this.recordLead(args.email, args);
      },

      capture: function (email, args) {
        log('called capture()');
        this.recordLead(email, args);
      },

      visit: function (args) {
        log('Called visit');
        args = typeof args !== 'undefined' ? args : {};
        this.recordVisit(args);
        if (args.email) {
          this.recordLead(args.email, args);
        }
      },
    };

    function domReady(handler) {
      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', handler);
      } else {
        setTimeout(handler, 0);
      }
    }

    function eachNode(list, handler) {
      if (!list) {
        return;
      }
      for (var i = 0; i < list.length; i++) {
        handler(list[i], i);
      }
    }

    function setInputValue(selector, value) {
      eachNode(document.querySelectorAll(selector), function (node) {
        if ("value" in node) {
          node.value = value;
        }
      });
    }

    function getMetaContent(name) {
      var selector = 'meta[name="' + name + '"], meta[property="' + name + '"]';
      var meta = document.querySelector(selector);
      return meta ? meta.getAttribute('content') : null;
    }

    function appendHiddenInput(form, name, value) {
      var input = document.createElement('input');
      input.type = 'hidden';
      input.name = name;
      input.value = value;
      form.appendChild(input);
    }

    function delegateEvent(root, eventName, selector, handler) {
      root.addEventListener(eventName, function (event) {
        var target = event.target;
        if (target && target.matches(selector)) {
          handler(target, event);
        }
      });
    }
})();
