(function() { // Fill the variables dynamically from the server var enableFormConversions = true; var enableSubmissionButtonCheck = false; var enableCookieCreation = false; var enableCBUTMCodesFirst = false; //var enableCBUTMCodesFirst = false; console.log("EnableCBUTMFirst = " + enableCBUTMCodesFirst); var serverParamters = '{"VisitorIPAddress":"76.131.226.205","Port":"443","AccountToken":"ea6c34a0-fb94-4178-9712-cefce51a574c","RequestUrlScheme":"https","JavaGUID":"7c1b5d50-a8fc-4752-9748-00383fcca0d3","ServerTime":"5","ClickbackWebTimeout":"1200","ClickbackWebTimeoutFormData":"3000","ClickbackMail":"//www.cbvisittracker.com/Cookie/CookieReadJS.js","FormData":"{}"}'; var trackers = '[{"TrackerAPI":"track.cbdatatracker.com","Token":"ea6c34a0-fb94-4178-9712-cefce51a574c"}]'; //var cbEmail = ""; // Create the script element in the page var script = document.createElement('script'); // Get the json object string built dynamically on the server... var oTxt = serverParamters; // ...and parse it var o = JSON.parse(oTxt); // Get some values from the parsed object var javaGUID = o.JavaGUID; // The GUID that identifies the current page view var serverTime = o.ServerTime; // The initial length of time to wait before pinging var waitTime = serverTime * 1000; // Parse the array of trackers provided by the server var arrTrackers = JSON.parse(trackers); // A function to replace undefined values with the string 'undefined' var replacer = function(key, value) { return (typeof value === 'undefined' ? 'undefined' : value); }; // A function to send txt to the tracking url at the specified server // Ping: true/false - whether or not to send to a Ping address // synchronous: whether to send the data synchronously or not // RequestUrlScheme: the scheme to use for sending // Port: the port to send to on the server function send (server, txt, ping, synchronous, RequestUrlScheme, Port) { // Get the object that will do the sending var xmlhttp; if (window.XMLHttpRequest) { xmlhttp = new XMLHttpRequest(); } else { xmlhttp = new ActiveXObject('Microsoft.XMLHTTP'); } // Build the url to send to var url = RequestUrlScheme + '://' + server + ':' + Port + '/api/tracker/Post?_method=put'; if (ping) { url = RequestUrlScheme + '://' + server + ':' + Port + '/api/tracker/PostPing?_method=put' } // Open a connection xmlhttp.open('POST', url, !synchronous); // Add a handler to log changes to ReadyState xmlhttp.onreadystatechange = function () { console.log("Ready State: " + xmlhttp.readyState + "; Status: " + xmlhttp.status + "; Response: " + xmlhttp.responseText); }; // Set various header values xmlhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); xmlhttp.setRequestHeader('Cache-Control', 'no-cache'); xmlhttp.setRequestHeader('Access-Control-Allow-Origin', '*'); try { // Send the data xmlhttp.send(txt); } catch (ex) { // Sending failed. Log to the console try { console.log(ex.name + ": " + ex.message); } catch (logex) { // Do nothing - couldn't log the error } } } // Get a function to send tracking data to the trackers // Returns: a function (no parameters) that will // 1. Update the provided json object with current info from the browser and // 2. Send the updated json object to the trackers // sSourceObject: the string representation of the json object to update and send // Synchronous: true/false (false is default) - set to true to make // the returned function send the data synchronously var getTrackingDataSender = function (sSourceObject, synchronous) { if (!synchronous) { synchronous = false; } // Declare and return a function return function() { // Parse the input string var o = JSON.parse(sSourceObject); // Get the referrer from the page var refrr; try { refrr = parent == self ? document.referrer : parent.document.referrer; } catch (e) { refrr = document.referrer; } refrr = refrr.replace(/&/g, 'AND'); // A function to get information about the browser function getBrowserInfo() { if(enableCookieCreation) { // Create a cookie from the utm information var documentUrl = document.URL.trim().split("?"); if(cbEmail===""){ if(documentUrl.length===2){ var queryString = documentUrl[1].replace(/AND/g, ""); var utmItems = queryString.toLowerCase().split("utm_"); var cookieString = ""; var date = new Date(); date.setTime(date.getTime()+(365*24*60*60*1000)); var expires = "; expires=" + date.toUTCString(); for(var i = 0; i < utmItems.length; i++){ var utmValues = utmItems[i].split("="); switch(utmValues[0]){ case "email": document.cookie = "E=" + utmValues[1] + expires; break; case "rid": document.cookie = "CID=-1" + expires; break; case "sid": document.cookie = "S=" + utmValues[1] + expires; break; } } if(cbEmail!=="") { document.cookie = cbEmail; } } } } if(cbEmail==="") { cbEmail = document.cookie; } var ua = navigator.userAgent, tem, M = ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || []; if (/trident/i.test(M[1])) { tem = /\brv[ :]+(\d+)/g.exec(ua) || []; return 'IE' + (tem[1] || ''); } if (M[1] === 'Chrome') { tem = ua.match(/\b(OPR|Edge)\/(\d+)/); if (tem != null) { tem.slice(1).join(' ').replace('OPR', 'Opera'); } } M = M[2] ? [M[1], M[2]] : [navigator.appName, navigator.appVersion, '-?']; if ((tem = ua.match(/version\/(\d+)/i)) != null) M.splice(1, 1, tem[1]); return M.join(' '); }; // Add the browser information to the json object o.br = getBrowserInfo().substring(0, 49); // A function to build a json object string from the one provided function buildtxt(token) { var oFinal = { IP: o.VisitorIPAddress, ClientAccountToken: token, VisitDateTime: "1970 -01-01T00:00:00.000Z", PageTitle: document.title, PageUrl: document.URL.trim().substr(0,1500), LandingTypeID: 1, LandingFromName: refrr, LandingFromKeys: "", ScreenWidth: (typeof screen.availWidth === 'undefined' ? 'undefined' : screen.availWidth), ScreenHeight: (typeof screen.availHeight === 'undefined' ? 'undefined' : screen.availHeight), OS: navigator.platform, IsCookieEnabled: navigator.cookieEnabled, Browser: o.br, BrowserVersion: navigator.userAgent, LanguageCode: navigator.language, ColorDepth: screen.colorDepth, CbEmail: cbEmail, BrowserAgent: navigator.userAgent, FormData: JSON.parse(JSON.stringify(o.FormData, replacer)), JavaGUID: javaGUID }; return JSON.stringify(oFinal, replacer); } // Send the json to the trackers for (var i = 0; i < arrTrackers.length; i++) { send(arrTrackers[i].TrackerAPI, buildtxt(arrTrackers[i].Token), false, synchronous, o.RequestUrlScheme, o.Port); } }; }; // Form handling var setupFormData = (function (o, trackingDataSender) { var sendFormData = function (formData, synchronous) { o.FormData = formData; trackingDataSender(JSON.stringify(o), synchronous)(); }; function GetIDsFromObjectProperties(object, doc, op) { var cbFormIdText = "CB_FORM_ID_"; var cbFormIdCount = 0; var cbInputIdText = "CB_INPUT_ID_"; var cbInputIdCount = 0; if (!op) { op = function (property) { if (property.id) { result[i] = property.id; i++; } else if (property.tagName && property.tagName.toLowerCase() === "form") { var element; var newID; do { cbFormIdCount++; newID = cbFormIdText + cbFormIdCount.toString(); element = doc.getElementById(newID); } while (element !== null && element !== undefined); property.id = newID; result[i] = property.id; i++; } else if (property.tagName && (property.tagName.toLowerCase() === "input" || property.tagName.toLowerCase() === "textarea")) { var element; var newID; do { cbInputIdCount++; if(property.name) { newID = property.name + cbInputIdCount.toString(); } else { newID = cbInputIdText + cbInputIdCount.toString(); } } while (element !== null && element !== undefined); property.id = newID; result[i] = property.id; i++; } } } var result = []; var i = 0; for (var key in object) { var property = object[key]; op(property); } return result; } var cb = {}; window.cb = cb; // Form state handling cb.FormState = (function () { var UNPROCESSED = "UNPROCESSED"; var PROCESSED = "PROCESSED"; var INVALID = "INVALID"; var VALID = "VALID"; var unprocessedFormIDs = {}; var formValidity = {}; var FormState = {}; // Check if the form is in the specified state on the specified states object // formID: the ID of a form on the page // states: the object in which to check // stateToCheck: the state to check for // returns: truthy if formID is in the specified state, // falsy otherwise including if the state is undefined var IsFormInState = function (formId, states, stateToCheck) { return states[formId] && states[formId] === stateToCheck; } // Set the form to be in the specified new state on the specified states object // formID: the ID of a form on the page // states: the object that will get the new state // newState: the state to set the form to var SetState = function (formId, states, newState) { states[formId] = newState; } var SetFormUnprocessed = function (formId) { SetState(formId, unprocessedFormIDs, UNPROCESSED); }; var SetFormProcessed = function (formId) { SetState(formId, unprocessedFormIDs, PROCESSED); }; var SetFormInvalid = function (formId) { SetState(formId, formValidity, INVALID); }; var SetFormValid = function (formId) { SetState(formId, formValidity, VALID); }; var IsFormUnprocessed = function (formId) { return IsFormInState(formId, unprocessedFormIDs, UNPROCESSED); }; var IsFormProcessed = function (formId) { return IsFormInState(formId, unprocessedFormIDs, PROCESSED); }; var IsFormInvalid = function (formId) { return IsFormInState(formId, formValidity, INVALID); }; var IsFormValid = function (formId) { return IsFormInState(formId, formValidity, VALID); }; FormState.SetFormUnprocessed = SetFormUnprocessed; FormState.SetFormProcessed = SetFormProcessed; FormState.SetFormInvalid = SetFormInvalid; FormState.SetFormValid = SetFormValid; FormState.IsFormUnprocessed = IsFormUnprocessed; FormState.IsFormProcessed = IsFormProcessed; FormState.IsFormInvalid = IsFormInvalid; FormState.IsFormValid = IsFormValid; return FormState; })(); // Form handling cb.Form = (function () { // Get the forms on the page // doc: a document object // returns: an OBJECT (not array) with properties containing each form element function getFormsOnPage(doc) { return doc.getElementsByTagName('form'); } // Get the IDs of the forms on the page // doc: a document object // returns: an array containing the IDs of the forms on the page function getFormIDsOnPage(doc) { var forms = getFormsOnPage(doc); return GetIDsFromObjectProperties(forms, doc); } function Registration(doc, getFormIDs, register, setFormUnprocessed) { var formIDs = getFormIDs(doc); for (var i = 0; i < formIDs.length; i++) { var id = formIDs[i]; register(doc, id); setFormUnprocessed(id); } } function RegisterForm(doc, formID, onInvalid, onSubmit) { try { var form = doc.getElementById(formID); var inputs = document.getElementsByTagName("input"); var foundSubmit = false; var subButton; if(enableSubmissionButtonCheck) { for(let item of inputs) { if(item.type=="submit"){foundSubmit=true;} if(item.type=="button"&&item.value.toLowerCase()=="submit"){subButton=item;} } if(!foundSubmit) { if(typeof subButton!=='undefined'){subButton.addEventListener('click',onSubmit(doc, formID, form));} } } form.addEventListener('invalid', onInvalid(formID)); form.addEventListener('submit', onSubmit(doc, formID, form)); } catch (ex) { // Do nothing - failed to get form data } } function Setup() { // Do any setup here } var o = {}; o.getFormsOnPage = getFormsOnPage; o.getFormIDsOnPage = getFormIDsOnPage; o.Registration = Registration; o.RegisterForm = RegisterForm; o.Setup = Setup; return o; })(); // Form element handling cb.Form.Elements = (function () { function GetFieldIDsFromForm(form) { var inputs = GetIDsFromObjectProperties(form.getElementsByTagName("input")); var textAreas = GetIDsFromObjectProperties(form.getElementsByTagName("textarea")); var selects = GetIDsFromObjectProperties(form.getElementsByTagName("select")); return inputs.concat(textAreas).concat(selects); } function GetElementValue(element) { var result = {}; switch (element.localName.toLowerCase()) { case 'select': result = getSelectValues(element); break; default: result = element.value; break; } return result; } function getSelectValues(select) { var result = []; var options = select && select.options; var opt; for (var i = 0, iLen = options.length; i < iLen; i++) { opt = options[i]; if (opt.selected) { result.push(opt.text); } } return result; } function skipElementID(element) { var elementID = element.id; var elementType = element.type; result = false; if (elementID === "__EVENTTARGET" || elementID === "__EVENTARGUMENT" || elementID === "__VIEWSTATE" || elementID === "g-recaptcha-response" || elementType === "password" || elementType === "hidden") { result = true; } return result; } function GetElementValues(doc, elementIDs) { var result = {}; for (var i = 0; i < elementIDs.length; i++) { var elementID = elementIDs[i]; var element = doc.getElementById(elementID); if (skipElementID(element)) { continue; } try { result[elementID] = GetElementValue(element); } catch (ex) { result[elementID] = 'Error getting value: ' + ex.message; } } return result; } function GetElementValuesFromForm(doc, form) { return GetElementValues(doc, GetFieldIDsFromForm(form)); } var o = {}; o.GetFieldIDsFromForm = GetFieldIDsFromForm; o.GetElementValues = GetElementValues; o.GetElementValuesFromForm = GetElementValuesFromForm; return o; })(); function onInvalid(formID) { return function (e) { cb.FormState.SetFormInvalid(formID); }; }; function onSubmit(doc, formID, form) { return function (e) { if (cb.FormState.IsFormInvalid(formID)) { // Not valid. Set back to valid cb.FormState.SetFormValid(formID); return; } else if (cb.FormState.IsFormProcessed(formID)) { // Already processed return true; } try { var toSend = cb.Form.Elements.GetElementValuesFromForm(doc, form); // Send synchronously - this ensures the data is sent and *does NOT block* the submit from continuing! // TODO: Change this code to use asynchronous calls for // browsers that don't cancel send operations once onSubmit returns sendFormData(toSend, true); } catch (ex) { // Do nothing - failed to get form data } }; }; function register(doc, formID) { cb.FormState.SetFormValid(formID); cb.Form.RegisterForm(doc, formID, onInvalid, onSubmit); } cb.onInvalid = onInvalid; cb.onSubmit = onSubmit; cb.register = register; return function () { cb.Form.Registration(document, cb.Form.getFormIDsOnPage, register, cb.FormState.SetFormUnprocessed); }; })(o, getTrackingDataSender); // A function to ping the server // 1. Send the data to the server // 2. Set up the next ping function pingServer(){ // Get object to send var oInterval = { JavaGUID: javaGUID, ServerTime: serverTime, CbEmail: cbEmail }; var oIntervalTxt = JSON.stringify(oInterval); for(var i = 0; i < arrTrackers.length; i++) { send(arrTrackers[i].TrackerAPI, oIntervalTxt, true, false, o.RequestUrlScheme, o.Port); } // Update reporting, double the time 5, 10, 20, 30, etc... serverTime = serverTime * 2; // Update the wait time too, but remove the original wait time to keep the timing consitent waitTime = (serverTime * 1000) - waitTime; // Set up next ping setTimeout(pingServer, waitTime); } // Everything above this point is just setup. Here we actually do the work // Add a handler for when the script finishes loading script.onload = function() { // Set up the first ping setTimeout(pingServer, waitTime); // Send the initial tracking data setTimeout(getTrackingDataSender(oTxt), Number(o.ClickbackWebTimeout)); // Set up form tracking on the pag if (enableFormConversions) { setTimeout(setupFormData, Number(o.ClickbackWebTimeoutFormData)); } }; // Add script to retrieve the cookie script.src = o.ClickbackMail; document.head.appendChild(script); if (window.location.href.indexOf('sid')>=0) utmcodes(window.location.hostname, enableCBUTMCodesFirst); })(); function utmcodes(url, enableCBUTMCodesFirst) { var domainsToDecorate = [ url // add or remove domains (without https or trailing slash) ], queryParams = [ 'cid', //add or remove query parameters you want to transfer 'sid', 'rID', 'rid', 'lid' ] // ------------------------- do not edit anything below this line ------------------------- // var links = document.querySelectorAll('a'); //console.log("EnableCBUTMFirst = " + enableCBUTMCodesFirst); // check if links contain domain from the domainsToDecorate array and then decorates for (var linkIndex = 0; linkIndex < links.length; linkIndex++) { for (var domainIndex = 0; domainIndex < domainsToDecorate.length; domainIndex++) { if (links[linkIndex].href.indexOf(domainsToDecorate[domainIndex]) > -1 && links[linkIndex].href.indexOf("#") === -1) { links[linkIndex].href = decorateUrl(links[linkIndex].href); } } } // decorates the URL with query params function decorateUrl(urlToDecorate) { //urlToDecorate = (urlToDecorate.indexOf('?') === -1) ? urlToDecorate + '?' : urlToDecorate + '&'; var collectedQueryParams = []; for (var queryIndex = 0; queryIndex < queryParams.length; queryIndex++) { if (getQueryParam(queryParams[queryIndex])) { collectedQueryParams.push(queryParams[queryIndex] + '=' + getQueryParam(queryParams[queryIndex])) } } if (collectedQueryParams.length>0) urlToDecorate = (urlToDecorate.indexOf('?') === -1) ? urlToDecorate + '?' : urlToDecorate + '&'; return urlToDecorate + collectedQueryParams.join('&'); } // borrowed from https://stackoverflow.com/questions/831030/ // a function that retrieves the value of a query parameter function getQueryParam(name) { if (name = (new RegExp('[?&]' + encodeURIComponent(name) + '=([^&]*)')).exec(window.location.search)) return decodeURIComponent(name[1]); } };