PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/HelloWorld/platforms/ios/www/js/mixpanel.js

https://github.com/ABensick/cordova
JavaScript | 3027 lines | 2149 code | 250 blank | 628 comment | 386 complexity | f7c6e4057d25b02392e543c3814517ab MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * Mixpanel JS Library v2.2.1
  3. *
  4. * Copyright 2012, Mixpanel, Inc. All Rights Reserved
  5. * http://mixpanel.com/
  6. *
  7. * Includes portions of Underscore.js
  8. * http://documentcloud.github.com/underscore/
  9. * (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
  10. * Released under the MIT License.
  11. */
  12. // ==ClosureCompiler==
  13. // @compilation_level ADVANCED_OPTIMIZATIONS
  14. // @output_file_name mixpanel-2.2.min.js
  15. // ==/ClosureCompiler==
  16. /*
  17. Will export window.mixpanel
  18. */
  19. /*
  20. SIMPLE STYLE GUIDE:
  21. this.x == public function
  22. this._x == internal - only use within this file
  23. this.__x == private - only use within the class
  24. Globals should be all caps
  25. */
  26. (function (mixpanel) {
  27. /*
  28. * Saved references to long variable names, so that closure compiler can
  29. * minimize file size.
  30. */
  31. var ArrayProto = Array.prototype
  32. , ObjProto = Object.prototype
  33. , slice = ArrayProto.slice
  34. , toString = ObjProto.toString
  35. , hasOwnProperty = ObjProto.hasOwnProperty
  36. , windowConsole = window.console
  37. , navigator = window.navigator
  38. , document = window.document
  39. , userAgent = navigator.userAgent;
  40. /*
  41. * Constants
  42. */
  43. /** @const */ var PRIMARY_INSTANCE_NAME = "mixpanel"
  44. /** @const */ , SET_QUEUE_KEY = "__mps"
  45. /** @const */ , SET_ONCE_QUEUE_KEY = "__mpso"
  46. /** @const */ , ADD_QUEUE_KEY = "__mpa"
  47. /** @const */ , APPEND_QUEUE_KEY = "__mpap"
  48. /** @const */ , SET_ACTION = "$set"
  49. /** @const */ , SET_ONCE_ACTION = "$set_once"
  50. /** @const */ , ADD_ACTION = "$add"
  51. /** @const */ , APPEND_ACTION = "$append"
  52. // This key is deprecated, but we want to check for it to see whether aliasing is allowed.
  53. /** @const */ , PEOPLE_DISTINCT_ID_KEY = "$people_distinct_id"
  54. /** @const */ , ALIAS_ID_KEY = "__alias"
  55. /** @const */ , RESERVED_PROPERTIES = [SET_QUEUE_KEY, SET_ONCE_QUEUE_KEY, ADD_QUEUE_KEY, APPEND_QUEUE_KEY, PEOPLE_DISTINCT_ID_KEY, ALIAS_ID_KEY];
  56. /*
  57. * Dynamic... constants? Is that an oxymoron?
  58. */
  59. var HTTP_PROTOCOL = (("https:" == document.location.protocol) ? "https://" : "http://")
  60. , SNIPPET_VERSION = (mixpanel && mixpanel['__SV']) || 0
  61. // http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/
  62. // https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#withCredentials
  63. , USE_XHR = (window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest())
  64. // IE<10 does not support cross-origin XHR's but script tags
  65. // with defer won't block window.onload; ENQUEUE_REQUESTS
  66. // should only be true for Opera<12
  67. , ENQUEUE_REQUESTS = !USE_XHR && (userAgent.indexOf('MSIE') == -1);
  68. /*
  69. * Closure-level globals
  70. */
  71. var _ = {}
  72. , DEBUG = false
  73. , DEFAULT_CONFIG = {
  74. "api_host": HTTP_PROTOCOL + 'api.mixpanel.com'
  75. , "cross_subdomain_cookie": true
  76. , "cookie_name": ""
  77. , "loaded": function() {}
  78. , "store_google": true
  79. , "save_referrer": true
  80. , "test": false
  81. , "verbose": false
  82. , "img": false
  83. , "track_pageview": true
  84. , "debug": false
  85. , "track_links_timeout": 300
  86. , "cookie_expiration": 365
  87. , "upgrade": false
  88. , "disable_cookie": false
  89. , "secure_cookie": false
  90. , "ip": true
  91. }
  92. , DOM_LOADED = false;
  93. // UNDERSCORE
  94. // Embed part of the Underscore Library
  95. (function() {
  96. var nativeForEach = ArrayProto.forEach,
  97. nativeIndexOf = ArrayProto.indexOf,
  98. nativeIsArray = Array.isArray,
  99. breaker = {};
  100. /**
  101. * @param {*=} obj
  102. * @param {function(...[*])=} iterator
  103. * @param {Object=} context
  104. */
  105. var each = _.each = function(obj, iterator, context) {
  106. if (obj == null) return;
  107. if (nativeForEach && obj.forEach === nativeForEach) {
  108. obj.forEach(iterator, context);
  109. } else if (obj.length === +obj.length) {
  110. for (var i = 0, l = obj.length; i < l; i++) {
  111. if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
  112. }
  113. } else {
  114. for (var key in obj) {
  115. if (hasOwnProperty.call(obj, key)) {
  116. if (iterator.call(context, obj[key], key, obj) === breaker) return;
  117. }
  118. }
  119. }
  120. };
  121. _.extend = function(obj) {
  122. each(slice.call(arguments, 1), function(source) {
  123. for (var prop in source) {
  124. if (source[prop] !== void 0) obj[prop] = source[prop];
  125. }
  126. });
  127. return obj;
  128. };
  129. _.isArray = nativeIsArray || function(obj) {
  130. return toString.call(obj) === '[object Array]';
  131. };
  132. // from a comment on http://dbj.org/dbj/?p=286
  133. // fails on only one very rare and deliberate custom object:
  134. // var bomb = { toString : undefined, valueOf: function(o) { return "function BOMBA!"; }};
  135. _.isFunction = function (f) {
  136. try {
  137. return /^\s*\bfunction\b/.test(f);
  138. } catch (x) {
  139. return false;
  140. }
  141. };
  142. _.isArguments = function(obj) {
  143. return !!(obj && hasOwnProperty.call(obj, 'callee'));
  144. };
  145. _.toArray = function(iterable) {
  146. if (!iterable) return [];
  147. if (iterable.toArray) return iterable.toArray();
  148. if (_.isArray(iterable)) return slice.call(iterable);
  149. if (_.isArguments(iterable)) return slice.call(iterable);
  150. return _.values(iterable);
  151. };
  152. _.values = function(obj) {
  153. var results = [];
  154. if (obj == null) return results;
  155. each(obj, function(value) {
  156. results[results.length] = value;
  157. });
  158. return results;
  159. };
  160. _.identity = function(value) {
  161. return value;
  162. };
  163. _.include = function(obj, target) {
  164. var found = false;
  165. if (obj == null) return found;
  166. if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
  167. each(obj, function(value) {
  168. if (found || (found = (value === target))) { return breaker; }
  169. });
  170. return found;
  171. };
  172. _.includes = function(str, needle) {
  173. return str.indexOf(needle) !== -1;
  174. };
  175. })();
  176. // Underscore Addons
  177. _.inherit = function(subclass, superclass) {
  178. subclass.prototype = new superclass();
  179. subclass.prototype.constructor = subclass;
  180. subclass.superclass = superclass.prototype;
  181. return subclass;
  182. };
  183. _.isObject = function(obj) {
  184. return (obj === Object(obj) && !_.isArray(obj));
  185. };
  186. _.isEmptyObject = function(obj) {
  187. if (_.isObject(obj)) {
  188. for (var key in obj) {
  189. if (hasOwnProperty.call(obj, key)) {
  190. return false;
  191. }
  192. }
  193. return true;
  194. }
  195. return false;
  196. };
  197. _.isUndefined = function(obj) {
  198. return obj === void 0;
  199. };
  200. _.isString = function(obj) {
  201. return toString.call(obj) == '[object String]';
  202. };
  203. _.isDate = function(obj) {
  204. return toString.call(obj) == '[object Date]';
  205. };
  206. _.isNumber = function(obj) {
  207. return toString.call(obj) == '[object Number]';
  208. };
  209. _.encodeDates = function(obj) {
  210. _.each(obj, function(v, k) {
  211. if (_.isDate(v)) {
  212. obj[k] = _.formatDate(v);
  213. } else if (_.isObject(v)) {
  214. obj[k] = _.encodeDates(v); // recurse
  215. }
  216. });
  217. return obj;
  218. };
  219. _.formatDate = function(d) {
  220. // YYYY-MM-DDTHH:MM:SS in UTC
  221. function pad(n) {return n < 10 ? '0' + n : n}
  222. return d.getUTCFullYear() + '-'
  223. + pad(d.getUTCMonth() + 1) + '-'
  224. + pad(d.getUTCDate()) + 'T'
  225. + pad(d.getUTCHours()) + ':'
  226. + pad(d.getUTCMinutes()) + ':'
  227. + pad(d.getUTCSeconds());
  228. };
  229. _.strip_empty_properties = function(p) {
  230. var ret = {};
  231. _.each(p, function(v, k) {
  232. if (_.isString(v) && v.length > 0) { ret[k] = v; }
  233. });
  234. return ret;
  235. };
  236. /*
  237. * this function returns a copy of object after truncating it. If
  238. * passed an Array or Object it will iterate through obj and
  239. * truncate all the values recursively.
  240. */
  241. _.truncate = function(obj, length) {
  242. var ret;
  243. if (typeof(obj) === "string") {
  244. ret = obj.slice(0, length);
  245. } else if (_.isArray(obj)) {
  246. ret = [];
  247. _.each(obj, function(val) {
  248. ret.push(_.truncate(val, length));
  249. });
  250. } else if (_.isObject(obj)) {
  251. ret = {};
  252. _.each(obj, function(val, key) {
  253. ret[key] = _.truncate(val, length);
  254. });
  255. } else {
  256. ret = obj;
  257. }
  258. return ret;
  259. };
  260. _.JSONEncode = (function() {
  261. return function(mixed_val) {
  262. var indent;
  263. var value = mixed_val;
  264. var i;
  265. var quote = function (string) {
  266. var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
  267. var meta = { // table of character substitutions
  268. '\b': '\\b',
  269. '\t': '\\t',
  270. '\n': '\\n',
  271. '\f': '\\f',
  272. '\r': '\\r',
  273. '"' : '\\"',
  274. '\\': '\\\\'
  275. };
  276. escapable.lastIndex = 0;
  277. return escapable.test(string) ?
  278. '"' + string.replace(escapable, function (a) {
  279. var c = meta[a];
  280. return typeof c === 'string' ? c :
  281. '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  282. }) + '"' :
  283. '"' + string + '"';
  284. };
  285. var str = function(key, holder) {
  286. var gap = '';
  287. var indent = ' ';
  288. var i = 0; // The loop counter.
  289. var k = ''; // The member key.
  290. var v = ''; // The member value.
  291. var length = 0;
  292. var mind = gap;
  293. var partial = [];
  294. var value = holder[key];
  295. // If the value has a toJSON method, call it to obtain a replacement value.
  296. if (value && typeof value === 'object' &&
  297. typeof value.toJSON === 'function') {
  298. value = value.toJSON(key);
  299. }
  300. // What happens next depends on the value's type.
  301. switch (typeof value) {
  302. case 'string':
  303. return quote(value);
  304. case 'number':
  305. // JSON numbers must be finite. Encode non-finite numbers as null.
  306. return isFinite(value) ? String(value) : 'null';
  307. case 'boolean':
  308. case 'null':
  309. // If the value is a boolean or null, convert it to a string. Note:
  310. // typeof null does not produce 'null'. The case is included here in
  311. // the remote chance that this gets fixed someday.
  312. return String(value);
  313. case 'object':
  314. // If the type is 'object', we might be dealing with an object or an array or
  315. // null.
  316. // Due to a specification blunder in ECMAScript, typeof null is 'object',
  317. // so watch out for that case.
  318. if (!value) {
  319. return 'null';
  320. }
  321. // Make an array to hold the partial results of stringifying this object value.
  322. gap += indent;
  323. partial = [];
  324. // Is the value an array?
  325. if (toString.apply(value) === '[object Array]') {
  326. // The value is an array. Stringify every element. Use null as a placeholder
  327. // for non-JSON values.
  328. length = value.length;
  329. for (i = 0; i < length; i += 1) {
  330. partial[i] = str(i, value) || 'null';
  331. }
  332. // Join all of the elements together, separated with commas, and wrap them in
  333. // brackets.
  334. v = partial.length === 0 ? '[]' :
  335. gap ? '[\n' + gap +
  336. partial.join(',\n' + gap) + '\n' +
  337. mind + ']' :
  338. '[' + partial.join(',') + ']';
  339. gap = mind;
  340. return v;
  341. }
  342. // Iterate through all of the keys in the object.
  343. for (k in value) {
  344. if (hasOwnProperty.call(value, k)) {
  345. v = str(k, value);
  346. if (v) {
  347. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  348. }
  349. }
  350. }
  351. // Join all of the member texts together, separated with commas,
  352. // and wrap them in braces.
  353. v = partial.length === 0 ? '{}' :
  354. gap ? '{' + partial.join(',') + '' +
  355. mind + '}' : '{' + partial.join(',') + '}';
  356. gap = mind;
  357. return v;
  358. }
  359. };
  360. // Make a fake root object containing our value under the key of ''.
  361. // Return the result of stringifying the value.
  362. return str('', {
  363. '': value
  364. });
  365. };
  366. })();
  367. _.JSONDecode = (function() { // https://github.com/douglascrockford/JSON-js/blob/master/json_parse.js
  368. var at, // The index of the current character
  369. ch, // The current character
  370. escapee = {
  371. '"': '"',
  372. '\\': '\\',
  373. '/': '/',
  374. 'b': '\b',
  375. 'f': '\f',
  376. 'n': '\n',
  377. 'r': '\r',
  378. 't': '\t'
  379. },
  380. text,
  381. error = function (m) {
  382. throw {
  383. name: 'SyntaxError',
  384. message: m,
  385. at: at,
  386. text: text
  387. };
  388. },
  389. next = function (c) {
  390. // If a c parameter is provided, verify that it matches the current character.
  391. if (c && c !== ch) {
  392. error("Expected '" + c + "' instead of '" + ch + "'");
  393. }
  394. // Get the next character. When there are no more characters,
  395. // return the empty string.
  396. ch = text.charAt(at);
  397. at += 1;
  398. return ch;
  399. },
  400. number = function () {
  401. // Parse a number value.
  402. var number,
  403. string = '';
  404. if (ch === '-') {
  405. string = '-';
  406. next('-');
  407. }
  408. while (ch >= '0' && ch <= '9') {
  409. string += ch;
  410. next();
  411. }
  412. if (ch === '.') {
  413. string += '.';
  414. while (next() && ch >= '0' && ch <= '9') {
  415. string += ch;
  416. }
  417. }
  418. if (ch === 'e' || ch === 'E') {
  419. string += ch;
  420. next();
  421. if (ch === '-' || ch === '+') {
  422. string += ch;
  423. next();
  424. }
  425. while (ch >= '0' && ch <= '9') {
  426. string += ch;
  427. next();
  428. }
  429. }
  430. number = +string;
  431. if (!isFinite(number)) {
  432. error("Bad number");
  433. } else {
  434. return number;
  435. }
  436. },
  437. string = function () {
  438. // Parse a string value.
  439. var hex,
  440. i,
  441. string = '',
  442. uffff;
  443. // When parsing for string values, we must look for " and \ characters.
  444. if (ch === '"') {
  445. while (next()) {
  446. if (ch === '"') {
  447. next();
  448. return string;
  449. }
  450. if (ch === '\\') {
  451. next();
  452. if (ch === 'u') {
  453. uffff = 0;
  454. for (i = 0; i < 4; i += 1) {
  455. hex = parseInt(next(), 16);
  456. if (!isFinite(hex)) {
  457. break;
  458. }
  459. uffff = uffff * 16 + hex;
  460. }
  461. string += String.fromCharCode(uffff);
  462. } else if (typeof escapee[ch] === 'string') {
  463. string += escapee[ch];
  464. } else {
  465. break;
  466. }
  467. } else {
  468. string += ch;
  469. }
  470. }
  471. }
  472. error("Bad string");
  473. },
  474. white = function () {
  475. // Skip whitespace.
  476. while (ch && ch <= ' ') {
  477. next();
  478. }
  479. },
  480. word = function () {
  481. // true, false, or null.
  482. switch (ch) {
  483. case 't':
  484. next('t');
  485. next('r');
  486. next('u');
  487. next('e');
  488. return true;
  489. case 'f':
  490. next('f');
  491. next('a');
  492. next('l');
  493. next('s');
  494. next('e');
  495. return false;
  496. case 'n':
  497. next('n');
  498. next('u');
  499. next('l');
  500. next('l');
  501. return null;
  502. }
  503. error("Unexpected '" + ch + "'");
  504. },
  505. value, // Placeholder for the value function.
  506. array = function () {
  507. // Parse an array value.
  508. var array = [];
  509. if (ch === '[') {
  510. next('[');
  511. white();
  512. if (ch === ']') {
  513. next(']');
  514. return array; // empty array
  515. }
  516. while (ch) {
  517. array.push(value());
  518. white();
  519. if (ch === ']') {
  520. next(']');
  521. return array;
  522. }
  523. next(',');
  524. white();
  525. }
  526. }
  527. error("Bad array");
  528. },
  529. object = function () {
  530. // Parse an object value.
  531. var key,
  532. object = {};
  533. if (ch === '{') {
  534. next('{');
  535. white();
  536. if (ch === '}') {
  537. next('}');
  538. return object; // empty object
  539. }
  540. while (ch) {
  541. key = string();
  542. white();
  543. next(':');
  544. if (Object.hasOwnProperty.call(object, key)) {
  545. error('Duplicate key "' + key + '"');
  546. }
  547. object[key] = value();
  548. white();
  549. if (ch === '}') {
  550. next('}');
  551. return object;
  552. }
  553. next(',');
  554. white();
  555. }
  556. }
  557. error("Bad object");
  558. };
  559. value = function () {
  560. // Parse a JSON value. It could be an object, an array, a string,
  561. // a number, or a word.
  562. white();
  563. switch (ch) {
  564. case '{':
  565. return object();
  566. case '[':
  567. return array();
  568. case '"':
  569. return string();
  570. case '-':
  571. return number();
  572. default:
  573. return ch >= '0' && ch <= '9' ? number() : word();
  574. }
  575. };
  576. // Return the json_parse function. It will have access to all of the
  577. // above functions and variables.
  578. return function (source) {
  579. var result;
  580. text = source;
  581. at = 0;
  582. ch = ' ';
  583. result = value();
  584. white();
  585. if (ch) {
  586. error("Syntax error");
  587. }
  588. return result;
  589. };
  590. })();
  591. _.base64Encode = function(data) {
  592. var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  593. var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc="", tmp_arr = [];
  594. if (!data) {
  595. return data;
  596. }
  597. data = _.utf8Encode(data);
  598. do { // pack three octets into four hexets
  599. o1 = data.charCodeAt(i++);
  600. o2 = data.charCodeAt(i++);
  601. o3 = data.charCodeAt(i++);
  602. bits = o1<<16 | o2<<8 | o3;
  603. h1 = bits>>18 & 0x3f;
  604. h2 = bits>>12 & 0x3f;
  605. h3 = bits>>6 & 0x3f;
  606. h4 = bits & 0x3f;
  607. // use hexets to index into b64, and append result to encoded string
  608. tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
  609. } while (i < data.length);
  610. enc = tmp_arr.join('');
  611. switch( data.length % 3 ){
  612. case 1:
  613. enc = enc.slice(0, -2) + '==';
  614. break;
  615. case 2:
  616. enc = enc.slice(0, -1) + '=';
  617. break;
  618. }
  619. return enc;
  620. };
  621. _.utf8Encode = function(string) {
  622. string = (string+'').replace(/\r\n/g, "\n").replace(/\r/g, "\n");
  623. var utftext = "",
  624. start,
  625. end;
  626. var stringl = 0,
  627. n;
  628. start = end = 0;
  629. stringl = string.length;
  630. for (n = 0; n < stringl; n++) {
  631. var c1 = string.charCodeAt(n);
  632. var enc = null;
  633. if (c1 < 128) {
  634. end++;
  635. } else if((c1 > 127) && (c1 < 2048)) {
  636. enc = String.fromCharCode((c1 >> 6) | 192, (c1 & 63) | 128);
  637. } else {
  638. enc = String.fromCharCode((c1 >> 12) | 224, ((c1 >> 6) & 63) | 128, (c1 & 63) | 128);
  639. }
  640. if (enc !== null) {
  641. if (end > start) {
  642. utftext += string.substring(start, end);
  643. }
  644. utftext += enc;
  645. start = end = n+1;
  646. }
  647. }
  648. if (end > start) {
  649. utftext += string.substring(start, string.length);
  650. }
  651. return utftext;
  652. };
  653. _.UUID = (function() {
  654. // Time/ticks information
  655. // 1*new Date() is a cross browser version of Date.now()
  656. var T = function() {
  657. var d = 1*new Date()
  658. , i = 0;
  659. // this while loop figures how many browser ticks go by
  660. // before 1*new Date() returns a new number, ie the amount
  661. // of ticks that go by per millisecond
  662. while (d == 1*new Date()) { i++; }
  663. return d.toString(16) + i.toString(16);
  664. };
  665. // Math.Random entropy
  666. var R = function() {
  667. return Math.random().toString(16).replace('.','');
  668. };
  669. // User agent entropy
  670. // This function takes the user agent string, and then xors
  671. // together each sequence of 8 bytes. This produces a final
  672. // sequence of 8 bytes which it returns as hex.
  673. var UA = function(n) {
  674. var ua = userAgent, i, ch, buffer = [], ret = 0;
  675. function xor(result, byte_array) {
  676. var j, tmp = 0;
  677. for (j = 0; j < byte_array.length; j++) {
  678. tmp |= (buffer[j] << j*8);
  679. }
  680. return result ^ tmp;
  681. }
  682. for (i = 0; i < ua.length; i++) {
  683. ch = ua.charCodeAt(i);
  684. buffer.unshift(ch & 0xFF);
  685. if (buffer.length >= 4) {
  686. ret = xor(ret, buffer);
  687. buffer = [];
  688. }
  689. }
  690. if (buffer.length > 0) { ret = xor(ret, buffer); }
  691. return ret.toString(16);
  692. };
  693. return function() {
  694. var se = (screen.height*screen.width).toString(16);
  695. return (T()+"-"+R()+"-"+UA()+"-"+se+"-"+T());
  696. };
  697. })();
  698. // _.isBlockedUA()
  699. // This is to block various web spiders from executing our JS and
  700. // sending false tracking data
  701. _.isBlockedUA = function(ua) {
  702. if (/(google web preview|baiduspider|yandexbot|bingbot|googlebot|yahoo! slurp)/i.test(ua)) {
  703. return true;
  704. }
  705. return false;
  706. };
  707. /**
  708. * @param {Object=} formdata
  709. * @param {string=} arg_separator
  710. */
  711. _.HTTPBuildQuery = function(formdata, arg_separator) {
  712. var key, use_val, use_key, tmp_arr = [];
  713. if (typeof(arg_separator) === "undefined") {
  714. arg_separator = '&';
  715. }
  716. _.each(formdata, function(val, key) {
  717. use_val = encodeURIComponent(val.toString());
  718. use_key = encodeURIComponent(key);
  719. tmp_arr[tmp_arr.length] = use_key + '=' + use_val;
  720. });
  721. return tmp_arr.join(arg_separator);
  722. };
  723. _.getQueryParam = function(url, param) {
  724. // Expects a raw URL
  725. param = param.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
  726. var regexS = "[\\?&]" + param + "=([^&#]*)",
  727. regex = new RegExp( regexS ),
  728. results = regex.exec(url);
  729. if (results === null || (results && typeof(results[1]) !== 'string' && results[1].length)) {
  730. return '';
  731. } else {
  732. return decodeURIComponent(results[1]).replace(/\+/g, ' ');
  733. }
  734. };
  735. // _.cookie
  736. // Methods partially borrowed from quirksmode.org/js/cookies.html
  737. _.cookie = {
  738. get: function(name) {
  739. var nameEQ = name + "=";
  740. var ca = document.cookie.split(';');
  741. for(var i=0;i < ca.length;i++) {
  742. var c = ca[i];
  743. while (c.charAt(0)==' ') c = c.substring(1,c.length);
  744. if (c.indexOf(nameEQ) == 0) return decodeURIComponent(c.substring(nameEQ.length,c.length));
  745. }
  746. return null;
  747. },
  748. parse: function(name) {
  749. var cookie;
  750. try {
  751. cookie = _.JSONDecode(_.cookie.get(name)) || {};
  752. } catch (err) {}
  753. return cookie;
  754. },
  755. set: function(name, value, days, cross_subdomain, is_secure) {
  756. var cdomain = "", expires = "", secure = "";
  757. if (cross_subdomain) {
  758. var matches = document.location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i)
  759. , domain = matches ? matches[0] : '';
  760. cdomain = ((domain) ? "; domain=." + domain : "");
  761. }
  762. if (days) {
  763. var date = new Date();
  764. date.setTime(date.getTime()+(days*24*60*60*1000));
  765. expires = "; expires=" + date.toGMTString();
  766. }
  767. if (is_secure) {
  768. secure = "; secure";
  769. }
  770. document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/" + cdomain + secure;
  771. },
  772. remove: function(name, cross_subdomain) {
  773. _.cookie.set(name, '', -1, cross_subdomain);
  774. }
  775. };
  776. _.register_event = (function() {
  777. // written by Dean Edwards, 2005
  778. // with input from Tino Zijdel - crisp@xs4all.nl
  779. // with input from Carl Sverre - mail@carlsverre.com
  780. // with input from Mixpanel
  781. // http://dean.edwards.name/weblog/2005/10/add-event/
  782. // https://gist.github.com/1930440
  783. /**
  784. * @param {Object} element
  785. * @param {string} type
  786. * @param {function(...[*])} handler
  787. * @param {boolean=} oldSchool
  788. */
  789. var register_event = function(element, type, handler, oldSchool) {
  790. if (!element) {
  791. console.error("No valid element provided to register_event");
  792. return;
  793. }
  794. if (element.addEventListener && !oldSchool) {
  795. element.addEventListener(type, handler, false);
  796. } else {
  797. var ontype = 'on' + type;
  798. var old_handler = element[ontype]; // can be undefined
  799. element[ontype] = makeHandler(element, handler, old_handler);
  800. }
  801. };
  802. function makeHandler(element, new_handler, old_handlers) {
  803. var handler = function(event) {
  804. event = event || fixEvent(window.event);
  805. // this basically happens in firefox whenever another script
  806. // overwrites the onload callback and doesn't pass the event
  807. // object to previously defined callbacks. All the browsers
  808. // that don't define window.event implement addEventListener
  809. // so the dom_loaded handler will still be fired as usual.
  810. if (!event) { return undefined; }
  811. var ret = true;
  812. var old_result, new_result;
  813. if (_.isFunction(old_handlers)) {
  814. old_result = old_handlers(event);
  815. }
  816. new_result = new_handler.call(element, event);
  817. if ((false === old_result) || (false === new_result)) {
  818. ret = false;
  819. }
  820. return ret;
  821. };
  822. return handler;
  823. };
  824. function fixEvent(event) {
  825. if (event) {
  826. event.preventDefault = fixEvent.preventDefault;
  827. event.stopPropagation = fixEvent.stopPropagation;
  828. }
  829. return event;
  830. };
  831. fixEvent.preventDefault = function() {
  832. this.returnValue = false;
  833. };
  834. fixEvent.stopPropagation = function() {
  835. this.cancelBubble = true;
  836. };
  837. return register_event;
  838. })();
  839. _.dom_query = (function() {
  840. /* document.getElementsBySelector(selector)
  841. - returns an array of element objects from the current document
  842. matching the CSS selector. Selectors can contain element names,
  843. class names and ids and can be nested. For example:
  844. elements = document.getElementsBySelector('div#main p a.external')
  845. Will return an array of all 'a' elements with 'external' in their
  846. class attribute that are contained inside 'p' elements that are
  847. contained inside the 'div' element which has id="main"
  848. New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
  849. See http://www.w3.org/TR/css3-selectors/#attribute-selectors
  850. Version 0.4 - Simon Willison, March 25th 2003
  851. -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6, Internet Explorer 5 on Windows
  852. -- Opera 7 fails
  853. Version 0.5 - Carl Sverre, Jan 7th 2013
  854. -- Now uses jQuery-esque `hasClass` for testing class name
  855. equality. This fixes a bug related to '-' characters being
  856. considered not part of a 'word' in regex.
  857. */
  858. function getAllChildren(e) {
  859. // Returns all children of element. Workaround required for IE5/Windows. Ugh.
  860. return e.all ? e.all : e.getElementsByTagName('*');
  861. }
  862. var bad_whitespace = /[\t\r\n]/g;
  863. function hasClass(elem, selector) {
  864. var className = " " + selector + " ";
  865. return ((" " + elem.className + " ").replace(bad_whitespace, " ").indexOf(className) >= 0);
  866. }
  867. function getElementsBySelector(selector) {
  868. // Attempt to fail gracefully in lesser browsers
  869. if (!document.getElementsByTagName) {
  870. return new Array();
  871. }
  872. // Split selector in to tokens
  873. var tokens = selector.split(' ');
  874. var token;
  875. var currentContext = new Array(document);
  876. for (var i = 0; i < tokens.length; i++) {
  877. token = tokens[i].replace(/^\s+/,'').replace(/\s+$/,'');
  878. if (token.indexOf('#') > -1) {
  879. // Token is an ID selector
  880. var bits = token.split('#');
  881. var tagName = bits[0];
  882. var id = bits[1];
  883. var element = document.getElementById(id);
  884. if (!element || (tagName && element.nodeName.toLowerCase() != tagName)) {
  885. // element not found or tag with that ID not found, return false
  886. return new Array();
  887. }
  888. // Set currentContext to contain just this element
  889. currentContext = new Array(element);
  890. continue; // Skip to next token
  891. }
  892. if (token.indexOf('.') > -1) {
  893. // Token contains a class selector
  894. var bits = token.split('.');
  895. var tagName = bits[0];
  896. var className = bits[1];
  897. if (!tagName) {
  898. tagName = '*';
  899. }
  900. // Get elements matching tag, filter them for class selector
  901. var found = new Array;
  902. var foundCount = 0;
  903. for (var h = 0; h < currentContext.length; h++) {
  904. var elements;
  905. if (tagName == '*') {
  906. elements = getAllChildren(currentContext[h]);
  907. } else {
  908. elements = currentContext[h].getElementsByTagName(tagName);
  909. }
  910. for (var j = 0; j < elements.length; j++) {
  911. found[foundCount++] = elements[j];
  912. }
  913. }
  914. currentContext = new Array;
  915. var currentContextIndex = 0;
  916. for (var k = 0; k < found.length; k++) {
  917. if (found[k].className
  918. && _.isString(found[k].className) // some SVG elements have classNames which are not strings
  919. && hasClass(found[k], className)
  920. ) {
  921. currentContext[currentContextIndex++] = found[k];
  922. }
  923. }
  924. continue; // Skip to next token
  925. }
  926. // Code to deal with attribute selectors
  927. if (token.match(/^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/)) {
  928. var tagName = RegExp.$1;
  929. var attrName = RegExp.$2;
  930. var attrOperator = RegExp.$3;
  931. var attrValue = RegExp.$4;
  932. if (!tagName) {
  933. tagName = '*';
  934. }
  935. // Grab all of the tagName elements within current context
  936. var found = new Array;
  937. var foundCount = 0;
  938. for (var h = 0; h < currentContext.length; h++) {
  939. var elements;
  940. if (tagName == '*') {
  941. elements = getAllChildren(currentContext[h]);
  942. } else {
  943. elements = currentContext[h].getElementsByTagName(tagName);
  944. }
  945. for (var j = 0; j < elements.length; j++) {
  946. found[foundCount++] = elements[j];
  947. }
  948. }
  949. currentContext = new Array;
  950. var currentContextIndex = 0;
  951. var checkFunction; // This function will be used to filter the elements
  952. switch (attrOperator) {
  953. case '=': // Equality
  954. checkFunction = function(e) { return (e.getAttribute(attrName) == attrValue); };
  955. break;
  956. case '~': // Match one of space seperated words
  957. checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('\\b'+attrValue+'\\b'))); };
  958. break;
  959. case '|': // Match start with value followed by optional hyphen
  960. checkFunction = function(e) { return (e.getAttribute(attrName).match(new RegExp('^'+attrValue+'-?'))); };
  961. break;
  962. case '^': // Match starts with value
  963. checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) == 0); };
  964. break;
  965. case '$': // Match ends with value - fails with "Warning" in Opera 7
  966. checkFunction = function(e) { return (e.getAttribute(attrName).lastIndexOf(attrValue) == e.getAttribute(attrName).length - attrValue.length); };
  967. break;
  968. case '*': // Match ends with value
  969. checkFunction = function(e) { return (e.getAttribute(attrName).indexOf(attrValue) > -1); };
  970. break;
  971. default :
  972. // Just test for existence of attribute
  973. checkFunction = function(e) { return e.getAttribute(attrName); };
  974. }
  975. currentContext = new Array;
  976. currentContextIndex = 0;
  977. for (var k = 0; k < found.length; k++) {
  978. if (checkFunction(found[k])) {
  979. currentContext[currentContextIndex++] = found[k];
  980. }
  981. }
  982. // alert('Attribute Selector: '+tagName+' '+attrName+' '+attrOperator+' '+attrValue);
  983. continue; // Skip to next token
  984. }
  985. // If we get here, token is JUST an element (not a class or ID selector)
  986. tagName = token;
  987. var found = new Array;
  988. var foundCount = 0;
  989. for (var h = 0; h < currentContext.length; h++) {
  990. var elements = currentContext[h].getElementsByTagName(tagName);
  991. for (var j = 0; j < elements.length; j++) {
  992. found[foundCount++] = elements[j];
  993. }
  994. }
  995. currentContext = found;
  996. }
  997. return currentContext;
  998. };
  999. return getElementsBySelector;
  1000. })();
  1001. _.info = {
  1002. campaignParams: function() {
  1003. var campaign_keywords = 'utm_source utm_medium utm_campaign utm_content utm_term'.split(' ')
  1004. , kw = ''
  1005. , params = {};
  1006. _.each(campaign_keywords, function(kwkey) {
  1007. kw = _.getQueryParam(document.URL, kwkey);
  1008. if (kw.length) {
  1009. params[kwkey] = kw;
  1010. }
  1011. });
  1012. return params;
  1013. },
  1014. searchEngine: function(referrer) {
  1015. if (referrer.search('https?://(.*)google.([^/?]*)') === 0) {
  1016. return 'google';
  1017. } else if (referrer.search('https?://(.*)bing.com') === 0) {
  1018. return 'bing';
  1019. } else if (referrer.search('https?://(.*)yahoo.com') === 0) {
  1020. return 'yahoo';
  1021. } else if (referrer.search('https?://(.*)duckduckgo.com') === 0) {
  1022. return 'duckduckgo';
  1023. } else {
  1024. return null;
  1025. }
  1026. },
  1027. searchInfo: function(referrer) {
  1028. var search = _.info.searchEngine(referrer)
  1029. , param = (search != "yahoo") ? "q" : "p"
  1030. , ret = {};
  1031. if (search !== null) {
  1032. ret["$search_engine"] = search;
  1033. var keyword = _.getQueryParam(referrer, param);
  1034. if (keyword.length) {
  1035. ret["mp_keyword"] = keyword;
  1036. }
  1037. }
  1038. return ret;
  1039. },
  1040. /**
  1041. * This function detects which browser is running this script.
  1042. * The order of the checks are important since many user agents
  1043. * include key words used in later checks.
  1044. */
  1045. browser: function(user_agent, vendor, opera) {
  1046. var vendor = vendor || ''; // vendor is undefined for at least IE9
  1047. if (opera) {
  1048. if (_.includes(user_agent, "Mini")) {
  1049. return "Opera Mini";
  1050. }
  1051. return "Opera";
  1052. } else if (/(BlackBerry|PlayBook|BB10)/i.test(user_agent)) {
  1053. return 'BlackBerry';
  1054. } else if (_.includes(user_agent, "FBIOS")) {
  1055. return "Facebook Mobile";
  1056. } else if (_.includes(user_agent, "Chrome")) {
  1057. return "Chrome";
  1058. } else if (_.includes(user_agent, "CriOS")) {
  1059. return "Chrome iOS";
  1060. } else if (_.includes(vendor, "Apple")) {
  1061. if (_.includes(user_agent, "Mobile")) {
  1062. return "Mobile Safari";
  1063. }
  1064. return "Safari";
  1065. } else if (_.includes(user_agent, "Android")) {
  1066. return "Android Mobile";
  1067. } else if (_.includes(user_agent, "Konqueror")) {
  1068. return "Konqueror";
  1069. } else if (_.includes(user_agent, "Firefox")) {
  1070. return "Firefox";
  1071. } else if (_.includes(user_agent, "MSIE") || _.includes(user_agent, "Trident/")) {
  1072. return "Internet Explorer";
  1073. } else if (_.includes(user_agent, "Gecko")) {
  1074. return "Mozilla";
  1075. } else {
  1076. return "";
  1077. }
  1078. },
  1079. os: function() {
  1080. var a = userAgent;
  1081. if (/Windows/i.test(a)) {
  1082. if (/Phone/.test(a)) { return 'Windows Mobile'; }
  1083. return 'Windows';
  1084. } else if (/(iPhone|iPad|iPod)/.test(a)) {
  1085. return 'iOS';
  1086. } else if (/Android/.test(a)) {
  1087. return 'Android';
  1088. } else if (/(BlackBerry|PlayBook|BB10)/i.test(a)) {
  1089. return 'BlackBerry';
  1090. } else if (/Mac/i.test(a)) {
  1091. return 'Mac OS X';
  1092. } else if (/Linux/.test(a)) {
  1093. return 'Linux';
  1094. } else {
  1095. return '';
  1096. }
  1097. },
  1098. device: function(user_agent) {
  1099. if (/iPad/.test(user_agent)) {
  1100. return 'iPad';
  1101. } else if (/iPod/.test(user_agent)) {
  1102. return 'iPod Touch';
  1103. } else if (/iPhone/.test(user_agent)) {
  1104. return 'iPhone';
  1105. } else if (/(BlackBerry|PlayBook|BB10)/i.test(user_agent)) {
  1106. return 'BlackBerry';
  1107. } else if (/Windows Phone/i.test(user_agent)) {
  1108. return 'Windows Phone';
  1109. } else if (/Android/.test(user_agent)) {
  1110. return 'Android';
  1111. } else {
  1112. return '';
  1113. }
  1114. },
  1115. referringDomain: function(referrer) {
  1116. var split = referrer.split("/");
  1117. if (split.length >= 3) {
  1118. return split[2];
  1119. }
  1120. return "";
  1121. },
  1122. properties: function() {
  1123. return _.extend(_.strip_empty_properties({
  1124. '$os': _.info.os(),
  1125. '$browser': _.info.browser(userAgent, navigator.vendor, window.opera),
  1126. '$referrer': document.referrer,
  1127. '$referring_domain': _.info.referringDomain(document.referrer),
  1128. '$device': _.info.device(userAgent)
  1129. }), {
  1130. '$screen_height': screen.height,
  1131. '$screen_width': screen.width,
  1132. 'mp_lib': 'web'
  1133. });
  1134. },
  1135. people_properties: function() {
  1136. return _.strip_empty_properties({
  1137. '$os': _.info.os(),
  1138. '$browser': _.info.browser(userAgent, navigator.vendor, window.opera)
  1139. });
  1140. },
  1141. pageviewInfo: function(page) {
  1142. return _.strip_empty_properties({
  1143. 'mp_page': page
  1144. , 'mp_referrer': document.referrer
  1145. , 'mp_browser': _.info.browser(userAgent, navigator.vendor, window.opera)
  1146. , 'mp_platform': _.info.os()
  1147. });
  1148. }
  1149. };
  1150. // Console override
  1151. var console = {
  1152. /** @type {function(...[*])} */
  1153. log: function() {
  1154. if (DEBUG && !_.isUndefined(windowConsole) && windowConsole) {
  1155. try {
  1156. windowConsole.log.apply(windowConsole, arguments);
  1157. } catch(err) {
  1158. _.each(arguments, function(arg) {
  1159. windowConsole.log(arg);
  1160. });
  1161. }
  1162. }
  1163. },
  1164. /** @type {function(...[*])} */
  1165. error: function() {
  1166. if (DEBUG && !_.isUndefined(windowConsole) && windowConsole) {
  1167. var args = ["Mixpanel error:"].concat(_.toArray(arguments));
  1168. try {
  1169. windowConsole.error.apply(windowConsole, args);
  1170. } catch(err) {
  1171. _.each(args, function(arg) {
  1172. windowConsole.error(arg);
  1173. });
  1174. }
  1175. }
  1176. },
  1177. /** @type {function(...[*])} */
  1178. critical: function() {
  1179. if (!_.isUndefined(windowConsole) && windowConsole) {
  1180. var args = ["Mixpanel error:"].concat(_.toArray(arguments));
  1181. try {
  1182. windowConsole.error.apply(windowConsole, args);
  1183. } catch(err) {
  1184. _.each(args, function(arg) {
  1185. windowConsole.error(arg);
  1186. });
  1187. }
  1188. }
  1189. }
  1190. };
  1191. /**
  1192. * DomTracker Object
  1193. * @constructor
  1194. */
  1195. var DomTracker = function() {};
  1196. // interface
  1197. DomTracker.prototype.create_properties = function() {};
  1198. DomTracker.prototype.event_handler = function() {};
  1199. DomTracker.prototype.after_track_handler = function() {};
  1200. DomTracker.prototype.init = function(mixpanel_instance) {
  1201. this.mp = mixpanel_instance;
  1202. return this;
  1203. };
  1204. /**
  1205. * @param {string} query
  1206. * @param {string} event_name
  1207. * @param {Object=} properties
  1208. * @param {function(...[*])=} user_callback
  1209. */
  1210. DomTracker.prototype.track = function(query, event_name, properties, user_callback) {
  1211. var that = this
  1212. , elements = _.dom_query(query);
  1213. if (elements.length == 0) {
  1214. console.error("The DOM query (" + query + ") returned 0 elements");
  1215. return;
  1216. }
  1217. _.each(elements, function(element) {
  1218. _.register_event(element, this.override_event, function(e) {
  1219. var options = {}
  1220. , props = that.create_properties(properties, this)
  1221. , timeout = that.mp.get_config("track_links_timeout");
  1222. that.event_handler(e, this, options);
  1223. // in case the mixpanel servers don't get back to us in time
  1224. window.se

Large files files are truncated, but you can click here to view the full file