Advertisement
AndrewHaxalot

pa.js

Dec 19th, 2013
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. (function () {
  2.   "use strict";
  3.   // namespaces
  4.   var PAYPAL = window.PAYPAL || {};
  5.   window.PAYPAL = PAYPAL;
  6.   PAYPAL.analytics = {};
  7.   window.fpti = window.fpti || {};
  8.   window.fptiserverurl = window.fptiserverurl || '//t.paypal.com/ts';
  9.  
  10.    // constructor
  11.    PAYPAL.analytics.Analytics = function (options) {
  12.                 this._init(options);
  13.    };
  14.  
  15.    PAYPAL.analytics.Analytics.prototype = {
  16.  
  17.                 // analytics version
  18.                 version: '0.1',
  19.  
  20.                 // options
  21.                 options: {
  22.                        click: {
  23.                              elements: '*[data-pp-click]',
  24.                              onClick: function () {},
  25.                              request: {
  26.                                     keys: {
  27.                                            linkUrl: 'lu'
  28.                                     },
  29.                                     values: {
  30.                                            eventType: 'cl'
  31.                                     }
  32.                              }
  33.                        },
  34.                        formAbandonment: {
  35.                              elements: 'form',
  36.                              request: {
  37.                                     keys: {
  38.                                            lastFormFocused: 'lf',
  39.                                            lastInputFocused: 'li'
  40.                                     },
  41.                                     values: {
  42.                                            eventType: 'fa'
  43.                                     }
  44.                              }
  45.                        },
  46.                        impression: {
  47.                              request: {
  48.                                     keys: {
  49.                                            cookiesEnabled: 'ce',
  50.                                            plugins: 'pl',
  51.                                            jsVersion: 'jsv',
  52.                                            pageTitle: 'pt',
  53.                                            referrer: 'ru',
  54.                                            screenColorDepth: 'cd',
  55.                                            screenWidth: 'sw',
  56.                                            screenHeight: 'sh',
  57.                                            browserWidth: 'bw',
  58.                                            browserHeight: 'bh'
  59.                                     },
  60.                                     values: {
  61.                                            eventType: 'im'
  62.                                     }
  63.                              }
  64.                        },
  65.                        request: {
  66.                              data: {}, // custom data
  67.                              unloadDelay: false, // set this to an integer in ms to delay unload to beat race condition
  68.                              keys: { // these default params will be prefixed with the prefix
  69.                                     version: 'v',
  70.                                     timestamp: 't',
  71.                                     gmtOffset: 'g',
  72.                                     eventType: 'e'
  73.                              },
  74.                              values: {
  75.                                     eventType: 'na',
  76.                                     'true': 1,
  77.                                     'false': 0
  78.                              },
  79.                              keyPrefix: '', // the prefix to be used for system variable keys in the query string
  80.                              url: '//t.paypal.com/ts',
  81.                              onBeaconCreate: function () {}
  82.                        }
  83.                 },
  84.                 _delayUnloadUntil: null, // the browser will not unload until this timestamp is met
  85.                 _lastFormFocused: null, // the last form that was focused on
  86.                 _lastInputFocused: null, // the last input that was focused on
  87.  
  88.                 // constructor
  89.                 _init: function (options) {
  90.                        // set options
  91.                        this.setOptions(options);
  92.  
  93.                        // add unload delay listener
  94.                        this._enableUnloadDelay();
  95.                 },
  96.  
  97.                 // merges two options objects
  98.                 _mergeOptions: function (options1, options2) {
  99.                        // convert data query string to object if needed
  100.                        if (options1 && options1.data && typeof options1.data === 'string') {
  101.                              options1.data = this.utils.queryStringToObject(options1.data);
  102.                        }
  103.                        if (options2 && options2.data && typeof options2.data === 'string') {
  104.                              options2.data = this.utils.queryStringToObject(options2.data);
  105.                        }
  106.  
  107.                        // merge one time options
  108.                        var options = this.utils.merge(options1, options2);
  109.  
  110.                        return options;
  111.                 },
  112.  
  113.                 _enableUnloadDelay: function () {
  114.                        var self = this,
  115.                              delay = function () {
  116.                              var now;
  117.  
  118.                              // lock browser until delay is met
  119.                              if (self._delayUnloadUntil) {
  120.                                     do {
  121.                                            now = new Date();
  122.                                     } while (now.getTime() < self._delayUnloadUntil);
  123.                              }
  124.                        };
  125.                        this.utils.removeListener(window, 'beforeunload', delay);
  126.                        this.utils.addListener(window, 'beforeunload', delay);
  127.                 },
  128.  
  129.                 // fires a tracking event with the prefix prepended to all event datas
  130.                 // note: this is only used for predefined events like impression, click, etc
  131.                 _recordEvent: function (eventData, options) {
  132.  
  133.  
  134.  
  135.                        var key;
  136.                                              options = this._mergeOptions(this.options.request, options);
  137.                        // ensure data is set
  138.                        options.data = options.data || {};
  139.  
  140.  
  141.  
  142.                        // prepend prefix to all event data keys and add event data to options.data
  143.                        for (key in eventData) {
  144.                              options.data[options.keyPrefix + key] = eventData[key];
  145.                        }
  146.  
  147.                        // record this event
  148.                        this.record(options);
  149.                 },
  150.  
  151.                 // makes tracking record request
  152.                 _request: function (options) {
  153.                        this._createBeacon(options);
  154.                 },
  155.  
  156.                 // creates an image tag for sending a tracking request to the server
  157.                 _createBeacon: function (options) {
  158.  
  159.                var self = this,
  160.                              beacon = new Image();
  161.                        if (options.onBeaconCreate(beacon) !== false) {
  162.                              beacon.src = this._generateBeaconUrl(options);
  163.  
  164.                              // unload delay (browser lock)
  165.                              if (options.unloadDelay) {
  166.                                     this.setUnloadDelay(options.unloadDelay);
  167.                              }
  168.                        }
  169.                 },
  170.  
  171.                 // generates a beacon url given request options
  172.                 _generateBeaconUrl: function (options) {
  173.                        var parts = options.url.split('?'),
  174.                              // remove existing query string
  175.                              url = parts[0],
  176.                              optKeys = options.keys,    //caching the keys
  177.                              key, perfData,
  178.                              rLogId, tempFpti=window.fpti.constructor();
  179.  
  180.              var tempFpti = window.fpti.constructor();
  181.  
  182.              // deep clone
  183.              for (key in window.fpti) {
  184.               tempFpti[key] = window.fpti[key];
  185.                        }
  186.  
  187.                        // if url begins with '//' then we automatically add the current protocol
  188.  
  189.  
  190.                        if (url.match(/^\/\//)) {
  191.                              url = window.location.protocol + url;
  192.                        }
  193.  
  194.                        // add query string delimiter
  195.                        url += '?';
  196.  
  197.                        // reappend existing query string
  198.                        if (parts[1]) {
  199.                              url += parts[1] + '&';
  200.                        }
  201.  
  202.                        // append version
  203.                        url += options.keyPrefix + optKeys.version + '=' + encodeURIComponent(this.version);
  204.  
  205.                        // append timestamp
  206.                        if (optKeys.timestamp) {
  207.                              url = this._appendQueryStringData(url, options.keyPrefix + optKeys.timestamp, this._getTimestamp());
  208.                        }
  209.  
  210.                        // append gmt offset
  211.                        if (optKeys.gmtOffset) {
  212.                              url = this._appendQueryStringData(url, options.keyPrefix + optKeys.gmtOffset, this._getGmtOffset());
  213.                        }
  214.  
  215.                        // append type
  216.                        if (optKeys.eventType) {
  217.                              url = this._appendQueryStringData(url, options.keyPrefix + optKeys.eventType, options.values.eventType);
  218.                        }
  219.                        // append the data in options data
  220.  
  221.                       for (var opt in options.data) {
  222.  
  223.                  if(opt == 'opic'){
  224.                    var opic = tempFpti[opt] || options.data[opt];
  225.                    opic = opic.replace(/\w+\@\w+\.\w+/g,'');
  226.                    opic = opic.replace(/\d+/g,'');
  227.                    tempFpti[opt] = opic;
  228.                  }
  229.                  else {
  230.                  tempFpti[opt]= tempFpti[opt] || options.data[opt];
  231.                  }
  232.                        }
  233.  
  234.                        for (key in tempFpti) {
  235.             if(tempFpti[key] !== " ") {
  236.                              url = this._appendQueryStringData(url, key, tempFpti[key]);
  237.                         }
  238.             }
  239.  
  240.                        perfData = this._buildPerformanceData(options.data);
  241.                        for (key in perfData) {
  242.                              url = this._appendQueryStringData(url, key, perfData[key]);
  243.                }
  244.                        rLogId = this._getRLogId();
  245.                        if (rLogId) {
  246.                              url = this._appendQueryStringData(url, "teal", rLogId);
  247.                        }
  248.  
  249.                        return url;
  250.                 },
  251.  
  252.                 // adds a new piece of data to the query string of a url
  253.                 _appendQueryStringData: function (url, key, value) {
  254.                        if ((key || key === 0) && (value || value === 0)) {
  255.                              url += '&' + key + '=' + encodeURIComponent(value);
  256.                        }
  257.                        return url;
  258.                 },
  259.  
  260.                 // gets current timestamp in epoch seconds
  261.                 _getTimestamp: function () {
  262.                        return new Date().getTime();
  263.                 },
  264.  
  265.                 // build the Performance Timing related data
  266.                 _buildPerformanceData: function (data) {
  267.                        var perf = {},
  268.                              pgst = data.pgst || 0;
  269.                        if (!PAYPAL.analytics.perf) {
  270.                              if (window.performance) {
  271.                                     var timing = window.performance.timing,
  272.                                            secureConTime = timing.secureConnectionStart || timing.connectEnd,
  273.                                            loadEventEnd = timing.loadEventEnd || timing.loadEventStart;
  274.  
  275.                                     perf.t1 = this._getPerformanceData( timing.requestStart, timing.fetchStart);  //t1 - DNS+Connection+SSL (t1c + t1d + t1s));
  276.                                     perf.t1c = this._getPerformanceData( timing.connectEnd, timing.fetchStart);  //t1c - Network latency or Connection timing
  277.                                      perf.t1d = this._getPerformanceData( timing.domainLookupEnd, timing.domainLookupStart); //t1d - DNS resolution timing
  278.                                     perf.t1s = this._getPerformanceData( timing.connectEnd, secureConTime);                           //t1s - SSL timing
  279.                                     perf.t2 = this._getPerformanceData( timing.responseStart, timing.requestStart);      //t2 - Server time
  280.                                     perf.t3 = this._getPerformanceData( timing.responseEnd, timing.responseStart);            //t3- Html/Content download time
  281.                                     perf.t4d = this._getPerformanceData( timing.domComplete, timing.domLoading);        //t4d - DOM Timing
  282.                                     perf.t4 = this._getPerformanceData( loadEventEnd, timing.domLoading);                     // t4- Browser process time (t4d + t4e)
  283.                                     perf.t4e = this._getPerformanceData( loadEventEnd, timing.loadEventStart);           //t4e - Event Binding timing
  284.                                     perf.tt = this._getPerformanceData( loadEventEnd, timing.navigationStart);   //tt - The whole process of navigation and page load
  285.                              } else if (pgst) { //Server sets the pgst value (page Load Start Time)
  286.                                     perf.told = new Date().getTime() - pgst; //(t old) t2+t3+t4
  287.                              }
  288.                              PAYPAL.analytics.perf = perf;
  289.                        }
  290.                        return perf;
  291.                 },
  292.  
  293.                 _getPerformanceData: function (end, start){
  294.                        var diff = 0;
  295.                        var max = 600000; //10 minutes
  296.                        if(this._validateNumber(start) && this._validateNumber(end)){//Both start & end has to be a valid number
  297.                              if(end >= start){//End should be greater than 0 & bigger or equal to start
  298.                                     diff = end - start;
  299.                                     if(diff > max){//End should not be bigger than max
  300.                                            diff = 0;
  301.                                     }
  302.                              }
  303.                        }
  304.                        return diff;
  305.                 },
  306.  
  307.                 //Check for valid number
  308.                 _validateNumber: function (value) {
  309.                        if(!value){
  310.                              return false;
  311.                        }
  312.                  if (value.length == 0) {
  313.                      return false;
  314.                  }
  315.                  var intValue = parseInt(value);
  316.                  if (intValue == Number.NaN) {
  317.                      return false;
  318.                  }
  319.                  if (intValue < 0) {
  320.                      return false;
  321.                  }
  322.                  return true;
  323.              },
  324.  
  325.                 //Captures the rLogId from the page
  326.                 _getRLogId: function () {
  327.                        var rLogId,
  328.                              commentNode,
  329.                              commentData;
  330.                        if (window.rLogId) {
  331.                              rLogId = window.rLogId;
  332.                        } else {
  333.                              try {
  334.                                     commentNode = [].slice.call(document.head.childNodes).filter(function (node) {
  335.                                            return node.nodeType === 8;
  336.                                     })[0];
  337.                                     commentData = commentNode.data;
  338.                                     if (commentData && commentData.length > 0) {
  339.                                            commentData = commentData.trim().split(" : ");
  340.                                            if (commentData.length > 2) {
  341.                                                   rLogId = commentData[commentData.length - 1];
  342.                                            }
  343.                                     }
  344.                              } catch (e) {}
  345.                        }
  346.                        return rLogId;
  347.                 },
  348.  
  349.                 // gets current gmt offset in minutes
  350.                 _getGmtOffset: function () {
  351.                        return new Date().getTimezoneOffset();
  352.                 },
  353.  
  354.                 // get page title
  355.                 _getPageTitle: function () {
  356.                        return document.title;
  357.                 },
  358.  
  359.                 // get referrer
  360.                 _getReferrer: function () {
  361.                        return document.referrer;
  362.                 },
  363.  
  364.                 // gets screen color depth
  365.                 _getScreenColorDepth: function () {
  366.                        return window.screen.colorDepth;
  367.                 },
  368.  
  369.                 // gets screen dimensions
  370.                 _getScreenDimensions: function () {
  371.                        return {
  372.                              width: window.screen.width,
  373.                              height: window.screen.height
  374.                        };
  375.                 },
  376.  
  377.                 // gets browser dimensions
  378.                 _getBrowserDimensions: function () {
  379.                        // pulled from http://andylangton.co.uk/articles/javascript/get-viewport-size-javascript/
  380.                        var browserWidth,
  381.                              browserHeight,
  382.                              element = document.documentElement;
  383.  
  384.                        // the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight
  385.                        if (window.innerWidth !== 'undefined' && window.innerWidth) {
  386.                              browserWidth = window.innerWidth;
  387.                              browserHeight = window.innerHeight;
  388.                        }
  389.                        // IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
  390.                        else if (element !== 'undefined' && element.clientWidth !== 'undefined' && element.clientWidth !== 0) {
  391.                              browserWidth = element.clientWidth;
  392.                              browserHeight = element.clientHeight;
  393.                        }
  394.                        // older versions of IE
  395.                        else {
  396.                              element = document.getElementsByTagName('body')[0];
  397.                              browserWidth = element.clientWidth;
  398.                              browserHeight = element.clientHeight;
  399.                        }
  400.  
  401.                        return {
  402.                              width: browserWidth,
  403.                              height: browserHeight
  404.                        };
  405.                 },
  406.  
  407.                 // gets boolean for java enabled
  408.                 _getJavaEnabled: function (options) {
  409.                        options = this._mergeOptions(this.options.request, options);
  410.                        return (navigator.javaEnabled()) ? options.values['true'] : options.values['false'];
  411.                 },
  412.  
  413.                 // gets boolean for cookies enabled
  414.                 _getCookiesEnabled: function (options) {
  415.                        options = this._mergeOptions(this.options.request, options);
  416.  
  417.                        var enabled = 0;
  418.                        if (navigator.cookieEnabled === 'undefined') {
  419.                              document.cookie = 'enabledCheck';
  420.                              enabled = (document.cookie.indexOf('enabledCheck') !== -1) ? options.values['true'] : options.values['false'];
  421.                        } else {
  422.                              enabled = (navigator.cookieEnabled) ? options.values['true'] : options.values['false'];
  423.                        }
  424.                        return enabled;
  425.                 },
  426.  
  427.                 // gets flash version information for metadata
  428.                 _getFlashVersion: function () {
  429.                        var version = null;
  430.  
  431.                        // pulled from http://www.featureblend.com/flash_detect_1-0-4/flash_detect.js
  432.                        if (navigator.plugins && navigator.plugins.length > 0) {
  433.                              var type = 'application/x-shockwave-flash',
  434.                                     mimeTypes = navigator.mimeTypes;
  435.                              if (mimeTypes && mimeTypes[type] && mimeTypes[type].enabledPlugin && mimeTypes[type].enabledPlugin.description) {
  436.                                     version = mimeTypes[type].enabledPlugin.description;
  437.                              }
  438.                        }
  439.  
  440.                        return version;
  441.                 },
  442.  
  443.                 // get plugin data to check for
  444.                 _getPluginData: function () {
  445.                        return {
  446.                              director: 'application/x-director',
  447.                              mediaplayer: 'application/x-mplayer2',
  448.                              pdf: 'application/pdf',
  449.                              quicktime: 'video/quicktime',
  450.                              real: 'audio/x-pn-realaudio-plugin',
  451.                              silverlight: 'application/x-silverlight'
  452.                        };
  453.                 },
  454.  
  455.                 //gets the IE plugin data
  456.                 _getPluginDataIE: function () {
  457.                        var names = [ "abk", "wnt", "aol", "arb", "chs", "cht", "dht",
  458.                                            "dhj", "dan", "dsh", "heb", "ie5", "icw", "ibe",
  459.                                            "iec", "ieh", "iee", "jap", "krn", "lan", "swf",
  460.                                            "shw", "msn", "wmp", "obp", "oex", "net", "pan",
  461.                                            "thi", "tks", "uni", "vtc", "vnm", "mvm", "vbs",
  462.                                            "wfd" ],
  463.                              components = [ "7790769C-0471-11D2-AF11-00C04FA35D02",
  464.                                            "89820200-ECBD-11CF-8B85-00AA005B4340",
  465.                                            "47F67D00-9E55-11D1-BAEF-00C04FC2D130",
  466.                                            "76C19B38-F0C8-11CF-87CC-0020AFEECF20",
  467.                                            "76C19B34-F0C8-11CF-87CC-0020AFEECF20",
  468.                                            "76C19B33-F0C8-11CF-87CC-0020AFEECF20",
  469.                                            "9381D8F2-0288-11D0-9501-00AA00B911A5",
  470.                                            "4F216970-C90C-11D1-B5C7-0000F8051515",
  471.                                            "283807B5-2C60-11D0-A31D-00AA00B92C03",
  472.                                            "44BBA848-CC51-11CF-AAFA-00AA00B6015C",
  473.                                            "76C19B36-F0C8-11CF-87CC-0020AFEECF20",
  474.                                            "89820200-ECBD-11CF-8B85-00AA005B4383",
  475.                                            "5A8D6EE0-3E18-11D0-821E-444553540000",
  476.                                            "630B1DA0-B465-11D1-9948-00C04F98BBC9",
  477.                                            "08B0E5C0-4FCB-11CF-AAA5-00401C608555",
  478.                                            "45EA75A0-A269-11D1-B5BF-0000F8051515",
  479.                                            "DE5AED00-A4BF-11D1-9948-00C04F98BBC9",
  480.                                            "76C19B30-F0C8-11CF-87CC-0020AFEECF20",
  481.                                            "76C19B31-F0C8-11CF-87CC-0020AFEECF20",
  482.                                            "76C19B50-F0C8-11CF-87CC-0020AFEECF20",
  483.                                            "D27CDB6E-AE6D-11CF-96B8-444553540000",
  484.                                            "2A202491-F00D-11CF-87CC-0020AFEECF20",
  485.                                            "5945C046-LE7D-LLDL-BC44-00C04FD912BE",
  486.                                            "22D6F312-B0F6-11D0-94AB-0080C74C7E95",
  487.                                            "3AF36230-A269-11D1-B5BF-0000F8051515",
  488.                                            "44BBA840-CC51-11CF-AAFA-00AA00B6015C",
  489.                                            "44BBA842-CC51-11CF-AAFA-00AA00B6015B",
  490.                                            "76C19B32-F0C8-11CF-87CC-0020AFEECF20",
  491.                                            "76C19B35-F0C8-11CF-87CC-0020AFEECF20",
  492.                                            "CC2A9BA0-3BDD-11D0-821E-444553540000",
  493.                                            "3BF42070-B3B1-11D1-B5C5-0000F8051515",
  494.                                            "10072CEC-8CC1-11D1-986E-00A0C955B42F",
  495.                                            "76C19B37-F0C8-11CF-87CC-0020AFEECF20",
  496.                                            "08B0E5C0-4FCB-11CF-AAA5-00401C608500",
  497.                                            "4F645220-306D-11D2-995D-00C04F98BBC9",
  498.                                            "73FA19D0-2D75-11D2-995D-00C04F98BBC9" ],
  499.                              plugins = {},
  500.                              body = document.body,
  501.                              i, ver;
  502.                        body.addBehavior("#default#clientCaps");
  503.  
  504.                        for (i = 0; i < components.length; i++) {
  505.                              ver = body.getComponentVersion("{" + components[i] + "}", "componentid");
  506.                              var name = names[i];
  507.                              if (ver) {
  508.                                     plugins[name] = ver;
  509.                              }
  510.                        }
  511.                        return plugins;
  512.                 },
  513.  
  514.                 // get list of plugins
  515.                 _getPlugins: function () {
  516.                        var pluginArray = [],
  517.                              name, flash,
  518.                              plugins = this._getPluginData();
  519.  
  520.                        // Except IE browser supports navigator.plugins
  521.                        // go through each plugin and check to see if it exists
  522.                        for (name in plugins) {
  523.                              if (this._detectPlugin(plugins[name])) {
  524.                                     pluginArray.push(name);
  525.                              }
  526.                        }
  527.  
  528.                        // check for flash
  529.                        flash = this._getFlashVersion();
  530.                        if (flash) {
  531.                              pluginArray.push(flash);
  532.                        }
  533.  
  534.                        if (pluginArray.length === 0 && (navigator.appName === 'Microsoft Internet Explorer')) {
  535.                              plugins = this._getPluginDataIE();
  536.  
  537.                              for (name in plugins) {
  538.                                     pluginArray.push(name);
  539.                              }
  540.                        }
  541.  
  542.                        // return a comma delimited string of plugin names
  543.                        return pluginArray.join(',');
  544.                 },
  545.  
  546.                 // detect plugin by mime type
  547.                 _detectPlugin: function (type) {
  548.                        var mimeTypes = navigator.mimeTypes,
  549.                              plugin;
  550.                        if (mimeTypes && mimeTypes.length) {
  551.                              plugin = mimeTypes[type];
  552.                              return (plugin && plugin.enabledPlugin);
  553.                        }
  554.                 },
  555.  
  556.                 // gets the value to send for the last form that was focused on
  557.                 _getLastFormFocusedValue: function () {
  558.                        var value = '';
  559.                        if (this._lastFormFocused) {
  560.                         value = this._lastFormFocused.getAttribute('name') || this._lastFormFocused.getAttribute('id') || '';
  561.            }
  562.                        return value;
  563.                 },
  564.  
  565.                 // gets the value to send for the last form that was focused on
  566.                 _getLastInputFocusedValue: function () {
  567.                        var value = '';
  568.                        if (this._lastInputFocused) {
  569.                         value = this._lastInputFocused.getAttribute('name') || this._lastInputFocused.getAttribute('id') || '';
  570.            }
  571.                        return value;
  572.                 },
  573.  
  574.                 // gets elements fom various styles of argument input (css string, elements array, element)
  575.                 _getElements: function (arg) {
  576.                        var elements = [], i;
  577.                        // get elements to be tracked from options
  578.                        if (arg) {
  579.                              // if they gave us a string we will search the dom
  580.                              if (typeof arg === 'string') {
  581.                                     elements = this.utils.getElements(arg);
  582.                              }
  583.  
  584.                              // if they gave us an array of elements just copy over the items that actually are elements
  585.                              else if (typeof arg === 'object') {
  586.                                     for (i in arg) {
  587.                                            if (arg.hasOwnProperty(i) && arg[i].nodeType === 1) {
  588.                                                   elements.push(arg[i]);
  589.                                            }
  590.                                     }
  591.                              }
  592.                              // if they gave us one element then push it to the elements array
  593.                              else if (arg.nodeType === 1) {
  594.                                     elements.push(arg);
  595.                              }
  596.                        }
  597.                        return elements;
  598.                 },
  599.  
  600.                 // clicks the given element and changes page if it's a link
  601.                 _click: function (element) {
  602.                        if (element.getAttribute('href')) {
  603.                              window.location.href = element.getAttribute('href');
  604.                        }
  605.                 },
  606.  
  607.                 // sets options
  608.                 setOptions: function (options) {
  609.             this.options = this._mergeOptions(this.options, options);
  610.                 },
  611.  
  612.                 // sets custom request data
  613.                 setRequestData: function (key, value) {
  614.                        if (typeof key === 'object') {
  615.                              var i;
  616.                              // user is setting multiple key/value pairs
  617.                              for (i in key) {
  618.                                     this.options.request.data[i] = key[i];
  619.                              }
  620.                        } else if (typeof key === 'string' && value === undefined) {
  621.                              // user is setting key/value pairs using a query string
  622.                              this.setRequestData(this.utils.queryStringToObject(key));
  623.                        } else if (typeof key === 'string' && value !== undefined) {
  624.                              // user is only setting one key/value pair
  625.                              this.options.request.data[key] = value;
  626.                        }
  627.                 },
  628.  
  629.                 // gets custom request data
  630.                 getRequestData: function (key) {
  631.                        var data = this.options.request.data;
  632.                        if (key) {
  633.                              data = data[key];
  634.                        }
  635.                        return data;
  636.                 },
  637.  
  638.                 // removes custom request data by key
  639.                 removeRequestData: function (key) {
  640.                        // if a key is provided, remove just that piece of data, otherwise remove it all
  641.                        var data = this.options.request.data;
  642.                        if (data[key]) {
  643.                              delete data[key];
  644.                        } else if (!key) {
  645.                              data = {};
  646.                        }
  647.                 },
  648.  
  649.                 // sets browser lock/delay to defeat race condition
  650.                 setUnloadDelay: function (delay) {
  651.                        this._delayUnloadUntil = new Date().getTime() + delay;
  652.                 },
  653.  
  654.                 // record impression event
  655.                 recordImpression: function (options) {
  656.                        // get keys from options
  657.                        var eventData = {},
  658.                              optKeys,      //caching options keys
  659.                              screenDimensions,
  660.                              browserDimensions;
  661.  
  662.                        options = this._mergeOptions(this.options.impression.request, options);
  663.                        optKeys = options.keys;
  664.  
  665.                        // page title
  666.                        eventData[optKeys.pageTitle] = this._getPageTitle();
  667.  
  668.                        // referrer url
  669.                        eventData[optKeys.referrer] = this._getReferrer();
  670.  
  671.                        // screen color depth
  672.                        eventData[optKeys.screenColorDepth] = this._getScreenColorDepth();
  673.  
  674.                        // screen dimensions
  675.                        screenDimensions = this._getScreenDimensions();
  676.                        eventData[optKeys.screenWidth] = screenDimensions.width;
  677.                        eventData[optKeys.screenHeight] = screenDimensions.height;
  678.  
  679.                        // browser dimensions
  680.                        browserDimensions = this._getBrowserDimensions();
  681.                        eventData[optKeys.browserWidth] = browserDimensions.width;
  682.                        eventData[optKeys.browserHeight] = browserDimensions.height;
  683.  
  684.                        // cookies enabled
  685.                        eventData[optKeys.cookiesEnabled] = this._getCookiesEnabled(options);
  686.  
  687.                        // plugins
  688.                        eventData[optKeys.plugins] = this._getPlugins();
  689.                        this._recordEvent(eventData, options);
  690.                 },
  691.  
  692.                 // record click event
  693.                 recordClick: function (options) {
  694.                        // get keys from options
  695.                        var eventData = {};
  696.  
  697.                        options = this._mergeOptions(this.options.click.request, options);
  698.                        this._recordEvent(eventData, options);
  699.                 },
  700.  
  701.                 // record form abandonment event
  702.                 recordFormAbandonment: function (options) {
  703.                        // get keys from options
  704.                        var eventData = {},
  705.                        optKeys;
  706.  
  707.                        options = this._mergeOptions(this.options.formAbandonment.request, options);
  708.  
  709.                        optKeys = options.keys;
  710.  
  711.                        // last form focused
  712.                        eventData[optKeys.lastFormFocused] = this._getLastFormFocusedValue();
  713.  
  714.                        // last input focused
  715.                        eventData[optKeys.lastInputFocused] = this._getLastInputFocusedValue();
  716.  
  717.                        this._recordEvent(eventData, options);
  718.                 },
  719.  
  720.                 // sets up click tracking
  721.                 trackClicks: function (options) {
  722.  
  723.                        var elements,
  724.                              element,
  725.                              i,
  726.                              recordClick,
  727.                              self = this;
  728.  
  729.                        // merge the click options
  730.                        options = this._mergeOptions(this.options.click, options);
  731.  
  732.                        // merge the request options
  733.                        options = this._mergeOptions({ request: this.options.request }, options);
  734.  
  735.                        // get elements from options
  736.                        elements = this._getElements(options.elements);
  737.  
  738.                        // attach click events to elements
  739.                        recordClick = function (ev) {
  740.                              var target = ev.currentTarget || ev.srcElement,
  741.                                     clickOptions,
  742.                                     timeout;
  743.  
  744.                              if (typeof options.onClick === 'function') {
  745.                                     clickOptions = options.onClick(ev);
  746.                              }
  747.  
  748.                              // if the onClick returned false then we need to NOT track the click event
  749.                              if (clickOptions !== false) {
  750.  
  751.                                     // if the onClick returns an object we need to treat it as the request options to be used for that click track event
  752.                                     if (typeof clickOptions === 'object') {
  753.                                            options.request = self._mergeOptions(options.request, clickOptions);
  754.                                     }
  755.  
  756.                                     // get link url
  757.                                     options.request.data[options.request.keys.linkUrl] = target.getAttribute('href') || '';
  758.  
  759.                                     self.recordClick(options.request);
  760.                              }
  761.                        };
  762.                        for (i = 0; i < elements.length; i++) {
  763.                           element = elements[i];
  764.               this.utils.removeListener(element, 'click', recordClick);
  765.                           this.utils.addListener(element, 'click', recordClick);
  766.                        }
  767.                 },
  768.  
  769.                 // track form abandonment
  770.                 trackFormAbandonment: function (options) {
  771.                        var elements = [],
  772.                              self = this,
  773.                              i, //used in the for loop
  774.                              elementsLength;
  775.  
  776.                        // merge the form abandonment options
  777.                        options = this._mergeOptions(this.options.formAbandonment, options);
  778.  
  779.                        // merge the request options
  780.                        options = this._mergeOptions({ request: this.options.request }, options);
  781.  
  782.                        // get elements from options
  783.                        elements = this._getElements(options.elements);
  784.                        elementsLength = elements.length;
  785.                        // get all inputs within these elements
  786.                        for (i = 0; i < elementsLength; i++) {
  787.                              var element = elements[i],
  788.                              // get all form elements individually so we can loop through and have a reference to the parent form of any given input
  789.                              forms = (element.tagName.toLowerCase() === 'form') ? [element] : this.utils.getElements('form', element),
  790.                              formsLength = forms.length, j;
  791.  
  792.                              for (j = 0; j < formsLength; j++) {
  793.                                     var form = forms[j],
  794.                                            inputs,
  795.                                            inputsLength,
  796.                                            input,
  797.                                            k;
  798.  
  799.                                     // attach focus events to all inputs within this form to capture last form/input focused
  800.                                     inputs = this.utils.getElements('input', form);
  801.                                     inputsLength = inputs.length;
  802.                                     for (k = 0; k < inputsLength; k++) {
  803.                                            input = inputs[k];
  804.  
  805.                                            // use closure to maintain proper references of each iteration of input/form
  806.                                            (function (form, input) {
  807.                                                   self.utils.addListener(input, 'focus', function (ev) {
  808.                                                          self._lastFormFocused = form;
  809.                                                          self._lastInputFocused = input;
  810.  
  811.                                                          // attach unload function to window if it hasn't been done yet
  812.                                                          if (!self._trackingFormAbandonment) {
  813.                                                                 self._trackingFormAbandonment = true;
  814.  
  815.                                                                 // record form abandonment onbeforeunload
  816.                                                                 self.utils.addListener(window, 'beforeunload', function (ev) {
  817.                                                                        // record form abandonment only if there's an incomplete form
  818.                                                                        if (self._lastFormFocused !== null && self._lastInputFocused !== null) {
  819.                                                                               self.recordFormAbandonment(options.request);
  820.                                                                        }
  821.                                                                 });
  822.                                                                 // remove from abandonment event listener on submit of form, because they are not abandonoing the form
  823.                                                                 self.utils.addListener(form, 'submit', function (ev) {
  824.                                                                        // set last form/input to null to indicate form completion
  825.                                                                        self._lastFormFocused = null;
  826.                                                                        self._lastInputFocused = null;
  827.                                                                 });
  828.                                                          }
  829.                                                   });
  830.                                            })(form, input);
  831.                                     }
  832.                              }
  833.                        }
  834.                 },
  835.  
  836.                 // record arbitrary event
  837.                 record: function (options) {
  838.                        options = this._mergeOptions(this.options.request, options);
  839.  
  840.                        // create request
  841.                        this._request(options);
  842.                 }
  843.          };
  844.  
  845.          // utils
  846.          PAYPAL.analytics.Analytics.prototype.utils = {
  847.  
  848.                 // deep clones an object
  849.                 clone: function (obj) {
  850.                        // return if it is not an object
  851.                        if (obj === null || obj.constructor !== Object) {
  852.                              return obj;
  853.                        }
  854.  
  855.                        // TODO: not real clone, has prototype issues
  856.  
  857.                        // clone object
  858.                        var clone = obj.constructor(),
  859.                              key;
  860.  
  861.                        // deep clone
  862.                        for (key in obj) {
  863.                              clone[key] = this.clone(obj[key]);
  864.                        }
  865.  
  866.                        return clone;
  867.                 },
  868.  
  869.                 // object merge (recursive merge and does not changed original objects)
  870.                 merge: function (obj1, obj2) {
  871.                        // make sure we have some objects
  872.                        obj1 = obj1 || {};
  873.                        obj2 = obj2 || {};
  874.  
  875.                        // clone the objects so we don't affect the originals
  876.                        var clone1 = this.clone(obj1),
  877.                              clone2 = this.clone(obj2),
  878.                              p;
  879.  
  880.                        // merge the objects recursively
  881.                        for (p in clone2) {
  882.  
  883.                              try {
  884.                                     if (clone2[p].constructor === Object) {
  885.                                            clone1[p] = this.merge(clone1[p], clone2[p]);
  886.                                     } else {
  887.                                            clone1[p] = clone2[p];
  888.                                     }
  889.                              } catch (e) {
  890.                                     clone1[p] = clone2[p];
  891.                              }
  892.                        }
  893.  
  894.                        // return our merged object
  895.                        return clone1;
  896.                 },
  897.  
  898.                 // converts query string to object
  899.                 queryStringToObject: function (string) {
  900.                        var obj = {},
  901.                              i,
  902.                              parts,
  903.                              pairs = string.split('&');
  904.                        for (i = 0; i < pairs.length; i++) {
  905.                              parts = pairs[i].split('=');
  906.                              obj[parts[0]] = decodeURIComponent(parts[1]);
  907.                        }
  908.                        return obj;
  909.                 },
  910.  
  911.                 // selector engine
  912.                 getElements: function (selector, parent) {
  913.                        var elements = [],
  914.                              i = 0,
  915.                              length,
  916.                              obj,
  917.                              style,
  918.                              nodes, //to store the list of child nodes
  919.                              node;  //single node
  920.  
  921.                        // set parent to document if not passed
  922.                        parent = parent || document;
  923.  
  924.                        // if the browser does not support querySelectorAll we need to do it ourselves
  925.                        if (parent.querySelectorAll) {
  926.                               obj = parent.querySelectorAll(selector);
  927.  
  928.                              // convert object/function to array of elements
  929.                              if ((typeof obj === 'object' || typeof obj === 'function') && typeof obj.length === 'number') {
  930.                                     for (i = 0; i < obj.length; i++) {
  931.                                            elements.push(obj[i]);
  932.                                     }
  933.                              } else if (typeof obj === 'array') {
  934.                                     elements = obj;
  935.                              }
  936.                        } else if (document.createStyleSheet) {
  937.                              if (document.styleSheets.length) { // IE requires you check against the length as it bugs out otherwise
  938.                                     style = document.styleSheets[0];
  939.                              } else {
  940.                                     style = document.createStyleSheet();
  941.                              }
  942.  
  943.                              // split selector on comma because IE7 doesn't support comma delimited selectors
  944.                              var selectors = selector.split(/\s*,\s*/),
  945.                                     indexes = [],
  946.                                     index;
  947.                              for (i = 0; i < selectors.length; i++) {
  948.                                     // create custom (random) style rule and add it to elements
  949.                                     index = style.rules.length;
  950.                                     indexes.push(index);
  951.                                     style.addRule(selectors[i], 'aybabtu:pa', index);
  952.                              }
  953.  
  954.                              // get all child nodes (document object has bugs with childNodes)
  955.                              if (parent === document) {
  956.                                     nodes = parent.all;
  957.                              } else {
  958.                                     nodes = parent.childNodes;
  959.                              }
  960.  
  961.                              // cycle through all elements until we find the ones with our custom style rule
  962.                              for (i = 0, length = nodes.length; i < length; i++) {
  963.                                     node = nodes[i];
  964.                                     if (node.nodeType === 1 && node.currentStyle.aybabtu === 'pa') {
  965.                                            elements.push(node);
  966.                                     }
  967.                              }
  968.  
  969.                              // remove the custom style rules we added (go backwards through loop)
  970.                              for (i = indexes.length - 1; i >= 0; i--) {
  971.                                     style.removeRule(indexes[i]);
  972.                              }
  973.                        }
  974.                        return elements;
  975.                 },
  976.  
  977.                 // cross browser add event listener
  978.                 addListener: function (element, event, callback) {
  979.                        if (element.addEventListener) {
  980.                              element.addEventListener(event, callback, false);
  981.                        } else if (element.attachEvent) {
  982.                              return element.attachEvent('on' + event, callback);
  983.                        }
  984.                 },
  985.  
  986.                 // cross browser remove event listener
  987.                 // TODO: test this function, it has not been tested yet
  988.                 removeListener: function (event, element, callback) {
  989.                        if (element.removeEventListener) {
  990.                              element.removeEventListener(event, callback, false);
  991.                        } else if (element.detachEvent) {
  992.                              return element.detachEvent('on' + event, callback);
  993.                        }
  994.                 },
  995.  
  996.                 // sets cookie key/value pair
  997.                 setCookie: function (key, value, options) {
  998.                        var date, expires;
  999.                        options = options || {};
  1000.  
  1001.                        // convert expiration from days to ms
  1002.                        if (options.expires) {
  1003.                              date = new Date();
  1004.                              date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
  1005.                              expires = '; expires=' + date.toGMTString();
  1006.                        } else {
  1007.                              expires = '';
  1008.                        }
  1009.  
  1010.                        // set the cookie
  1011.                        document.cookie = key + "=" + value + expires + '; path=/';
  1012.                 },
  1013.  
  1014.                 // gets cookie value from given key
  1015.                 getCookie: function (key) {
  1016.                        // break cookie string into key/value segments and check for the given key
  1017.                        var segments = document.cookie.split(';'),
  1018.                              i;
  1019.                        for (i = 0; i < segments.length; i++) {
  1020.                              var segment = segments[i];
  1021.  
  1022.                              // trim leading white space
  1023.                              while (segment.charAt(0) === ' ') {
  1024.                                     segment = segment.substring(1, segment.length);
  1025.                              }
  1026.  
  1027.                              // check to see if this segment has they key they asked for at the beginning
  1028.                              if (segment.indexOf(key + '=') === 0) {
  1029.                                     return segment.substring((key + '=').length, segment.length);
  1030.                              }
  1031.                        }
  1032.  
  1033.                        // return null if key was not found
  1034.                        return null;
  1035.                 },
  1036.  
  1037.                 // removes a cookie key/value pair
  1038.                 removeCookie: function (key) {
  1039.                        this.setCookie(key, '', { expires: -1 });
  1040.                 }
  1041.          };
  1042.  
  1043.          // PAYPAL sugar layer
  1044.          // Adding the delay of 500 msec,
  1045.          // so that this Analytics JS is not blocking the execution of other important scripts in the page.
  1046.       PAYPAL.analytics.setup = function (options) {
  1047.               PAYPAL.analytics.setupComplete = PAYPAL.analytics.setupComplete || function () {};
  1048.               setTimeout(function () {
  1049.                    PAYPAL.analytics.setupComplete(PAYPAL.analytics.setup.init(options));
  1050.               }, 500);
  1051.          };
  1052.  
  1053.          PAYPAL.analytics.setup.init = function (options) {
  1054.                 options = options || {};
  1055.  
  1056.           window.fptiserverurl = options.url || '//t.paypal.com/ts';
  1057.  
  1058.                 // unload delay
  1059.                 var unloadDelay = 500;
  1060.  
  1061.  
  1062.                 // create analytics object with request options
  1063.                 var analytics = new PAYPAL.analytics.Analytics({
  1064.                        request: {
  1065.                              data: options.data || {},
  1066.                              keys: {
  1067.                                     version: 'v',
  1068.                                     timestamp: 't',
  1069.                                     gmtOffset: 'g',
  1070.                                     eventType: 'e'
  1071.                              },
  1072.                              values: {
  1073.                                     eventType: 'na',
  1074.                                     'true': 1,
  1075.                                     'false': 0
  1076.                              },
  1077.                              keyPrefix: '', // no prefix
  1078.                              url: options.url || '//t.paypal.com/ts',
  1079.                              onBeaconCreate: function () {},
  1080.                              onBeaconDestroy: function () {}
  1081.                        }
  1082.                 });
  1083.  
  1084.                 if (options.data) {
  1085.                   window.fpti = analytics.utils.queryStringToObject(options.data);
  1086.                 }
  1087.  
  1088.                 // setup impression options
  1089.                 var impressionOptions = {
  1090.                        keys: {
  1091.                              cookiesEnabled: 'ce',
  1092.                              plugins: 'pl',
  1093.                              jsVersion: 'jsv',
  1094.                              pageTitle: 'pt',
  1095.                              referrer: 'ru',
  1096.                              screenColorDepth: 'cd',
  1097.                              screenWidth: 'sw',
  1098.                              screenHeight: 'sh',
  1099.                              browserWidth: 'bw',
  1100.                              browserHeight: 'bh'
  1101.                        },
  1102.                        values: {
  1103.                              eventType: 'im'
  1104.                        }
  1105.                 };
  1106.  
  1107.                 // set pglk if we have something stored in the tcs cookie, then remove the cookie
  1108.                 var pglk = analytics.utils.getCookie('tcs');
  1109.                 analytics.utils.removeCookie('tcs');
  1110.                 if (pglk) {
  1111.                     pglk = decodeURIComponent(pglk);
  1112.                     impressionOptions.data = { pglk: pglk };
  1113.                 }
  1114.  
  1115.                 // link text getter
  1116.                 var getFirstText = function (element) {
  1117.                        var nodes = element.childNodes,
  1118.                              i,
  1119.                              node;
  1120.                        for (i = 0; i < nodes.length; i++) {
  1121.                              node = nodes[i];
  1122.                              if (node.nodeType === 3 && node.nodeValue && node.nodeValue.match(/\S/)) {
  1123.                                     return node.nodeValue;
  1124.                              } else if (node.nodeType === 1 && node.childNodes.length) {
  1125.                                     return getFirstText(node);
  1126.                              }
  1127.                        }
  1128.                 };
  1129.  
  1130.                 var getTargetAttr = function (target, primaryAttrName) {
  1131.                        var attr;
  1132.                        if (primaryAttrName) {
  1133.                              attr = target.getAttribute(primaryAttrName);
  1134.                        }
  1135.                        if (!attr) {
  1136.                              attr = target.getAttribute('name') ||
  1137.                                            target.getAttribute('id') || getFirstText(target) ||
  1138.                                            target.getAttribute('href');
  1139.                        }
  1140.                        return attr;
  1141.                 };
  1142.  
  1143.                 // record impression
  1144.                 analytics.recordImpression(impressionOptions);
  1145.  
  1146.                 // setup click tracking
  1147.                 analytics.trackClicks({
  1148.                        elements: '*[data-pa-click]',
  1149.                        onClick: function (ev) {
  1150.                              var target = ev.currentTarget || ev.srcElement,
  1151.                                     link = getTargetAttr(target, 'data-pa-click');
  1152.  
  1153.                              // return request options for this click event
  1154.                              return {
  1155.                                     data: {
  1156.                                            link: link
  1157.                                     }
  1158.                              };
  1159.                        },
  1160.                        request: {
  1161.                              unloadDelay: unloadDelay,
  1162.                              keys: {
  1163.                                     linkUrl: 'lu'
  1164.                              },
  1165.                              values: {
  1166.                                     eventType: 'cl'
  1167.                              }
  1168.                        }
  1169.                 });
  1170.  
  1171.                 // setup exit click tracking
  1172.                 analytics.trackClicks({
  1173.                        elements: '*[data-pa-exit]',
  1174.                        onClick: function (ev) {
  1175.                              var target = ev.currentTarget || ev.srcElement,
  1176.                                     link = getTargetAttr(target, 'data-pa-exit');
  1177.  
  1178.                              // return request options for this click event
  1179.                              return {
  1180.                                     data: {
  1181.                                            link: link,
  1182.                                            exit: analytics.options.request.values['true']
  1183.                                     }
  1184.                              };
  1185.                        },
  1186.                        request: {
  1187.                              unloadDelay: unloadDelay,
  1188.                              keys: {
  1189.                                     linkUrl: 'lu'
  1190.                              },
  1191.                              values: {
  1192.                                     eventType: 'cl'
  1193.                              }
  1194.                        }
  1195.                 });
  1196.  
  1197.                 // setup download click tracking
  1198.                 analytics.trackClicks({
  1199.                        elements: '*[data-pa-download], *[href*=".zip"], *[href*=".wav"], *[href*=".mov"], *[href*=".mpg"], *[href*=".avi"], *[href*=".wmv"], *[href*=".doc"], *[href*=".docx"], *[href*=".pdf"], *[href*=".xls"], *[href*=".xlsx"], *[href*=".ppt"], *[href*=".pptx"], *[href*=".txt"], *[href*=".csv"], *[href*=".psd"], *[href*=".tar"]',
  1200.                        onClick: function (ev) {
  1201.                              var target = ev.currentTarget || ev.srcElement,
  1202.                                     link = getTargetAttr(target, 'data-pa-download');
  1203.  
  1204.                              // return request options for this click event
  1205.                              return {
  1206.                                     data: {
  1207.                                            link: link,
  1208.                                            dwnl: analytics.options.request.values['true']
  1209.                                     }
  1210.                              };
  1211.                        },
  1212.                        request: {
  1213.                              unloadDelay: unloadDelay,
  1214.                              keys: {
  1215.                                     linkUrl: 'lu'
  1216.                              },
  1217.                              values: {
  1218.                                     eventType: 'cl'
  1219.                              }
  1220.                        }
  1221.                 });
  1222.  
  1223.                 // setup legacy click tracking
  1224.                 analytics.trackClicks({
  1225.                        elements: '*[class*="scTrack"]',
  1226.                        onClick: function (ev) {
  1227.                              var target = ev.currentTarget || ev.srcElement,
  1228.                                     link = getTargetAttr(target);
  1229.  
  1230.                              // go through classes and find the link value
  1231.                              var classes = target.className.split(' '),
  1232.                                     i;
  1233.                              for (i = 0; i < classes.length; i++) {
  1234.                                     var parts = classes[i].split(':');
  1235.                                     if (parts[0] === 'scTrack' && parts.length > 1) {
  1236.                                            parts.shift();
  1237.                                            link = parts.join(':');
  1238.                                     }
  1239.                              }
  1240.  
  1241.                              // return request options for this click event
  1242.                              return {
  1243.                                     data: {
  1244.                                            link: link
  1245.                                     }
  1246.                              };
  1247.                        },
  1248.                        request: {
  1249.                              unloadDelay: unloadDelay,
  1250.                              keys: {
  1251.                                     linkUrl: 'lu'
  1252.                              },
  1253.                              values: {
  1254.                                     eventType: 'cl'
  1255.                              }
  1256.                        }
  1257.                 });
  1258.  
  1259.                 // setup legacy exit click tracking
  1260.                 analytics.trackClicks({
  1261.                        elements: '*[class*="scExit"]',
  1262.                        onClick: function (ev) {
  1263.                              var target = ev.currentTarget || ev.srcElement,
  1264.                                     link = getTargetAttr(target);
  1265.  
  1266.                              // go through classes and find the link value
  1267.                              var classes = target.className.split(' '),
  1268.                                     i;
  1269.                              for (i = 0; i < classes.length; i++) {
  1270.                                     var parts = classes[i].split(':');
  1271.                                     if (parts[0] === 'scExit' && parts.length > 1) {
  1272.                                            parts.shift();
  1273.                                            link = parts.join(':');
  1274.                                     }
  1275.                              }
  1276.  
  1277.                              // return request options for this click event
  1278.                              return {
  1279.                                     data: {
  1280.                                            link: link,
  1281.                                            exit: analytics.options.request.values['true']
  1282.                                     }
  1283.                              };
  1284.                        },
  1285.                        request: {
  1286.                              unloadDelay: unloadDelay,
  1287.                              keys: {
  1288.                                     linkUrl: 'lu'
  1289.                              },
  1290.                              values: {
  1291.                                     eventType: 'cl'
  1292.                              }
  1293.                        }
  1294.                 });
  1295.  
  1296.                 // setup click-through tracking
  1297.                 analytics.trackClicks({
  1298.                        elements: 'a, button, input[type=submit], input[type=button], input[type=image]',
  1299.                        onClick: function (ev) {
  1300.                              var target = ev.currentTarget || ev.srcElement,
  1301.                                     link = target.getAttribute('data-pa-click') ||
  1302.                                                   target.getAttribute('data-pa-exit') ||
  1303.                                                   target.getAttribute('data-pa-download');
  1304.                              var pgrp = analytics.getRequestData('pgrp') || '';
  1305.  
  1306.                              // try to get the link name from legacy attributes if we don't have one
  1307.                              if (!link) {
  1308.                                     // go through classes and find the link value
  1309.                                     var classes = target.className.split(' '),
  1310.                                            i;
  1311.                                     for (i = 0; i < classes.length; i++) {
  1312.                                            var parts = classes[i].split(':');
  1313.                                            if ((parts[0] === 'scTrack' || parts[0] === 'scExit') && parts.length > 1) {
  1314.                                                   parts.shift();
  1315.                                                   link = parts.join(':');
  1316.                                            }
  1317.                                     }
  1318.                              }
  1319.  
  1320.                              // default to name/id/text if we still don't have a link value
  1321.                              if (!link) {
  1322.                                     link = getTargetAttr(target);
  1323.                              }
  1324.  
  1325.                              // create pglk value
  1326.                              var pglk = encodeURIComponent(pgrp + '|' + link);
  1327.  
  1328.                              // 100 char cookie value limit
  1329.                              //if (pglk.length > 100) {
  1330.                              //       pglk = pglk.substr(0, 100);
  1331.                              //}
  1332.  
  1333.                              // store cookie data for next page
  1334.                              analytics.utils.setCookie('tcs', pglk);
  1335.  
  1336.                               // return false so the link doesnt record a click event
  1337.                              return false;
  1338.                        }
  1339.                 });
  1340.  
  1341.                 // setup form abandonment
  1342.                 analytics.trackFormAbandonment({
  1343.                        elements: 'form',
  1344.                        request: {
  1345.                              unloadDelay: unloadDelay,
  1346.                              keys: {
  1347.                                     lastFormFocused: 'lf',
  1348.                                     lastInputFocused: 'li'
  1349.                              },
  1350.                              values: {
  1351.                                     eventType: 'fa'
  1352.                              }
  1353.                        }
  1354.                 });
  1355.  
  1356.                 // return analytics object
  1357.                 return PAYPAL.analytics.instance = analytics;
  1358.  
  1359.        };
  1360. }());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement