/shared/assets/frontend/js/analytics-src/analytics.utils.js

https://github.com/deltafactory/landing-pages · JavaScript · 657 lines · 480 code · 47 blank · 130 comment · 109 complexity · f6db6b5cb9b0d29bd61668042ac5c9c9 MD5 · raw file

  1. /**
  2. * # _inbound UTILS
  3. *
  4. * This file contains all of the utility functions used by analytics
  5. *
  6. * @author David Wells <david@inboundnow.com>
  7. * @version 0.0.1
  8. */
  9. var _inboundUtils = (function(_inbound) {
  10. var storageSupported;
  11. _inbound.Utils = {
  12. init: function() {
  13. this.polyFills();
  14. this.checkLocalStorage();
  15. this.SetUID();
  16. this.storeReferralData();
  17. },
  18. /*! http://stackoverflow.com/questions/951791/javascript-global-error-handling */
  19. /* Polyfills for missing browser functionality */
  20. polyFills: function() {
  21. /* Console.log fix for old browsers */
  22. if (!window.console) {
  23. window.console = {};
  24. }
  25. var m = [
  26. "log", "info", "warn", "error", "debug", "trace", "dir", "group",
  27. "groupCollapsed", "groupEnd", "time", "timeEnd", "profile", "profileEnd",
  28. "dirxml", "assert", "count", "markTimeline", "timeStamp", "clear"
  29. ];
  30. // define undefined methods as noops to prevent errors
  31. for (var i = 0; i < m.length; i++) {
  32. if (!window.console[m[i]]) {
  33. window.console[m[i]] = function() {};
  34. }
  35. }
  36. /* Event trigger polyfill for IE9 and 10
  37. (function() {
  38. function CustomEvent(event, params) {
  39. params = params || {
  40. bubbles: false,
  41. cancelable: false,
  42. detail: undefined
  43. };
  44. var evt = document.createEvent('CustomEvent');
  45. evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
  46. return evt;
  47. }
  48. CustomEvent.prototype = window.Event.prototype;
  49. window.CustomEvent = CustomEvent;
  50. })();*/
  51. /* custom event for ie8+ https://gist.github.com/WebReflection/6693661 */
  52. try{new CustomEvent('?');}catch(o_O){
  53. /*!(C) Andrea Giammarchi -- WTFPL License*/
  54. this.CustomEvent = function(
  55. eventName,
  56. defaultInitDict
  57. ){
  58. // the infamous substitute
  59. function CustomEvent(type, eventInitDict) {
  60. var event = document.createEvent(eventName);
  61. if (type !== null) {
  62. initCustomEvent.call(
  63. event,
  64. type,
  65. (eventInitDict || (
  66. // if falsy we can just use defaults
  67. eventInitDict = defaultInitDict
  68. )).bubbles,
  69. eventInitDict.cancelable,
  70. eventInitDict.detail
  71. );
  72. } else {
  73. // no need to put the expando property otherwise
  74. // since an event cannot be initialized twice
  75. // previous case is the most common one anyway
  76. // but if we end up here ... there it goes
  77. event.initCustomEvent = initCustomEvent;
  78. }
  79. return event;
  80. }
  81. // borrowed or attached at runtime
  82. function initCustomEvent(
  83. type, bubbles, cancelable, detail
  84. ) {
  85. this['init' + eventName](type, bubbles, cancelable, detail);
  86. 'detail' in this || (this.detail = detail);
  87. }
  88. // that's it
  89. return CustomEvent;
  90. }(
  91. // is this IE9 or IE10 ?
  92. // where CustomEvent is there
  93. // but not usable as construtor ?
  94. this.CustomEvent ?
  95. // use the CustomEvent interface in such case
  96. 'CustomEvent' : 'Event',
  97. // otherwise the common compatible one
  98. {
  99. bubbles: false,
  100. cancelable: false,
  101. detail: null
  102. }
  103. );
  104. }
  105. /* querySelectorAll polyfill for ie7+ */
  106. if (!document.querySelectorAll) {
  107. document.querySelectorAll = function (selectors) {
  108. var style = document.createElement('style'), elements = [], element;
  109. document.documentElement.firstChild.appendChild(style);
  110. document._qsa = [];
  111. style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
  112. window.scrollBy(0, 0);
  113. style.parentNode.removeChild(style);
  114. while (document._qsa.length) {
  115. element = document._qsa.shift();
  116. element.style.removeAttribute('x-qsa');
  117. elements.push(element);
  118. }
  119. document._qsa = null;
  120. return elements;
  121. };
  122. }
  123. if (!document.querySelector) {
  124. document.querySelector = function (selectors) {
  125. var elements = document.querySelectorAll(selectors);
  126. return (elements.length) ? elements[0] : null;
  127. };
  128. }
  129. /* Innertext shim for firefox https://github.com/duckinator/innerText-polyfill/blob/master/innertext.js */
  130. if ( (!('innerText' in document.createElement('a'))) && ('getSelection' in window) ) {
  131. HTMLElement.prototype.__defineGetter__("innerText", function() {
  132. var selection = window.getSelection(),
  133. ranges = [],
  134. str;
  135. // Save existing selections.
  136. for (var i = 0; i < selection.rangeCount; i++) {
  137. ranges[i] = selection.getRangeAt(i);
  138. }
  139. // Deselect everything.
  140. selection.removeAllRanges();
  141. // Select `el` and all child nodes.
  142. // 'this' is the element .innerText got called on
  143. selection.selectAllChildren(this);
  144. // Get the string representation of the selected nodes.
  145. str = selection.toString();
  146. // Deselect everything. Again.
  147. selection.removeAllRanges();
  148. // Restore all formerly existing selections.
  149. for (var i = 0; i < ranges.length; i++) {
  150. selection.addRange(ranges[i]);
  151. }
  152. // Oh look, this is what we wanted.
  153. // String representation of the element, close to as rendered.
  154. return str;
  155. })
  156. }
  157. },
  158. /**
  159. * Create cookie
  160. *
  161. * ```js
  162. * // Creates cookie for 10 days
  163. * _inbound.utils.createCookie( 'cookie_name', 'value', 10 );
  164. * ```
  165. *
  166. * @param {string} name Name of cookie
  167. * @param {string} value Value of cookie
  168. * @param {string} days Length of storage
  169. */
  170. createCookie: function(name, value, days) {
  171. var expires = "";
  172. if (days) {
  173. var date = new Date();
  174. date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
  175. expires = "; expires=" + date.toGMTString();
  176. }
  177. document.cookie = name + "=" + value + expires + "; path=/";
  178. },
  179. /**
  180. * Read cookie value
  181. *
  182. * ```js
  183. * var cookie = _inbound.utils.readCookie( 'cookie_name' );
  184. * console.log(cookie); // cookie value
  185. * ```
  186. * @param {string} name name of cookie
  187. * @return {string} value of cookie
  188. */
  189. readCookie: function(name) {
  190. var nameEQ = name + "=";
  191. var ca = document.cookie.split(';');
  192. for (var i = 0; i < ca.length; i++) {
  193. var c = ca[i];
  194. while (c.charAt(0) === ' ') {
  195. c = c.substring(1, c.length);
  196. }
  197. if (c.indexOf(nameEQ) === 0) {
  198. return c.substring(nameEQ.length, c.length);
  199. }
  200. }
  201. return null;
  202. },
  203. /**
  204. * Erase cookie
  205. *
  206. * ```js
  207. * // usage:
  208. * _inbound.utils.eraseCookie( 'cookie_name' );
  209. * // deletes 'cookie_name' value
  210. * ```
  211. * @param {string} name name of cookie
  212. * @return {string} value of cookie
  213. */
  214. eraseCookie: function(name) {
  215. this.createCookie(name, "", -1);
  216. },
  217. /* Get All Cookies */
  218. getAllCookies: function() {
  219. var cookies = {};
  220. if (document.cookie && document.cookie !== '') {
  221. var split = document.cookie.split(';');
  222. for (var i = 0; i < split.length; i++) {
  223. var name_value = split[i].split("=");
  224. name_value[0] = name_value[0].replace(/^ /, '');
  225. cookies[decodeURIComponent(name_value[0])] = decodeURIComponent(name_value[1]);
  226. }
  227. }
  228. _inbound.totalStorage('inbound_cookies', cookies); // store cookie data
  229. return cookies;
  230. },
  231. /* Grab URL params and save */
  232. setUrlParams: function() {
  233. var urlParams = {};
  234. (function() {
  235. var e,
  236. d = function(s) {
  237. return decodeURIComponent(s).replace(/\+/g, " ");
  238. },
  239. q = window.location.search.substring(1),
  240. r = /([^&=]+)=?([^&]*)/g;
  241. while (e = r.exec(q)) {
  242. if (e[1].indexOf("[") == "-1")
  243. urlParams[d(e[1])] = d(e[2]);
  244. else {
  245. var b1 = e[1].indexOf("["),
  246. aN = e[1].slice(b1 + 1, e[1].indexOf("]", b1)),
  247. pN = d(e[1].slice(0, b1));
  248. if (typeof urlParams[pN] != "object")
  249. urlParams[d(pN)] = {},
  250. urlParams[d(pN)].length = 0;
  251. if (aN)
  252. urlParams[d(pN)][d(aN)] = d(e[2]);
  253. else
  254. Array.prototype.push.call(urlParams[d(pN)], d(e[2]));
  255. }
  256. }
  257. })();
  258. /* Set Param Cookies */
  259. for (var k in urlParams) {
  260. if (typeof urlParams[k] == "object") {
  261. for (var k2 in urlParams[k])
  262. this.createCookie(k2, urlParams[k][k2], 30);
  263. } else {
  264. this.createCookie(k, urlParams[k], 30);
  265. }
  266. }
  267. /* Set Param LocalStorage */
  268. if (storageSupported) {
  269. var pastParams = _inbound.totalStorage('inbound_url_params') || {};
  270. var params = this.mergeObjs(pastParams, urlParams);
  271. _inbound.totalStorage('inbound_url_params', params); // store cookie data
  272. }
  273. var options = {'option1': 'yo', 'option2': 'woooo'};
  274. _inbound.trigger('url_parameters', urlParams, options);
  275. },
  276. getAllUrlParams: function() {
  277. var get_params = {};
  278. if (storageSupported) {
  279. var get_params = _inbound.totalStorage('inbound_url_params');
  280. }
  281. return get_params;
  282. },
  283. /* Get url param */
  284. getParameterVal: function(name, string) {
  285. return (RegExp(name + '=' + '(.+?)(&|$)').exec(string)||[,false])[1];
  286. },
  287. // Check local storage
  288. // provate browsing safari fix https://github.com/marcuswestin/store.js/issues/42#issuecomment-25274685
  289. checkLocalStorage: function() {
  290. if ('localStorage' in window) {
  291. try {
  292. ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
  293. if (typeof ls == 'undefined' || typeof window.JSON == 'undefined') {
  294. storageSupported = false;
  295. } else {
  296. storageSupported = true;
  297. }
  298. } catch (err) {
  299. storageSupported = false;
  300. }
  301. }
  302. return storageSupported;
  303. /* http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/
  304. var hasStorage;
  305. hasStorage = function() {
  306. var mod, result;
  307. try {
  308. mod = new Date;
  309. localStorage.setItem(mod, mod.toString());
  310. result = localStorage.getItem(mod) === mod.toString();
  311. localStorage.removeItem(mod);
  312. return result;
  313. } catch (_error) {}
  314. };
  315. */
  316. },
  317. /* Add days to datetime */
  318. addDays: function(myDate, days) {
  319. return new Date(myDate.getTime() + days * 24 * 60 * 60 * 1000);
  320. },
  321. GetDate: function() {
  322. var time_now = new Date(),
  323. day = time_now.getDate() + 1;
  324. year = time_now.getFullYear(),
  325. hour = time_now.getHours(),
  326. minutes = time_now.getMinutes(),
  327. seconds = time_now.getSeconds(),
  328. month = time_now.getMonth() + 1;
  329. if (month < 10) {
  330. month = '0' + month;
  331. }
  332. _inbound.debug('Current Date:', function() {
  333. console.log(year + '/' + month + "/" + day + " " + hour + ":" + minutes + ":" + seconds);
  334. });
  335. var datetime = year + '/' + month + "/" + day + " " + hour + ":" + minutes + ":" + seconds;
  336. return datetime;
  337. },
  338. /* Set Expiration Date of Session Logging. LEGACY Not in Use */
  339. SetSessionTimeout: function() {
  340. var session = this.readCookie("lead_session_expire");
  341. //console.log(session_check);
  342. if (!session) {
  343. //_inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
  344. } else {
  345. //_inbound.trigger('session_resume'); // trigger 'inbound_analytics_session_active'
  346. }
  347. var d = new Date();
  348. d.setTime(d.getTime() + 30 * 60 * 1000);
  349. this.createCookie("lead_session_expire", true, d, true); // Set cookie on page load
  350. },
  351. storeReferralData: function() {
  352. //console.log(expire_time);
  353. var d = new Date(),
  354. referrer = document.referrer || "Direct Traffic",
  355. referrer_cookie = _inbound.Utils.readCookie("inbound_referral_site"),
  356. original_src = _inbound.totalStorage('inbound_original_referral');
  357. d.setTime(d.getTime() + 30 * 60 * 1000);
  358. if (!referrer_cookie) {
  359. this.createCookie("inbound_referral_site", referrer, d, true);
  360. }
  361. if (!original_src) {
  362. _inbound.totalStorage('inbound_original_referral', original_src);
  363. }
  364. },
  365. CreateUID: function(length) {
  366. var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split(''),
  367. str = '';
  368. if (!length) {
  369. length = Math.floor(Math.random() * chars.length);
  370. }
  371. for (var i = 0; i < length; i++) {
  372. str += chars[Math.floor(Math.random() * chars.length)];
  373. }
  374. return str;
  375. },
  376. SetUID: function(leadUID) {
  377. /* Set Lead UID */
  378. if (!this.readCookie("wp_lead_uid")) {
  379. var wp_lead_uid = leadUID || this.CreateUID(35);
  380. this.createCookie("wp_lead_uid", wp_lead_uid);
  381. }
  382. },
  383. /* Count number of session visits */
  384. countProperties: function(obj) {
  385. var count = 0;
  386. for (var prop in obj) {
  387. if (obj.hasOwnProperty(prop))
  388. ++count;
  389. }
  390. return count;
  391. },
  392. mergeObjs: function(obj1, obj2) {
  393. var obj3 = {};
  394. for (var attrname in obj1) {
  395. obj3[attrname] = obj1[attrname];
  396. }
  397. for (var attrname in obj2) {
  398. obj3[attrname] = obj2[attrname];
  399. }
  400. return obj3;
  401. },
  402. hasClass: function(className, el) {
  403. var hasClass = false;
  404. if ('classList' in document.documentElement) {
  405. var hasClass = el.classList.contains(className);
  406. } else {
  407. var hasClass = new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className); /* IE Polyfill */
  408. }
  409. return hasClass;
  410. },
  411. addClass: function(className, elem) {
  412. if ('classList' in document.documentElement) {
  413. elem.classList.add(className);
  414. } else {
  415. if (!this.hasClass(elem, className)) {
  416. elem.className += (elem.className ? ' ' : '') + className;
  417. }
  418. }
  419. },
  420. removeClass: function(className, elem) {
  421. if ('classList' in document.documentElement) {
  422. elem.classList.remove(className);
  423. } else {
  424. if (this.hasClass(elem, className)) {
  425. elem.className = elem.className.replace(new RegExp('(^|\\s)*' + className + '(\\s|$)*', 'g'), '');
  426. }
  427. }
  428. },
  429. trim: function(s) {
  430. s = s.replace(/(^\s*)|(\s*$)/gi, "");
  431. s = s.replace(/[ ]{2,}/gi, " ");
  432. s = s.replace(/\n /, "\n");
  433. return s;
  434. },
  435. ajaxPolyFill: function() {
  436. if (typeof XMLHttpRequest !== 'undefined') {
  437. return new XMLHttpRequest();
  438. }
  439. var versions = [
  440. "MSXML2.XmlHttp.5.0",
  441. "MSXML2.XmlHttp.4.0",
  442. "MSXML2.XmlHttp.3.0",
  443. "MSXML2.XmlHttp.2.0",
  444. "Microsoft.XmlHttp"
  445. ];
  446. var xhr;
  447. for (var i = 0; i < versions.length; i++) {
  448. try {
  449. xhr = new ActiveXObject(versions[i]);
  450. break;
  451. } catch (e) {}
  452. }
  453. return xhr;
  454. },
  455. ajaxSendData: function(url, callback, method, data, sync) {
  456. var x = this.ajaxPolyFill();
  457. x.open(method, url, sync);
  458. x.onreadystatechange = function() {
  459. if (x.readyState == 4) {
  460. callback(x.responseText)
  461. }
  462. };
  463. if (method == 'POST') {
  464. x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  465. }
  466. x.send(data);
  467. },
  468. ajaxGet: function(url, data, callback, sync) {
  469. var query = [];
  470. for (var key in data) {
  471. query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
  472. }
  473. this.ajaxSendData(url + '?' + query.join('&'), callback, 'GET', null, sync)
  474. },
  475. ajaxPost: function(url, data, callback, sync) {
  476. var query = [];
  477. for (var key in data) {
  478. query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
  479. }
  480. this.ajaxSendData(url, callback, 'POST', query.join('&'), sync)
  481. },
  482. makeRequest: function(url, data) {
  483. if (window.XMLHttpRequest) { // Mozilla, Safari, ...
  484. httpRequest = new XMLHttpRequest();
  485. } else if (window.ActiveXObject) { // IE
  486. try {
  487. httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
  488. } catch (e) {
  489. try {
  490. httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
  491. } catch (e) {}
  492. }
  493. }
  494. if (!httpRequest) {
  495. alert('Giving up :( Cannot create an XMLHTTP instance');
  496. return false;
  497. }
  498. httpRequest.onreadystatechange = _inbound.LeadsAPI.alertContents;
  499. httpRequest.open('GET', url);
  500. httpRequest.send(data);
  501. },
  502. domReady: function(win, fn) {
  503. var done = false,
  504. top = true,
  505. doc = win.document,
  506. root = doc.documentElement,
  507. add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
  508. rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
  509. pre = doc.addEventListener ? '' : 'on',
  510. init = function(e) {
  511. if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
  512. (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
  513. if (!done && (done = true)) fn.call(win, e.type || e);
  514. },
  515. poll = function() {
  516. try {
  517. root.doScroll('left');
  518. } catch (e) {
  519. setTimeout(poll, 50);
  520. return;
  521. }
  522. init('poll');
  523. };
  524. if (doc.readyState == 'complete') fn.call(win, 'lazy');
  525. else {
  526. if (doc.createEventObject && root.doScroll) {
  527. try {
  528. top = !win.frameElement;
  529. } catch (e) {}
  530. if (top) poll();
  531. }
  532. doc[add](pre + 'DOMContentLoaded', init, false);
  533. doc[add](pre + 'readystatechange', init, false);
  534. win[add](pre + 'load', init, false);
  535. }
  536. },
  537. /* Cross-browser event listening */
  538. addListener: function(element, eventName, listener) {
  539. //console.log(eventName);
  540. //console.log(listener);
  541. if (element.addEventListener) {
  542. element.addEventListener(eventName, listener, false);
  543. } else if (element.attachEvent) {
  544. element.attachEvent("on" + eventName, listener);
  545. } else {
  546. element['on' + eventName] = listener;
  547. }
  548. },
  549. removeListener: function(element, eventName, listener) {
  550. if (element.removeEventListener) {
  551. element.removeEventListener(eventName, listener, false);
  552. } else if (element.detachEvent) {
  553. element.detachEvent("on" + eventName, listener);
  554. } else {
  555. element["on" + eventName] = null;
  556. }
  557. },
  558. /*
  559. * Throttle function borrowed from:
  560. * Underscore.js 1.5.2
  561. * http://underscorejs.org
  562. * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
  563. * Underscore may be freely distributed under the MIT license.
  564. */
  565. throttle: function (func, wait) {
  566. var context, args, result;
  567. var timeout = null;
  568. var previous = 0;
  569. var later = function() {
  570. previous = new Date;
  571. timeout = null;
  572. result = func.apply(context, args);
  573. };
  574. return function() {
  575. var now = new Date;
  576. if (!previous) previous = now;
  577. var remaining = wait - (now - previous);
  578. context = this;
  579. args = arguments;
  580. if (remaining <= 0) {
  581. clearTimeout(timeout);
  582. timeout = null;
  583. previous = now;
  584. result = func.apply(context, args);
  585. } else if (!timeout) {
  586. timeout = setTimeout(later, remaining);
  587. }
  588. return result;
  589. };
  590. },
  591. /*
  592. * Determine which version of GA is being used
  593. * "ga", "_gaq", and "dataLayer" are the possible globals
  594. */
  595. checkTypeofGA: function() {
  596. if (typeof ga === "function") {
  597. universalGA = true;
  598. }
  599. if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
  600. classicGA = true;
  601. }
  602. if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
  603. googleTagManager = true;
  604. }
  605. }
  606. };
  607. return _inbound;
  608. })(_inbound || {});