PageRenderTime 58ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/files/piwik/2.1.0/piwik.js

https://gitlab.com/Mirros/jsdelivr
JavaScript | 1469 lines | 1414 code | 14 blank | 41 comment | 4 complexity | 70392472e849818be25ae33a19744d8c MD5 | raw file
  1. /*!
  2. * Piwik - Web Analytics
  3. *
  4. * JavaScript tracking client
  5. *
  6. * @link http://piwik.org
  7. * @source https://github.com/piwik/piwik/blob/master/js/piwik.js
  8. * @license http://piwik.org/free-software/bsd/ Simplified BSD (also in js/LICENSE.txt)
  9. */
  10. // Refer to README.md for build instructions when minifying this file for distribution.
  11. /*
  12. * Browser [In]Compatibility
  13. * - minimum required ECMAScript: ECMA-262, edition 3
  14. *
  15. * Incompatible with these (and earlier) versions of:
  16. * - IE4 - try..catch and for..in introduced in IE5
  17. * - IE5 - named anonymous functions, array.push, encodeURIComponent, decodeURIComponent, and getElementsByTagName introduced in IE5.5
  18. * - Firefox 1.0 and Netscape 8.x - FF1.5 adds array.indexOf, among other things
  19. * - Mozilla 1.7 and Netscape 6.x-7.x
  20. * - Netscape 4.8
  21. * - Opera 6 - Error object (and Presto) introduced in Opera 7
  22. * - Opera 7
  23. */
  24. /************************************************************
  25. * JSON - public domain reference implementation by Douglas Crockford
  26. * @version 2012-10-08
  27. * @link http://www.JSON.org/js.html
  28. ************************************************************/
  29. /*jslint evil: true, regexp: false, bitwise: true*/
  30. /*global JSON2:true */
  31. /*members "", "\b", "\t", "\n", "\f", "\r", "\"", "\\", apply,
  32. call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
  33. getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
  34. lastIndex, length, parse, prototype, push, replace, sort, slice, stringify,
  35. test, toJSON, toString, valueOf,
  36. objectToJSON
  37. */
  38. // Create a JSON object only if one does not already exist. We create the
  39. // methods in a closure to avoid creating global variables.
  40. if (typeof JSON2 !== 'object') {
  41. JSON2 = {};
  42. }
  43. (function () {
  44. 'use strict';
  45. function f(n) {
  46. // Format integers to have at least two digits.
  47. return n < 10 ? '0' + n : n;
  48. }
  49. function objectToJSON(value, key) {
  50. var objectType = Object.prototype.toString.apply(value);
  51. if (objectType === '[object Date]') {
  52. return isFinite(value.valueOf())
  53. ? value.getUTCFullYear() + '-' +
  54. f(value.getUTCMonth() + 1) + '-' +
  55. f(value.getUTCDate()) + 'T' +
  56. f(value.getUTCHours()) + ':' +
  57. f(value.getUTCMinutes()) + ':' +
  58. f(value.getUTCSeconds()) + 'Z'
  59. : null;
  60. }
  61. if (objectType === '[object String]' ||
  62. objectType === '[object Number]' ||
  63. objectType === '[object Boolean]') {
  64. return value.valueOf();
  65. }
  66. if (objectType !== '[object Array]' &&
  67. typeof value.toJSON === 'function') {
  68. return value.toJSON(key);
  69. }
  70. return value;
  71. }
  72. var cx = new RegExp('[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]', 'g'),
  73. // hack: workaround Snort false positive (sid 8443)
  74. pattern = '\\\\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]',
  75. escapable = new RegExp('[' + pattern, 'g'),
  76. gap,
  77. indent,
  78. meta = { // table of character substitutions
  79. '\b': '\\b',
  80. '\t': '\\t',
  81. '\n': '\\n',
  82. '\f': '\\f',
  83. '\r': '\\r',
  84. '"' : '\\"',
  85. '\\': '\\\\'
  86. },
  87. rep;
  88. function quote(string) {
  89. // If the string contains no control characters, no quote characters, and no
  90. // backslash characters, then we can safely slap some quotes around it.
  91. // Otherwise we must also replace the offending characters with safe escape
  92. // sequences.
  93. escapable.lastIndex = 0;
  94. return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
  95. var c = meta[a];
  96. return typeof c === 'string'
  97. ? c
  98. : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  99. }) + '"' : '"' + string + '"';
  100. }
  101. function str(key, holder) {
  102. // Produce a string from holder[key].
  103. var i, // The loop counter.
  104. k, // The member key.
  105. v, // The member value.
  106. length,
  107. mind = gap,
  108. partial,
  109. value = holder[key];
  110. // If the value has a toJSON method, call it to obtain a replacement value.
  111. if (value && typeof value === 'object') {
  112. value = objectToJSON(value, key);
  113. }
  114. // If we were called with a replacer function, then call the replacer to
  115. // obtain a replacement value.
  116. if (typeof rep === 'function') {
  117. value = rep.call(holder, key, value);
  118. }
  119. // What happens next depends on the value's type.
  120. switch (typeof value) {
  121. case 'string':
  122. return quote(value);
  123. case 'number':
  124. // JSON numbers must be finite. Encode non-finite numbers as null.
  125. return isFinite(value) ? String(value) : 'null';
  126. case 'boolean':
  127. case 'null':
  128. // If the value is a boolean or null, convert it to a string. Note:
  129. // typeof null does not produce 'null'. The case is included here in
  130. // the remote chance that this gets fixed someday.
  131. return String(value);
  132. // If the type is 'object', we might be dealing with an object or an array or
  133. // null.
  134. case 'object':
  135. // Due to a specification blunder in ECMAScript, typeof null is 'object',
  136. // so watch out for that case.
  137. if (!value) {
  138. return 'null';
  139. }
  140. // Make an array to hold the partial results of stringifying this object value.
  141. gap += indent;
  142. partial = [];
  143. // Is the value an array?
  144. if (Object.prototype.toString.apply(value) === '[object Array]') {
  145. // The value is an array. Stringify every element. Use null as a placeholder
  146. // for non-JSON values.
  147. length = value.length;
  148. for (i = 0; i < length; i += 1) {
  149. partial[i] = str(i, value) || 'null';
  150. }
  151. // Join all of the elements together, separated with commas, and wrap them in
  152. // brackets.
  153. v = partial.length === 0
  154. ? '[]'
  155. : gap
  156. ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
  157. : '[' + partial.join(',') + ']';
  158. gap = mind;
  159. return v;
  160. }
  161. // If the replacer is an array, use it to select the members to be stringified.
  162. if (rep && typeof rep === 'object') {
  163. length = rep.length;
  164. for (i = 0; i < length; i += 1) {
  165. if (typeof rep[i] === 'string') {
  166. k = rep[i];
  167. v = str(k, value);
  168. if (v) {
  169. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  170. }
  171. }
  172. }
  173. } else {
  174. // Otherwise, iterate through all of the keys in the object.
  175. for (k in value) {
  176. if (Object.prototype.hasOwnProperty.call(value, k)) {
  177. v = str(k, value);
  178. if (v) {
  179. partial.push(quote(k) + (gap ? ': ' : ':') + v);
  180. }
  181. }
  182. }
  183. }
  184. // Join all of the member texts together, separated with commas,
  185. // and wrap them in braces.
  186. v = partial.length === 0
  187. ? '{}'
  188. : gap
  189. ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
  190. : '{' + partial.join(',') + '}';
  191. gap = mind;
  192. return v;
  193. }
  194. }
  195. // If the JSON object does not yet have a stringify method, give it one.
  196. if (typeof JSON2.stringify !== 'function') {
  197. JSON2.stringify = function (value, replacer, space) {
  198. // The stringify method takes a value and an optional replacer, and an optional
  199. // space parameter, and returns a JSON text. The replacer can be a function
  200. // that can replace values, or an array of strings that will select the keys.
  201. // A default replacer method can be provided. Use of the space parameter can
  202. // produce text that is more easily readable.
  203. var i;
  204. gap = '';
  205. indent = '';
  206. // If the space parameter is a number, make an indent string containing that
  207. // many spaces.
  208. if (typeof space === 'number') {
  209. for (i = 0; i < space; i += 1) {
  210. indent += ' ';
  211. }
  212. // If the space parameter is a string, it will be used as the indent string.
  213. } else if (typeof space === 'string') {
  214. indent = space;
  215. }
  216. // If there is a replacer, it must be a function or an array.
  217. // Otherwise, throw an error.
  218. rep = replacer;
  219. if (replacer && typeof replacer !== 'function' &&
  220. (typeof replacer !== 'object' ||
  221. typeof replacer.length !== 'number')) {
  222. throw new Error('JSON2.stringify');
  223. }
  224. // Make a fake root object containing our value under the key of ''.
  225. // Return the result of stringifying the value.
  226. return str('', {'': value});
  227. };
  228. }
  229. // If the JSON object does not yet have a parse method, give it one.
  230. if (typeof JSON2.parse !== 'function') {
  231. JSON2.parse = function (text, reviver) {
  232. // The parse method takes a text and an optional reviver function, and returns
  233. // a JavaScript value if the text is a valid JSON text.
  234. var j;
  235. function walk(holder, key) {
  236. // The walk method is used to recursively walk the resulting structure so
  237. // that modifications can be made.
  238. var k, v, value = holder[key];
  239. if (value && typeof value === 'object') {
  240. for (k in value) {
  241. if (Object.prototype.hasOwnProperty.call(value, k)) {
  242. v = walk(value, k);
  243. if (v !== undefined) {
  244. value[k] = v;
  245. } else {
  246. delete value[k];
  247. }
  248. }
  249. }
  250. }
  251. return reviver.call(holder, key, value);
  252. }
  253. // Parsing happens in four stages. In the first stage, we replace certain
  254. // Unicode characters with escape sequences. JavaScript handles many characters
  255. // incorrectly, either silently deleting them, or treating them as line endings.
  256. text = String(text);
  257. cx.lastIndex = 0;
  258. if (cx.test(text)) {
  259. text = text.replace(cx, function (a) {
  260. return '\\u' +
  261. ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
  262. });
  263. }
  264. // In the second stage, we run the text against regular expressions that look
  265. // for non-JSON patterns. We are especially concerned with '()' and 'new'
  266. // because they can cause invocation, and '=' because it can cause mutation.
  267. // But just to be safe, we want to reject all unexpected forms.
  268. // We split the second stage into 4 regexp operations in order to work around
  269. // crippling inefficiencies in IE's and Safari's regexp engines. First we
  270. // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
  271. // replace all simple value tokens with ']' characters. Third, we delete all
  272. // open brackets that follow a colon or comma or that begin the text. Finally,
  273. // we look to see that the remaining characters are only whitespace or ']' or
  274. // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
  275. if ((new RegExp('^[\\],:{}\\s]*$'))
  276. .test(text.replace(new RegExp('\\\\(?:["\\\\/bfnrt]|u[0-9a-fA-F]{4})', 'g'), '@')
  277. .replace(new RegExp('"[^"\\\\\n\r]*"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?', 'g'), ']')
  278. .replace(new RegExp('(?:^|:|,)(?:\\s*\\[)+', 'g'), ''))) {
  279. // In the third stage we use the eval function to compile the text into a
  280. // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
  281. // in JavaScript: it can begin a block or an object literal. We wrap the text
  282. // in parens to eliminate the ambiguity.
  283. j = eval('(' + text + ')');
  284. // In the optional fourth stage, we recursively walk the new structure, passing
  285. // each name/value pair to a reviver function for possible transformation.
  286. return typeof reviver === 'function'
  287. ? walk({'': j}, '')
  288. : j;
  289. }
  290. // If the text is not JSON parseable, then a SyntaxError is thrown.
  291. throw new SyntaxError('JSON2.parse');
  292. };
  293. }
  294. }());
  295. /************************************************************
  296. * end JSON
  297. ************************************************************/
  298. /*jslint browser:true, plusplus:true, vars:true, nomen:true, evil:true */
  299. /*global window */
  300. /*global unescape */
  301. /*global ActiveXObject */
  302. /*members encodeURIComponent, decodeURIComponent, getElementsByTagName,
  303. shift, unshift,
  304. createElement, appendChild, characterSet, charset,
  305. addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies,
  306. cookie, domain, readyState, documentElement, doScroll, title, text,
  307. location, top, document, referrer, parent, links, href, protocol, name, GearsFactory,
  308. performance, mozPerformance, msPerformance, webkitPerformance, timing, requestStart,
  309. responseEnd, event, which, button, srcElement, type, target,
  310. parentNode, tagName, hostname, className,
  311. userAgent, cookieEnabled, platform, mimeTypes, enabledPlugin, javaEnabled,
  312. XMLHttpRequest, ActiveXObject, open, setRequestHeader, onreadystatechange, send, readyState, status,
  313. getTime, getTimeAlias, setTime, toGMTString, getHours, getMinutes, getSeconds,
  314. toLowerCase, toUpperCase, charAt, indexOf, lastIndexOf, split, slice,
  315. onload, src,
  316. round, random,
  317. exec,
  318. res, width, height, devicePixelRatio,
  319. pdf, qt, realp, wma, dir, fla, java, gears, ag,
  320. hook, getHook, getVisitorId, getVisitorInfo, setTrackerUrl, appendToTrackingUrl, setSiteId,
  321. getAttributionInfo, getAttributionCampaignName, getAttributionCampaignKeyword,
  322. getAttributionReferrerTimestamp, getAttributionReferrerUrl,
  323. setCustomData, getCustomData,
  324. setCustomVariable, getCustomVariable, deleteCustomVariable,
  325. setDownloadExtensions, addDownloadExtensions,
  326. setDomains, setIgnoreClasses, setRequestMethod,
  327. setReferrerUrl, setCustomUrl, setAPIUrl, setDocumentTitle,
  328. setDownloadClasses, setLinkClasses,
  329. setCampaignNameKey, setCampaignKeywordKey,
  330. discardHashTag,
  331. setCookieNamePrefix, setCookieDomain, setCookiePath, setVisitorIdCookie,
  332. setVisitorCookieTimeout, setSessionCookieTimeout, setReferralCookieTimeout,
  333. setConversionAttributionFirstReferrer,
  334. disablePerformanceTracking, setGenerationTimeMs,
  335. doNotTrack, setDoNotTrack, msDoNotTrack,
  336. addListener, enableLinkTracking, setLinkTrackingTimer,
  337. setHeartBeatTimer, killFrame, redirectFile, setCountPreRendered,
  338. trackGoal, trackLink, trackPageView, trackSiteSearch, trackEvent,
  339. setEcommerceView, addEcommerceItem, trackEcommerceOrder, trackEcommerceCartUpdate,
  340. deleteCookies
  341. */
  342. /*global _paq:true */
  343. /*members push */
  344. /*global Piwik:true */
  345. /*members addPlugin, getTracker, getAsyncTracker */
  346. /*global Piwik_Overlay_Client */
  347. /*members initialize */
  348. /*global define */
  349. /*members amd */
  350. // asynchronous tracker (or proxy)
  351. if (typeof _paq !== 'object') {
  352. _paq = [];
  353. }
  354. // Piwik singleton and namespace
  355. if (typeof Piwik !== 'object') {
  356. Piwik = (function () {
  357. 'use strict';
  358. /************************************************************
  359. * Private data
  360. ************************************************************/
  361. var expireDateTime,
  362. /* plugins */
  363. plugins = {},
  364. /* alias frequently used globals for added minification */
  365. documentAlias = document,
  366. navigatorAlias = navigator,
  367. screenAlias = screen,
  368. windowAlias = window,
  369. /* performance timing */
  370. performanceAlias = windowAlias.performance || windowAlias.mozPerformance || windowAlias.msPerformance || windowAlias.webkitPerformance,
  371. /* DOM Ready */
  372. hasLoaded = false,
  373. registeredOnLoadHandlers = [],
  374. /* encode */
  375. encodeWrapper = windowAlias.encodeURIComponent,
  376. /* decode */
  377. decodeWrapper = windowAlias.decodeURIComponent,
  378. /* urldecode */
  379. urldecode = unescape,
  380. /* asynchronous tracker */
  381. asyncTracker,
  382. /* iterator */
  383. iterator,
  384. /* local Piwik */
  385. Piwik;
  386. /************************************************************
  387. * Private methods
  388. ************************************************************/
  389. /*
  390. * Is property defined?
  391. */
  392. function isDefined(property) {
  393. // workaround https://github.com/douglascrockford/JSLint/commit/24f63ada2f9d7ad65afc90e6d949f631935c2480
  394. var propertyType = typeof property;
  395. return propertyType !== 'undefined';
  396. }
  397. /*
  398. * Is property a function?
  399. */
  400. function isFunction(property) {
  401. return typeof property === 'function';
  402. }
  403. /*
  404. * Is property an object?
  405. *
  406. * @return bool Returns true if property is null, an Object, or subclass of Object (i.e., an instanceof String, Date, etc.)
  407. */
  408. function isObject(property) {
  409. return typeof property === 'object';
  410. }
  411. /*
  412. * Is property a string?
  413. */
  414. function isString(property) {
  415. return typeof property === 'string' || property instanceof String;
  416. }
  417. /*
  418. * apply wrapper
  419. *
  420. * @param array parameterArray An array comprising either:
  421. * [ 'methodName', optional_parameters ]
  422. * or:
  423. * [ functionObject, optional_parameters ]
  424. */
  425. function apply() {
  426. var i, f, parameterArray;
  427. for (i = 0; i < arguments.length; i += 1) {
  428. parameterArray = arguments[i];
  429. f = parameterArray.shift();
  430. if (isString(f)) {
  431. asyncTracker[f].apply(asyncTracker, parameterArray);
  432. } else {
  433. f.apply(asyncTracker, parameterArray);
  434. }
  435. }
  436. }
  437. /*
  438. * Cross-browser helper function to add event handler
  439. */
  440. function addEventListener(element, eventType, eventHandler, useCapture) {
  441. if (element.addEventListener) {
  442. element.addEventListener(eventType, eventHandler, useCapture);
  443. return true;
  444. }
  445. if (element.attachEvent) {
  446. return element.attachEvent('on' + eventType, eventHandler);
  447. }
  448. element['on' + eventType] = eventHandler;
  449. }
  450. /*
  451. * Call plugin hook methods
  452. */
  453. function executePluginMethod(methodName, callback) {
  454. var result = '',
  455. i,
  456. pluginMethod;
  457. for (i in plugins) {
  458. if (Object.prototype.hasOwnProperty.call(plugins, i)) {
  459. pluginMethod = plugins[i][methodName];
  460. if (isFunction(pluginMethod)) {
  461. result += pluginMethod(callback);
  462. }
  463. }
  464. }
  465. return result;
  466. }
  467. /*
  468. * Handle beforeunload event
  469. *
  470. * Subject to Safari's "Runaway JavaScript Timer" and
  471. * Chrome V8 extension that terminates JS that exhibits
  472. * "slow unload", i.e., calling getTime() > 1000 times
  473. */
  474. function beforeUnloadHandler() {
  475. var now;
  476. executePluginMethod('unload');
  477. /*
  478. * Delay/pause (blocks UI)
  479. */
  480. if (expireDateTime) {
  481. // the things we do for backwards compatibility...
  482. // in ECMA-262 5th ed., we could simply use:
  483. // while (Date.now() < expireDateTime) { }
  484. do {
  485. now = new Date();
  486. } while (now.getTimeAlias() < expireDateTime);
  487. }
  488. }
  489. /*
  490. * Handler for onload event
  491. */
  492. function loadHandler() {
  493. var i;
  494. if (!hasLoaded) {
  495. hasLoaded = true;
  496. executePluginMethod('load');
  497. for (i = 0; i < registeredOnLoadHandlers.length; i++) {
  498. registeredOnLoadHandlers[i]();
  499. }
  500. }
  501. return true;
  502. }
  503. /*
  504. * Add onload or DOM ready handler
  505. */
  506. function addReadyListener() {
  507. var _timer;
  508. if (documentAlias.addEventListener) {
  509. addEventListener(documentAlias, 'DOMContentLoaded', function ready() {
  510. documentAlias.removeEventListener('DOMContentLoaded', ready, false);
  511. loadHandler();
  512. });
  513. } else if (documentAlias.attachEvent) {
  514. documentAlias.attachEvent('onreadystatechange', function ready() {
  515. if (documentAlias.readyState === 'complete') {
  516. documentAlias.detachEvent('onreadystatechange', ready);
  517. loadHandler();
  518. }
  519. });
  520. if (documentAlias.documentElement.doScroll && windowAlias === windowAlias.top) {
  521. (function ready() {
  522. if (!hasLoaded) {
  523. try {
  524. documentAlias.documentElement.doScroll('left');
  525. } catch (error) {
  526. setTimeout(ready, 0);
  527. return;
  528. }
  529. loadHandler();
  530. }
  531. }());
  532. }
  533. }
  534. // sniff for older WebKit versions
  535. if ((new RegExp('WebKit')).test(navigatorAlias.userAgent)) {
  536. _timer = setInterval(function () {
  537. if (hasLoaded || /loaded|complete/.test(documentAlias.readyState)) {
  538. clearInterval(_timer);
  539. loadHandler();
  540. }
  541. }, 10);
  542. }
  543. // fallback
  544. addEventListener(windowAlias, 'load', loadHandler, false);
  545. }
  546. /*
  547. * Load JavaScript file (asynchronously)
  548. */
  549. function loadScript(src, onLoad) {
  550. var script = documentAlias.createElement('script');
  551. script.type = 'text/javascript';
  552. script.src = src;
  553. if (script.readyState) {
  554. script.onreadystatechange = function () {
  555. var state = this.readyState;
  556. if (state === 'loaded' || state === 'complete') {
  557. script.onreadystatechange = null;
  558. onLoad();
  559. }
  560. };
  561. } else {
  562. script.onload = onLoad;
  563. }
  564. documentAlias.getElementsByTagName('head')[0].appendChild(script);
  565. }
  566. /*
  567. * Get page referrer
  568. */
  569. function getReferrer() {
  570. var referrer = '';
  571. try {
  572. referrer = windowAlias.top.document.referrer;
  573. } catch (e) {
  574. if (windowAlias.parent) {
  575. try {
  576. referrer = windowAlias.parent.document.referrer;
  577. } catch (e2) {
  578. referrer = '';
  579. }
  580. }
  581. }
  582. if (referrer === '') {
  583. referrer = documentAlias.referrer;
  584. }
  585. return referrer;
  586. }
  587. /*
  588. * Extract scheme/protocol from URL
  589. */
  590. function getProtocolScheme(url) {
  591. var e = new RegExp('^([a-z]+):'),
  592. matches = e.exec(url);
  593. return matches ? matches[1] : null;
  594. }
  595. /*
  596. * Extract hostname from URL
  597. */
  598. function getHostName(url) {
  599. // scheme : // [username [: password] @] hostame [: port] [/ [path] [? query] [# fragment]]
  600. var e = new RegExp('^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)'),
  601. matches = e.exec(url);
  602. return matches ? matches[1] : url;
  603. }
  604. /*
  605. * Extract parameter from URL
  606. */
  607. function getParameter(url, name) {
  608. var regexSearch = "[\\?&#]" + name + "=([^&#]*)";
  609. var regex = new RegExp(regexSearch);
  610. var results = regex.exec(url);
  611. return results ? decodeWrapper(results[1]) : '';
  612. }
  613. /*
  614. * UTF-8 encoding
  615. */
  616. function utf8_encode(argString) {
  617. return urldecode(encodeWrapper(argString));
  618. }
  619. /************************************************************
  620. * sha1
  621. * - based on sha1 from http://phpjs.org/functions/sha1:512 (MIT / GPL v2)
  622. ************************************************************/
  623. function sha1(str) {
  624. // + original by: Webtoolkit.info (http://www.webtoolkit.info/)
  625. // + namespaced by: Michael White (http://getsprink.com)
  626. // + input by: Brett Zamir (http://brett-zamir.me)
  627. // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
  628. // + jslinted by: Anthon Pang (http://piwik.org)
  629. var
  630. rotate_left = function (n, s) {
  631. return (n << s) | (n >>> (32 - s));
  632. },
  633. cvt_hex = function (val) {
  634. var strout = '',
  635. i,
  636. v;
  637. for (i = 7; i >= 0; i--) {
  638. v = (val >>> (i * 4)) & 0x0f;
  639. strout += v.toString(16);
  640. }
  641. return strout;
  642. },
  643. blockstart,
  644. i,
  645. j,
  646. W = [],
  647. H0 = 0x67452301,
  648. H1 = 0xEFCDAB89,
  649. H2 = 0x98BADCFE,
  650. H3 = 0x10325476,
  651. H4 = 0xC3D2E1F0,
  652. A,
  653. B,
  654. C,
  655. D,
  656. E,
  657. temp,
  658. str_len,
  659. word_array = [];
  660. str = utf8_encode(str);
  661. str_len = str.length;
  662. for (i = 0; i < str_len - 3; i += 4) {
  663. j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 |
  664. str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3);
  665. word_array.push(j);
  666. }
  667. switch (str_len & 3) {
  668. case 0:
  669. i = 0x080000000;
  670. break;
  671. case 1:
  672. i = str.charCodeAt(str_len - 1) << 24 | 0x0800000;
  673. break;
  674. case 2:
  675. i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000;
  676. break;
  677. case 3:
  678. i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) << 8 | 0x80;
  679. break;
  680. }
  681. word_array.push(i);
  682. while ((word_array.length & 15) !== 14) {
  683. word_array.push(0);
  684. }
  685. word_array.push(str_len >>> 29);
  686. word_array.push((str_len << 3) & 0x0ffffffff);
  687. for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
  688. for (i = 0; i < 16; i++) {
  689. W[i] = word_array[blockstart + i];
  690. }
  691. for (i = 16; i <= 79; i++) {
  692. W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
  693. }
  694. A = H0;
  695. B = H1;
  696. C = H2;
  697. D = H3;
  698. E = H4;
  699. for (i = 0; i <= 19; i++) {
  700. temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
  701. E = D;
  702. D = C;
  703. C = rotate_left(B, 30);
  704. B = A;
  705. A = temp;
  706. }
  707. for (i = 20; i <= 39; i++) {
  708. temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
  709. E = D;
  710. D = C;
  711. C = rotate_left(B, 30);
  712. B = A;
  713. A = temp;
  714. }
  715. for (i = 40; i <= 59; i++) {
  716. temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
  717. E = D;
  718. D = C;
  719. C = rotate_left(B, 30);
  720. B = A;
  721. A = temp;
  722. }
  723. for (i = 60; i <= 79; i++) {
  724. temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
  725. E = D;
  726. D = C;
  727. C = rotate_left(B, 30);
  728. B = A;
  729. A = temp;
  730. }
  731. H0 = (H0 + A) & 0x0ffffffff;
  732. H1 = (H1 + B) & 0x0ffffffff;
  733. H2 = (H2 + C) & 0x0ffffffff;
  734. H3 = (H3 + D) & 0x0ffffffff;
  735. H4 = (H4 + E) & 0x0ffffffff;
  736. }
  737. temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
  738. return temp.toLowerCase();
  739. }
  740. /************************************************************
  741. * end sha1
  742. ************************************************************/
  743. /*
  744. * Fix-up URL when page rendered from search engine cache or translated page
  745. */
  746. function urlFixup(hostName, href, referrer) {
  747. if (hostName === 'translate.googleusercontent.com') { // Google
  748. if (referrer === '') {
  749. referrer = href;
  750. }
  751. href = getParameter(href, 'u');
  752. hostName = getHostName(href);
  753. } else if (hostName === 'cc.bingj.com' || // Bing
  754. hostName === 'webcache.googleusercontent.com' || // Google
  755. hostName.slice(0, 5) === '74.6.') { // Yahoo (via Inktomi 74.6.0.0/16)
  756. href = documentAlias.links[0].href;
  757. hostName = getHostName(href);
  758. }
  759. return [hostName, href, referrer];
  760. }
  761. /*
  762. * Fix-up domain
  763. */
  764. function domainFixup(domain) {
  765. var dl = domain.length;
  766. // remove trailing '.'
  767. if (domain.charAt(--dl) === '.') {
  768. domain = domain.slice(0, dl);
  769. }
  770. // remove leading '*'
  771. if (domain.slice(0, 2) === '*.') {
  772. domain = domain.slice(1);
  773. }
  774. return domain;
  775. }
  776. /*
  777. * Title fixup
  778. */
  779. function titleFixup(title) {
  780. title = title && title.text ? title.text : title;
  781. if (!isString(title)) {
  782. var tmp = documentAlias.getElementsByTagName('title');
  783. if (tmp && isDefined(tmp[0])) {
  784. title = tmp[0].text;
  785. }
  786. }
  787. return title;
  788. }
  789. /************************************************************
  790. * Page Overlay
  791. ************************************************************/
  792. function getPiwikUrlForOverlay(trackerUrl, apiUrl) {
  793. if (apiUrl) {
  794. return apiUrl;
  795. }
  796. if (trackerUrl.slice(-9) === 'piwik.php') {
  797. trackerUrl = trackerUrl.slice(0, trackerUrl.length - 9);
  798. }
  799. return trackerUrl;
  800. }
  801. /*
  802. * Check whether this is a page overlay session
  803. *
  804. * @return boolean
  805. *
  806. * {@internal side-effect: modifies window.name }}
  807. */
  808. function isOverlaySession(configTrackerSiteId) {
  809. var windowName = 'Piwik_Overlay';
  810. // check whether we were redirected from the piwik overlay plugin
  811. var referrerRegExp = new RegExp('index\\.php\\?module=Overlay&action=startOverlaySession'
  812. + '&idsite=([0-9]+)&period=([^&]+)&date=([^&]+)$');
  813. var match = referrerRegExp.exec(documentAlias.referrer);
  814. if (match) {
  815. // check idsite
  816. var idsite = match[1];
  817. if (idsite !== String(configTrackerSiteId)) {
  818. return false;
  819. }
  820. // store overlay session info in window name
  821. var period = match[2],
  822. date = match[3];
  823. windowAlias.name = windowName + '###' + period + '###' + date;
  824. }
  825. // retrieve and check data from window name
  826. var windowNameParts = windowAlias.name.split('###');
  827. return windowNameParts.length === 3 && windowNameParts[0] === windowName;
  828. }
  829. /*
  830. * Inject the script needed for page overlay
  831. */
  832. function injectOverlayScripts(configTrackerUrl, configApiUrl, configTrackerSiteId) {
  833. var windowNameParts = windowAlias.name.split('###'),
  834. period = windowNameParts[1],
  835. date = windowNameParts[2],
  836. piwikUrl = getPiwikUrlForOverlay(configTrackerUrl, configApiUrl);
  837. loadScript(
  838. piwikUrl + 'plugins/Overlay/client/client.js?v=1',
  839. function () {
  840. Piwik_Overlay_Client.initialize(piwikUrl, configTrackerSiteId, period, date);
  841. }
  842. );
  843. }
  844. /************************************************************
  845. * End Page Overlay
  846. ************************************************************/
  847. /*
  848. * Piwik Tracker class
  849. *
  850. * trackerUrl and trackerSiteId are optional arguments to the constructor
  851. *
  852. * See: Tracker.setTrackerUrl() and Tracker.setSiteId()
  853. */
  854. function Tracker(trackerUrl, siteId) {
  855. /************************************************************
  856. * Private members
  857. ************************************************************/
  858. var
  859. /*<DEBUG>*/
  860. /*
  861. * registered test hooks
  862. */
  863. registeredHooks = {},
  864. /*</DEBUG>*/
  865. // Current URL and Referrer URL
  866. locationArray = urlFixup(documentAlias.domain, windowAlias.location.href, getReferrer()),
  867. domainAlias = domainFixup(locationArray[0]),
  868. locationHrefAlias = locationArray[1],
  869. configReferrerUrl = locationArray[2],
  870. // Request method (GET or POST)
  871. configRequestMethod = 'GET',
  872. // Tracker URL
  873. configTrackerUrl = trackerUrl || '',
  874. // API URL (only set if it differs from the Tracker URL)
  875. configApiUrl = '',
  876. // This string is appended to the Tracker URL Request (eg. to send data that is not handled by the existin setters/getters)
  877. configAppendToTrackingUrl = '',
  878. // Site ID
  879. configTrackerSiteId = siteId || '',
  880. // Document URL
  881. configCustomUrl,
  882. // Document title
  883. configTitle = documentAlias.title,
  884. // Extensions to be treated as download links
  885. configDownloadExtensions = '7z|aac|apk|ar[cj]|as[fx]|avi|bin|csv|deb|dmg|docx?|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mp(2|3|4|e?g)|mov(ie)?|ms[ip]|od[bfgpst]|og[gv]|pdf|phps|png|pptx?|qtm?|ra[mr]?|rpm|sea|sit|tar|t?bz2?|tgz|torrent|txt|wav|wm[av]|wpd||xlsx?|xml|z|zip',
  886. // Hosts or alias(es) to not treat as outlinks
  887. configHostsAlias = [domainAlias],
  888. // HTML anchor element classes to not track
  889. configIgnoreClasses = [],
  890. // HTML anchor element classes to treat as downloads
  891. configDownloadClasses = [],
  892. // HTML anchor element classes to treat at outlinks
  893. configLinkClasses = [],
  894. // Maximum delay to wait for web bug image to be fetched (in milliseconds)
  895. configTrackerPause = 500,
  896. // Minimum visit time after initial page view (in milliseconds)
  897. configMinimumVisitTime,
  898. // Recurring heart beat after initial ping (in milliseconds)
  899. configHeartBeatTimer,
  900. // Disallow hash tags in URL
  901. configDiscardHashTag,
  902. // Custom data
  903. configCustomData,
  904. // Campaign names
  905. configCampaignNameParameters = [ 'pk_campaign', 'piwik_campaign', 'utm_campaign', 'utm_source', 'utm_medium' ],
  906. // Campaign keywords
  907. configCampaignKeywordParameters = [ 'pk_kwd', 'piwik_kwd', 'utm_term' ],
  908. // First-party cookie name prefix
  909. configCookieNamePrefix = '_pk_',
  910. // First-party cookie domain
  911. // User agent defaults to origin hostname
  912. configCookieDomain,
  913. // First-party cookie path
  914. // Default is user agent defined.
  915. configCookiePath,
  916. // Cookies are disabled
  917. configCookiesDisabled = false,
  918. // Do Not Track
  919. configDoNotTrack,
  920. // Count sites which are pre-rendered
  921. configCountPreRendered,
  922. // Do we attribute the conversion to the first referrer or the most recent referrer?
  923. configConversionAttributionFirstReferrer,
  924. // Life of the visitor cookie (in milliseconds)
  925. configVisitorCookieTimeout = 63072000000, // 2 years
  926. // Life of the session cookie (in milliseconds)
  927. configSessionCookieTimeout = 1800000, // 30 minutes
  928. // Life of the referral cookie (in milliseconds)
  929. configReferralCookieTimeout = 15768000000, // 6 months
  930. // Is performance tracking enabled
  931. configPerformanceTrackingEnabled = true,
  932. // Generation time set from the server
  933. configPerformanceGenerationTime = 0,
  934. // Custom Variables read from cookie, scope "visit"
  935. customVariables = false,
  936. // Custom Variables, scope "page"
  937. customVariablesPage = {},
  938. // Custom Variables, scope "event"
  939. customVariablesEvent = {},
  940. // Custom Variables names and values are each truncated before being sent in the request or recorded in the cookie
  941. customVariableMaximumLength = 200,
  942. // Ecommerce items
  943. ecommerceItems = {},
  944. // Browser features via client-side data collection
  945. browserFeatures = {},
  946. // Guard against installing the link tracker more than once per Tracker instance
  947. linkTrackingInstalled = false,
  948. // Guard against installing the activity tracker more than once per Tracker instance
  949. activityTrackingInstalled = false,
  950. // Last activity timestamp
  951. lastActivityTime,
  952. // Internal state of the pseudo click handler
  953. lastButton,
  954. lastTarget,
  955. // Hash function
  956. hash = sha1,
  957. // Domain hash value
  958. domainHash,
  959. // Visitor UUID
  960. visitorUUID;
  961. /*
  962. * Set cookie value
  963. */
  964. function setCookie(cookieName, value, msToExpire, path, domain, secure) {
  965. if (configCookiesDisabled) {
  966. return;
  967. }
  968. var expiryDate;
  969. // relative time to expire in milliseconds
  970. if (msToExpire) {
  971. expiryDate = new Date();
  972. expiryDate.setTime(expiryDate.getTime() + msToExpire);
  973. }
  974. documentAlias.cookie = cookieName + '=' + encodeWrapper(value) +
  975. (msToExpire ? ';expires=' + expiryDate.toGMTString() : '') +
  976. ';path=' + (path || '/') +
  977. (domain ? ';domain=' + domain : '') +
  978. (secure ? ';secure' : '');
  979. }
  980. /*
  981. * Get cookie value
  982. */
  983. function getCookie(cookieName) {
  984. if (configCookiesDisabled) {
  985. return 0;
  986. }
  987. var cookiePattern = new RegExp('(^|;)[ ]*' + cookieName + '=([^;]*)'),
  988. cookieMatch = cookiePattern.exec(documentAlias.cookie);
  989. return cookieMatch ? decodeWrapper(cookieMatch[2]) : 0;
  990. }
  991. /*
  992. * Removes hash tag from the URL
  993. *
  994. * URLs are purified before being recorded in the cookie,
  995. * or before being sent as GET parameters
  996. */
  997. function purify(url) {
  998. var targetPattern;
  999. if (configDiscardHashTag) {
  1000. targetPattern = new RegExp('#.*');
  1001. return url.replace(targetPattern, '');
  1002. }
  1003. return url;
  1004. }
  1005. /*
  1006. * Resolve relative reference
  1007. *
  1008. * Note: not as described in rfc3986 section 5.2
  1009. */
  1010. function resolveRelativeReference(baseUrl, url) {
  1011. var protocol = getProtocolScheme(url),
  1012. i;
  1013. if (protocol) {
  1014. return url;
  1015. }
  1016. if (url.slice(0, 1) === '/') {
  1017. return getProtocolScheme(baseUrl) + '://' + getHostName(baseUrl) + url;
  1018. }
  1019. baseUrl = purify(baseUrl);
  1020. i = baseUrl.indexOf('?');
  1021. if (i >= 0) {
  1022. baseUrl = baseUrl.slice(0, i);
  1023. }
  1024. i = baseUrl.lastIndexOf('/');
  1025. if (i !== baseUrl.length - 1) {
  1026. baseUrl = baseUrl.slice(0, i + 1);
  1027. }
  1028. return baseUrl + url;
  1029. }
  1030. /*
  1031. * Is the host local? (i.e., not an outlink)
  1032. */
  1033. function isSiteHostName(hostName) {
  1034. var i,
  1035. alias,
  1036. offset;
  1037. for (i = 0; i < configHostsAlias.length; i++) {
  1038. alias = domainFixup(configHostsAlias[i].toLowerCase());
  1039. if (hostName === alias) {
  1040. return true;
  1041. }
  1042. if (alias.slice(0, 1) === '.') {
  1043. if (hostName === alias.slice(1)) {
  1044. return true;
  1045. }
  1046. offset = hostName.length - alias.length;
  1047. if ((offset > 0) && (hostName.slice(offset) === alias)) {
  1048. return true;
  1049. }
  1050. }
  1051. }
  1052. return false;
  1053. }
  1054. /*
  1055. * Send image request to Piwik server using GET.
  1056. * The infamous web bug (or beacon) is a transparent, single pixel (1x1) image
  1057. */
  1058. function getImage(request) {
  1059. var image = new Image(1, 1);
  1060. image.onload = function () {
  1061. iterator = 0; // To avoid JSLint warning of empty block
  1062. };
  1063. image.src = configTrackerUrl + (configTrackerUrl.indexOf('?') < 0 ? '?' : '&') + request;
  1064. }
  1065. /*
  1066. * POST request to Piwik server using XMLHttpRequest.
  1067. */
  1068. function sendXmlHttpRequest(request) {
  1069. try {
  1070. // we use the progid Microsoft.XMLHTTP because
  1071. // IE5.5 included MSXML 2.5; the progid MSXML2.XMLHTTP
  1072. // is pinned to MSXML2.XMLHTTP.3.0
  1073. var xhr = windowAlias.XMLHttpRequest
  1074. ? new windowAlias.XMLHttpRequest()
  1075. : windowAlias.ActiveXObject
  1076. ? new ActiveXObject('Microsoft.XMLHTTP')
  1077. : null;
  1078. xhr.open('POST', configTrackerUrl, true);
  1079. // fallback on error
  1080. xhr.onreadystatechange = function () {
  1081. if (this.readyState === 4 && this.status !== 200) {
  1082. getImage(request);
  1083. }
  1084. };
  1085. // see XMLHttpRequest Level 2 spec, section 4.7.2 for invalid headers
  1086. // @link http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html
  1087. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
  1088. xhr.send(request);
  1089. } catch (e) {
  1090. // fallback
  1091. getImage(request);
  1092. }
  1093. }
  1094. /*
  1095. * Send request
  1096. */
  1097. function sendRequest(request, delay) {
  1098. var now = new Date();
  1099. if (!configDoNotTrack) {
  1100. if (configRequestMethod === 'POST') {
  1101. sendXmlHttpRequest(request);
  1102. } else {
  1103. getImage(request);
  1104. }
  1105. expireDateTime = now.getTime() + delay;
  1106. }
  1107. }
  1108. /*
  1109. * Get cookie name with prefix and domain hash
  1110. */
  1111. function getCookieName(baseName) {
  1112. // NOTE: If the cookie name is changed, we must also update the PiwikTracker.php which
  1113. // will attempt to discover first party cookies. eg. See the PHP Client method getVisitorId()
  1114. return configCookieNamePrefix + baseName + '.' + configTrackerSiteId + '.' + domainHash;
  1115. }
  1116. /*
  1117. * Does browser have cookies enabled (for this site)?
  1118. */
  1119. function hasCookies() {
  1120. if (configCookiesDisabled) {
  1121. return '0';
  1122. }
  1123. if (!isDefined(navigatorAlias.cookieEnabled)) {
  1124. var testCookieName = getCookieName('testcookie');
  1125. setCookie(testCookieName, '1');
  1126. return getCookie(testCookieName) === '1' ? '1' : '0';
  1127. }
  1128. return navigatorAlias.cookieEnabled ? '1' : '0';
  1129. }
  1130. /*
  1131. * Update domain hash
  1132. */
  1133. function updateDomainHash() {
  1134. domainHash = hash((configCookieDomain || domainAlias) + (configCookiePath || '/')).slice(0, 4); // 4 hexits = 16 bits
  1135. }
  1136. /*
  1137. * Inits the custom variables object
  1138. */
  1139. function getCustomVariablesFromCookie() {
  1140. var cookieName = getCookieName('cvar'),
  1141. cookie = getCookie(cookieName);
  1142. if (cookie.length) {
  1143. cookie = JSON2.parse(cookie);
  1144. if (isObject(cookie)) {
  1145. return cookie;
  1146. }
  1147. }
  1148. return {};
  1149. }
  1150. /*
  1151. * Lazy loads the custom variables from the cookie, only once during this page view
  1152. */
  1153. function loadCustomVariables() {
  1154. if (customVariables === false) {
  1155. customVariables = getCustomVariablesFromCookie();
  1156. }
  1157. }
  1158. /*
  1159. * Process all "activity" events.
  1160. * For performance, this function must have low overhead.
  1161. */
  1162. function activityHandler() {
  1163. var now = new Date();
  1164. lastActivityTime = now.getTime();
  1165. }
  1166. /*
  1167. * Sets the Visitor ID cookie: either the first time loadVisitorIdCookie is called
  1168. * or when there is a new visit or a new page view
  1169. */
  1170. function setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs, lastEcommerceOrderTs) {
  1171. setCookie(getCookieName('id'), uuid + '.' + createTs + '.' + visitCount + '.'