/files/piwik/2.1.0/piwik.js
JavaScript | 1469 lines | 1414 code | 14 blank | 41 comment | 4 complexity | 70392472e849818be25ae33a19744d8c MD5 | raw file
- /*!
- * Piwik - Web Analytics
- *
- * JavaScript tracking client
- *
- * @link http://piwik.org
- * @source https://github.com/piwik/piwik/blob/master/js/piwik.js
- * @license http://piwik.org/free-software/bsd/ Simplified BSD (also in js/LICENSE.txt)
- */
- // Refer to README.md for build instructions when minifying this file for distribution.
- /*
- * Browser [In]Compatibility
- * - minimum required ECMAScript: ECMA-262, edition 3
- *
- * Incompatible with these (and earlier) versions of:
- * - IE4 - try..catch and for..in introduced in IE5
- * - IE5 - named anonymous functions, array.push, encodeURIComponent, decodeURIComponent, and getElementsByTagName introduced in IE5.5
- * - Firefox 1.0 and Netscape 8.x - FF1.5 adds array.indexOf, among other things
- * - Mozilla 1.7 and Netscape 6.x-7.x
- * - Netscape 4.8
- * - Opera 6 - Error object (and Presto) introduced in Opera 7
- * - Opera 7
- */
- /************************************************************
- * JSON - public domain reference implementation by Douglas Crockford
- * @version 2012-10-08
- * @link http://www.JSON.org/js.html
- ************************************************************/
- /*jslint evil: true, regexp: false, bitwise: true*/
- /*global JSON2:true */
- /*members "", "\b", "\t", "\n", "\f", "\r", "\"", "\\", apply,
- call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
- getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
- lastIndex, length, parse, prototype, push, replace, sort, slice, stringify,
- test, toJSON, toString, valueOf,
- objectToJSON
- */
- // Create a JSON object only if one does not already exist. We create the
- // methods in a closure to avoid creating global variables.
- if (typeof JSON2 !== 'object') {
- JSON2 = {};
- }
- (function () {
- 'use strict';
- function f(n) {
- // Format integers to have at least two digits.
- return n < 10 ? '0' + n : n;
- }
- function objectToJSON(value, key) {
- var objectType = Object.prototype.toString.apply(value);
- if (objectType === '[object Date]') {
- return isFinite(value.valueOf())
- ? value.getUTCFullYear() + '-' +
- f(value.getUTCMonth() + 1) + '-' +
- f(value.getUTCDate()) + 'T' +
- f(value.getUTCHours()) + ':' +
- f(value.getUTCMinutes()) + ':' +
- f(value.getUTCSeconds()) + 'Z'
- : null;
- }
- if (objectType === '[object String]' ||
- objectType === '[object Number]' ||
- objectType === '[object Boolean]') {
- return value.valueOf();
- }
- if (objectType !== '[object Array]' &&
- typeof value.toJSON === 'function') {
- return value.toJSON(key);
- }
- return value;
- }
- var cx = new RegExp('[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]', 'g'),
- // hack: workaround Snort false positive (sid 8443)
- pattern = '\\\\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]',
- escapable = new RegExp('[' + pattern, 'g'),
- gap,
- indent,
- meta = { // table of character substitutions
- '\b': '\\b',
- '\t': '\\t',
- '\n': '\\n',
- '\f': '\\f',
- '\r': '\\r',
- '"' : '\\"',
- '\\': '\\\\'
- },
- rep;
- function quote(string) {
- // If the string contains no control characters, no quote characters, and no
- // backslash characters, then we can safely slap some quotes around it.
- // Otherwise we must also replace the offending characters with safe escape
- // sequences.
- escapable.lastIndex = 0;
- return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
- var c = meta[a];
- return typeof c === 'string'
- ? c
- : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- }) + '"' : '"' + string + '"';
- }
- function str(key, holder) {
- // Produce a string from holder[key].
- var i, // The loop counter.
- k, // The member key.
- v, // The member value.
- length,
- mind = gap,
- partial,
- value = holder[key];
- // If the value has a toJSON method, call it to obtain a replacement value.
- if (value && typeof value === 'object') {
- value = objectToJSON(value, key);
- }
- // If we were called with a replacer function, then call the replacer to
- // obtain a replacement value.
- if (typeof rep === 'function') {
- value = rep.call(holder, key, value);
- }
- // What happens next depends on the value's type.
- switch (typeof value) {
- case 'string':
- return quote(value);
- case 'number':
- // JSON numbers must be finite. Encode non-finite numbers as null.
- return isFinite(value) ? String(value) : 'null';
- case 'boolean':
- case 'null':
- // If the value is a boolean or null, convert it to a string. Note:
- // typeof null does not produce 'null'. The case is included here in
- // the remote chance that this gets fixed someday.
- return String(value);
- // If the type is 'object', we might be dealing with an object or an array or
- // null.
- case 'object':
- // Due to a specification blunder in ECMAScript, typeof null is 'object',
- // so watch out for that case.
- if (!value) {
- return 'null';
- }
- // Make an array to hold the partial results of stringifying this object value.
- gap += indent;
- partial = [];
- // Is the value an array?
- if (Object.prototype.toString.apply(value) === '[object Array]') {
- // The value is an array. Stringify every element. Use null as a placeholder
- // for non-JSON values.
- length = value.length;
- for (i = 0; i < length; i += 1) {
- partial[i] = str(i, value) || 'null';
- }
- // Join all of the elements together, separated with commas, and wrap them in
- // brackets.
- v = partial.length === 0
- ? '[]'
- : gap
- ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
- : '[' + partial.join(',') + ']';
- gap = mind;
- return v;
- }
- // If the replacer is an array, use it to select the members to be stringified.
- if (rep && typeof rep === 'object') {
- length = rep.length;
- for (i = 0; i < length; i += 1) {
- if (typeof rep[i] === 'string') {
- k = rep[i];
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- } else {
- // Otherwise, iterate through all of the keys in the object.
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = str(k, value);
- if (v) {
- partial.push(quote(k) + (gap ? ': ' : ':') + v);
- }
- }
- }
- }
- // Join all of the member texts together, separated with commas,
- // and wrap them in braces.
- v = partial.length === 0
- ? '{}'
- : gap
- ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
- : '{' + partial.join(',') + '}';
- gap = mind;
- return v;
- }
- }
- // If the JSON object does not yet have a stringify method, give it one.
- if (typeof JSON2.stringify !== 'function') {
- JSON2.stringify = function (value, replacer, space) {
- // The stringify method takes a value and an optional replacer, and an optional
- // space parameter, and returns a JSON text. The replacer can be a function
- // that can replace values, or an array of strings that will select the keys.
- // A default replacer method can be provided. Use of the space parameter can
- // produce text that is more easily readable.
- var i;
- gap = '';
- indent = '';
- // If the space parameter is a number, make an indent string containing that
- // many spaces.
- if (typeof space === 'number') {
- for (i = 0; i < space; i += 1) {
- indent += ' ';
- }
- // If the space parameter is a string, it will be used as the indent string.
- } else if (typeof space === 'string') {
- indent = space;
- }
- // If there is a replacer, it must be a function or an array.
- // Otherwise, throw an error.
- rep = replacer;
- if (replacer && typeof replacer !== 'function' &&
- (typeof replacer !== 'object' ||
- typeof replacer.length !== 'number')) {
- throw new Error('JSON2.stringify');
- }
- // Make a fake root object containing our value under the key of ''.
- // Return the result of stringifying the value.
- return str('', {'': value});
- };
- }
- // If the JSON object does not yet have a parse method, give it one.
- if (typeof JSON2.parse !== 'function') {
- JSON2.parse = function (text, reviver) {
- // The parse method takes a text and an optional reviver function, and returns
- // a JavaScript value if the text is a valid JSON text.
- var j;
- function walk(holder, key) {
- // The walk method is used to recursively walk the resulting structure so
- // that modifications can be made.
- var k, v, value = holder[key];
- if (value && typeof value === 'object') {
- for (k in value) {
- if (Object.prototype.hasOwnProperty.call(value, k)) {
- v = walk(value, k);
- if (v !== undefined) {
- value[k] = v;
- } else {
- delete value[k];
- }
- }
- }
- }
- return reviver.call(holder, key, value);
- }
- // Parsing happens in four stages. In the first stage, we replace certain
- // Unicode characters with escape sequences. JavaScript handles many characters
- // incorrectly, either silently deleting them, or treating them as line endings.
- text = String(text);
- cx.lastIndex = 0;
- if (cx.test(text)) {
- text = text.replace(cx, function (a) {
- return '\\u' +
- ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
- });
- }
- // In the second stage, we run the text against regular expressions that look
- // for non-JSON patterns. We are especially concerned with '()' and 'new'
- // because they can cause invocation, and '=' because it can cause mutation.
- // But just to be safe, we want to reject all unexpected forms.
- // We split the second stage into 4 regexp operations in order to work around
- // crippling inefficiencies in IE's and Safari's regexp engines. First we
- // replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
- // replace all simple value tokens with ']' characters. Third, we delete all
- // open brackets that follow a colon or comma or that begin the text. Finally,
- // we look to see that the remaining characters are only whitespace or ']' or
- // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
- if ((new RegExp('^[\\],:{}\\s]*$'))
- .test(text.replace(new RegExp('\\\\(?:["\\\\/bfnrt]|u[0-9a-fA-F]{4})', 'g'), '@')
- .replace(new RegExp('"[^"\\\\\n\r]*"|true|false|null|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?', 'g'), ']')
- .replace(new RegExp('(?:^|:|,)(?:\\s*\\[)+', 'g'), ''))) {
- // In the third stage we use the eval function to compile the text into a
- // JavaScript structure. The '{' operator is subject to a syntactic ambiguity
- // in JavaScript: it can begin a block or an object literal. We wrap the text
- // in parens to eliminate the ambiguity.
- j = eval('(' + text + ')');
- // In the optional fourth stage, we recursively walk the new structure, passing
- // each name/value pair to a reviver function for possible transformation.
- return typeof reviver === 'function'
- ? walk({'': j}, '')
- : j;
- }
- // If the text is not JSON parseable, then a SyntaxError is thrown.
- throw new SyntaxError('JSON2.parse');
- };
- }
- }());
- /************************************************************
- * end JSON
- ************************************************************/
- /*jslint browser:true, plusplus:true, vars:true, nomen:true, evil:true */
- /*global window */
- /*global unescape */
- /*global ActiveXObject */
- /*members encodeURIComponent, decodeURIComponent, getElementsByTagName,
- shift, unshift,
- createElement, appendChild, characterSet, charset,
- addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies,
- cookie, domain, readyState, documentElement, doScroll, title, text,
- location, top, document, referrer, parent, links, href, protocol, name, GearsFactory,
- performance, mozPerformance, msPerformance, webkitPerformance, timing, requestStart,
- responseEnd, event, which, button, srcElement, type, target,
- parentNode, tagName, hostname, className,
- userAgent, cookieEnabled, platform, mimeTypes, enabledPlugin, javaEnabled,
- XMLHttpRequest, ActiveXObject, open, setRequestHeader, onreadystatechange, send, readyState, status,
- getTime, getTimeAlias, setTime, toGMTString, getHours, getMinutes, getSeconds,
- toLowerCase, toUpperCase, charAt, indexOf, lastIndexOf, split, slice,
- onload, src,
- round, random,
- exec,
- res, width, height, devicePixelRatio,
- pdf, qt, realp, wma, dir, fla, java, gears, ag,
- hook, getHook, getVisitorId, getVisitorInfo, setTrackerUrl, appendToTrackingUrl, setSiteId,
- getAttributionInfo, getAttributionCampaignName, getAttributionCampaignKeyword,
- getAttributionReferrerTimestamp, getAttributionReferrerUrl,
- setCustomData, getCustomData,
- setCustomVariable, getCustomVariable, deleteCustomVariable,
- setDownloadExtensions, addDownloadExtensions,
- setDomains, setIgnoreClasses, setRequestMethod,
- setReferrerUrl, setCustomUrl, setAPIUrl, setDocumentTitle,
- setDownloadClasses, setLinkClasses,
- setCampaignNameKey, setCampaignKeywordKey,
- discardHashTag,
- setCookieNamePrefix, setCookieDomain, setCookiePath, setVisitorIdCookie,
- setVisitorCookieTimeout, setSessionCookieTimeout, setReferralCookieTimeout,
- setConversionAttributionFirstReferrer,
- disablePerformanceTracking, setGenerationTimeMs,
- doNotTrack, setDoNotTrack, msDoNotTrack,
- addListener, enableLinkTracking, setLinkTrackingTimer,
- setHeartBeatTimer, killFrame, redirectFile, setCountPreRendered,
- trackGoal, trackLink, trackPageView, trackSiteSearch, trackEvent,
- setEcommerceView, addEcommerceItem, trackEcommerceOrder, trackEcommerceCartUpdate,
- deleteCookies
- */
- /*global _paq:true */
- /*members push */
- /*global Piwik:true */
- /*members addPlugin, getTracker, getAsyncTracker */
- /*global Piwik_Overlay_Client */
- /*members initialize */
- /*global define */
- /*members amd */
- // asynchronous tracker (or proxy)
- if (typeof _paq !== 'object') {
- _paq = [];
- }
- // Piwik singleton and namespace
- if (typeof Piwik !== 'object') {
- Piwik = (function () {
- 'use strict';
- /************************************************************
- * Private data
- ************************************************************/
- var expireDateTime,
- /* plugins */
- plugins = {},
- /* alias frequently used globals for added minification */
- documentAlias = document,
- navigatorAlias = navigator,
- screenAlias = screen,
- windowAlias = window,
- /* performance timing */
- performanceAlias = windowAlias.performance || windowAlias.mozPerformance || windowAlias.msPerformance || windowAlias.webkitPerformance,
- /* DOM Ready */
- hasLoaded = false,
- registeredOnLoadHandlers = [],
- /* encode */
- encodeWrapper = windowAlias.encodeURIComponent,
- /* decode */
- decodeWrapper = windowAlias.decodeURIComponent,
- /* urldecode */
- urldecode = unescape,
- /* asynchronous tracker */
- asyncTracker,
- /* iterator */
- iterator,
- /* local Piwik */
- Piwik;
- /************************************************************
- * Private methods
- ************************************************************/
- /*
- * Is property defined?
- */
- function isDefined(property) {
- // workaround https://github.com/douglascrockford/JSLint/commit/24f63ada2f9d7ad65afc90e6d949f631935c2480
- var propertyType = typeof property;
- return propertyType !== 'undefined';
- }
- /*
- * Is property a function?
- */
- function isFunction(property) {
- return typeof property === 'function';
- }
- /*
- * Is property an object?
- *
- * @return bool Returns true if property is null, an Object, or subclass of Object (i.e., an instanceof String, Date, etc.)
- */
- function isObject(property) {
- return typeof property === 'object';
- }
- /*
- * Is property a string?
- */
- function isString(property) {
- return typeof property === 'string' || property instanceof String;
- }
- /*
- * apply wrapper
- *
- * @param array parameterArray An array comprising either:
- * [ 'methodName', optional_parameters ]
- * or:
- * [ functionObject, optional_parameters ]
- */
- function apply() {
- var i, f, parameterArray;
- for (i = 0; i < arguments.length; i += 1) {
- parameterArray = arguments[i];
- f = parameterArray.shift();
- if (isString(f)) {
- asyncTracker[f].apply(asyncTracker, parameterArray);
- } else {
- f.apply(asyncTracker, parameterArray);
- }
- }
- }
- /*
- * Cross-browser helper function to add event handler
- */
- function addEventListener(element, eventType, eventHandler, useCapture) {
- if (element.addEventListener) {
- element.addEventListener(eventType, eventHandler, useCapture);
- return true;
- }
- if (element.attachEvent) {
- return element.attachEvent('on' + eventType, eventHandler);
- }
- element['on' + eventType] = eventHandler;
- }
- /*
- * Call plugin hook methods
- */
- function executePluginMethod(methodName, callback) {
- var result = '',
- i,
- pluginMethod;
- for (i in plugins) {
- if (Object.prototype.hasOwnProperty.call(plugins, i)) {
- pluginMethod = plugins[i][methodName];
- if (isFunction(pluginMethod)) {
- result += pluginMethod(callback);
- }
- }
- }
- return result;
- }
- /*
- * Handle beforeunload event
- *
- * Subject to Safari's "Runaway JavaScript Timer" and
- * Chrome V8 extension that terminates JS that exhibits
- * "slow unload", i.e., calling getTime() > 1000 times
- */
- function beforeUnloadHandler() {
- var now;
- executePluginMethod('unload');
- /*
- * Delay/pause (blocks UI)
- */
- if (expireDateTime) {
- // the things we do for backwards compatibility...
- // in ECMA-262 5th ed., we could simply use:
- // while (Date.now() < expireDateTime) { }
- do {
- now = new Date();
- } while (now.getTimeAlias() < expireDateTime);
- }
- }
- /*
- * Handler for onload event
- */
- function loadHandler() {
- var i;
- if (!hasLoaded) {
- hasLoaded = true;
- executePluginMethod('load');
- for (i = 0; i < registeredOnLoadHandlers.length; i++) {
- registeredOnLoadHandlers[i]();
- }
- }
- return true;
- }
- /*
- * Add onload or DOM ready handler
- */
- function addReadyListener() {
- var _timer;
- if (documentAlias.addEventListener) {
- addEventListener(documentAlias, 'DOMContentLoaded', function ready() {
- documentAlias.removeEventListener('DOMContentLoaded', ready, false);
- loadHandler();
- });
- } else if (documentAlias.attachEvent) {
- documentAlias.attachEvent('onreadystatechange', function ready() {
- if (documentAlias.readyState === 'complete') {
- documentAlias.detachEvent('onreadystatechange', ready);
- loadHandler();
- }
- });
- if (documentAlias.documentElement.doScroll && windowAlias === windowAlias.top) {
- (function ready() {
- if (!hasLoaded) {
- try {
- documentAlias.documentElement.doScroll('left');
- } catch (error) {
- setTimeout(ready, 0);
- return;
- }
- loadHandler();
- }
- }());
- }
- }
- // sniff for older WebKit versions
- if ((new RegExp('WebKit')).test(navigatorAlias.userAgent)) {
- _timer = setInterval(function () {
- if (hasLoaded || /loaded|complete/.test(documentAlias.readyState)) {
- clearInterval(_timer);
- loadHandler();
- }
- }, 10);
- }
- // fallback
- addEventListener(windowAlias, 'load', loadHandler, false);
- }
- /*
- * Load JavaScript file (asynchronously)
- */
- function loadScript(src, onLoad) {
- var script = documentAlias.createElement('script');
- script.type = 'text/javascript';
- script.src = src;
- if (script.readyState) {
- script.onreadystatechange = function () {
- var state = this.readyState;
- if (state === 'loaded' || state === 'complete') {
- script.onreadystatechange = null;
- onLoad();
- }
- };
- } else {
- script.onload = onLoad;
- }
- documentAlias.getElementsByTagName('head')[0].appendChild(script);
- }
- /*
- * Get page referrer
- */
- function getReferrer() {
- var referrer = '';
- try {
- referrer = windowAlias.top.document.referrer;
- } catch (e) {
- if (windowAlias.parent) {
- try {
- referrer = windowAlias.parent.document.referrer;
- } catch (e2) {
- referrer = '';
- }
- }
- }
- if (referrer === '') {
- referrer = documentAlias.referrer;
- }
- return referrer;
- }
- /*
- * Extract scheme/protocol from URL
- */
- function getProtocolScheme(url) {
- var e = new RegExp('^([a-z]+):'),
- matches = e.exec(url);
- return matches ? matches[1] : null;
- }
- /*
- * Extract hostname from URL
- */
- function getHostName(url) {
- // scheme : // [username [: password] @] hostame [: port] [/ [path] [? query] [# fragment]]
- var e = new RegExp('^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)'),
- matches = e.exec(url);
- return matches ? matches[1] : url;
- }
- /*
- * Extract parameter from URL
- */
- function getParameter(url, name) {
- var regexSearch = "[\\?&#]" + name + "=([^&#]*)";
- var regex = new RegExp(regexSearch);
- var results = regex.exec(url);
- return results ? decodeWrapper(results[1]) : '';
- }
- /*
- * UTF-8 encoding
- */
- function utf8_encode(argString) {
- return urldecode(encodeWrapper(argString));
- }
- /************************************************************
- * sha1
- * - based on sha1 from http://phpjs.org/functions/sha1:512 (MIT / GPL v2)
- ************************************************************/
- function sha1(str) {
- // + original by: Webtoolkit.info (http://www.webtoolkit.info/)
- // + namespaced by: Michael White (http://getsprink.com)
- // + input by: Brett Zamir (http://brett-zamir.me)
- // + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
- // + jslinted by: Anthon Pang (http://piwik.org)
- var
- rotate_left = function (n, s) {
- return (n << s) | (n >>> (32 - s));
- },
- cvt_hex = function (val) {
- var strout = '',
- i,
- v;
- for (i = 7; i >= 0; i--) {
- v = (val >>> (i * 4)) & 0x0f;
- strout += v.toString(16);
- }
- return strout;
- },
- blockstart,
- i,
- j,
- W = [],
- H0 = 0x67452301,
- H1 = 0xEFCDAB89,
- H2 = 0x98BADCFE,
- H3 = 0x10325476,
- H4 = 0xC3D2E1F0,
- A,
- B,
- C,
- D,
- E,
- temp,
- str_len,
- word_array = [];
- str = utf8_encode(str);
- str_len = str.length;
- for (i = 0; i < str_len - 3; i += 4) {
- j = str.charCodeAt(i) << 24 | str.charCodeAt(i + 1) << 16 |
- str.charCodeAt(i + 2) << 8 | str.charCodeAt(i + 3);
- word_array.push(j);
- }
- switch (str_len & 3) {
- case 0:
- i = 0x080000000;
- break;
- case 1:
- i = str.charCodeAt(str_len - 1) << 24 | 0x0800000;
- break;
- case 2:
- i = str.charCodeAt(str_len - 2) << 24 | str.charCodeAt(str_len - 1) << 16 | 0x08000;
- break;
- case 3:
- i = str.charCodeAt(str_len - 3) << 24 | str.charCodeAt(str_len - 2) << 16 | str.charCodeAt(str_len - 1) << 8 | 0x80;
- break;
- }
- word_array.push(i);
- while ((word_array.length & 15) !== 14) {
- word_array.push(0);
- }
- word_array.push(str_len >>> 29);
- word_array.push((str_len << 3) & 0x0ffffffff);
- for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
- for (i = 0; i < 16; i++) {
- W[i] = word_array[blockstart + i];
- }
- for (i = 16; i <= 79; i++) {
- W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
- }
- A = H0;
- B = H1;
- C = H2;
- D = H3;
- E = H4;
- for (i = 0; i <= 19; i++) {
- temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
- E = D;
- D = C;
- C = rotate_left(B, 30);
- B = A;
- A = temp;
- }
- for (i = 20; i <= 39; i++) {
- temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
- E = D;
- D = C;
- C = rotate_left(B, 30);
- B = A;
- A = temp;
- }
- for (i = 40; i <= 59; i++) {
- temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
- E = D;
- D = C;
- C = rotate_left(B, 30);
- B = A;
- A = temp;
- }
- for (i = 60; i <= 79; i++) {
- temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
- E = D;
- D = C;
- C = rotate_left(B, 30);
- B = A;
- A = temp;
- }
- H0 = (H0 + A) & 0x0ffffffff;
- H1 = (H1 + B) & 0x0ffffffff;
- H2 = (H2 + C) & 0x0ffffffff;
- H3 = (H3 + D) & 0x0ffffffff;
- H4 = (H4 + E) & 0x0ffffffff;
- }
- temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
- return temp.toLowerCase();
- }
- /************************************************************
- * end sha1
- ************************************************************/
- /*
- * Fix-up URL when page rendered from search engine cache or translated page
- */
- function urlFixup(hostName, href, referrer) {
- if (hostName === 'translate.googleusercontent.com') { // Google
- if (referrer === '') {
- referrer = href;
- }
- href = getParameter(href, 'u');
- hostName = getHostName(href);
- } else if (hostName === 'cc.bingj.com' || // Bing
- hostName === 'webcache.googleusercontent.com' || // Google
- hostName.slice(0, 5) === '74.6.') { // Yahoo (via Inktomi 74.6.0.0/16)
- href = documentAlias.links[0].href;
- hostName = getHostName(href);
- }
- return [hostName, href, referrer];
- }
- /*
- * Fix-up domain
- */
- function domainFixup(domain) {
- var dl = domain.length;
- // remove trailing '.'
- if (domain.charAt(--dl) === '.') {
- domain = domain.slice(0, dl);
- }
- // remove leading '*'
- if (domain.slice(0, 2) === '*.') {
- domain = domain.slice(1);
- }
- return domain;
- }
- /*
- * Title fixup
- */
- function titleFixup(title) {
- title = title && title.text ? title.text : title;
- if (!isString(title)) {
- var tmp = documentAlias.getElementsByTagName('title');
- if (tmp && isDefined(tmp[0])) {
- title = tmp[0].text;
- }
- }
- return title;
- }
- /************************************************************
- * Page Overlay
- ************************************************************/
- function getPiwikUrlForOverlay(trackerUrl, apiUrl) {
- if (apiUrl) {
- return apiUrl;
- }
- if (trackerUrl.slice(-9) === 'piwik.php') {
- trackerUrl = trackerUrl.slice(0, trackerUrl.length - 9);
- }
- return trackerUrl;
- }
- /*
- * Check whether this is a page overlay session
- *
- * @return boolean
- *
- * {@internal side-effect: modifies window.name }}
- */
- function isOverlaySession(configTrackerSiteId) {
- var windowName = 'Piwik_Overlay';
- // check whether we were redirected from the piwik overlay plugin
- var referrerRegExp = new RegExp('index\\.php\\?module=Overlay&action=startOverlaySession'
- + '&idsite=([0-9]+)&period=([^&]+)&date=([^&]+)$');
- var match = referrerRegExp.exec(documentAlias.referrer);
- if (match) {
- // check idsite
- var idsite = match[1];
- if (idsite !== String(configTrackerSiteId)) {
- return false;
- }
- // store overlay session info in window name
- var period = match[2],
- date = match[3];
- windowAlias.name = windowName + '###' + period + '###' + date;
- }
- // retrieve and check data from window name
- var windowNameParts = windowAlias.name.split('###');
- return windowNameParts.length === 3 && windowNameParts[0] === windowName;
- }
- /*
- * Inject the script needed for page overlay
- */
- function injectOverlayScripts(configTrackerUrl, configApiUrl, configTrackerSiteId) {
- var windowNameParts = windowAlias.name.split('###'),
- period = windowNameParts[1],
- date = windowNameParts[2],
- piwikUrl = getPiwikUrlForOverlay(configTrackerUrl, configApiUrl);
- loadScript(
- piwikUrl + 'plugins/Overlay/client/client.js?v=1',
- function () {
- Piwik_Overlay_Client.initialize(piwikUrl, configTrackerSiteId, period, date);
- }
- );
- }
- /************************************************************
- * End Page Overlay
- ************************************************************/
- /*
- * Piwik Tracker class
- *
- * trackerUrl and trackerSiteId are optional arguments to the constructor
- *
- * See: Tracker.setTrackerUrl() and Tracker.setSiteId()
- */
- function Tracker(trackerUrl, siteId) {
- /************************************************************
- * Private members
- ************************************************************/
- var
- /*<DEBUG>*/
- /*
- * registered test hooks
- */
- registeredHooks = {},
- /*</DEBUG>*/
- // Current URL and Referrer URL
- locationArray = urlFixup(documentAlias.domain, windowAlias.location.href, getReferrer()),
- domainAlias = domainFixup(locationArray[0]),
- locationHrefAlias = locationArray[1],
- configReferrerUrl = locationArray[2],
- // Request method (GET or POST)
- configRequestMethod = 'GET',
- // Tracker URL
- configTrackerUrl = trackerUrl || '',
- // API URL (only set if it differs from the Tracker URL)
- configApiUrl = '',
- // This string is appended to the Tracker URL Request (eg. to send data that is not handled by the existin setters/getters)
- configAppendToTrackingUrl = '',
- // Site ID
- configTrackerSiteId = siteId || '',
- // Document URL
- configCustomUrl,
- // Document title
- configTitle = documentAlias.title,
- // Extensions to be treated as download links
- 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',
- // Hosts or alias(es) to not treat as outlinks
- configHostsAlias = [domainAlias],
- // HTML anchor element classes to not track
- configIgnoreClasses = [],
- // HTML anchor element classes to treat as downloads
- configDownloadClasses = [],
- // HTML anchor element classes to treat at outlinks
- configLinkClasses = [],
- // Maximum delay to wait for web bug image to be fetched (in milliseconds)
- configTrackerPause = 500,
- // Minimum visit time after initial page view (in milliseconds)
- configMinimumVisitTime,
- // Recurring heart beat after initial ping (in milliseconds)
- configHeartBeatTimer,
- // Disallow hash tags in URL
- configDiscardHashTag,
- // Custom data
- configCustomData,
- // Campaign names
- configCampaignNameParameters = [ 'pk_campaign', 'piwik_campaign', 'utm_campaign', 'utm_source', 'utm_medium' ],
- // Campaign keywords
- configCampaignKeywordParameters = [ 'pk_kwd', 'piwik_kwd', 'utm_term' ],
- // First-party cookie name prefix
- configCookieNamePrefix = '_pk_',
- // First-party cookie domain
- // User agent defaults to origin hostname
- configCookieDomain,
- // First-party cookie path
- // Default is user agent defined.
- configCookiePath,
- // Cookies are disabled
- configCookiesDisabled = false,
- // Do Not Track
- configDoNotTrack,
- // Count sites which are pre-rendered
- configCountPreRendered,
- // Do we attribute the conversion to the first referrer or the most recent referrer?
- configConversionAttributionFirstReferrer,
- // Life of the visitor cookie (in milliseconds)
- configVisitorCookieTimeout = 63072000000, // 2 years
- // Life of the session cookie (in milliseconds)
- configSessionCookieTimeout = 1800000, // 30 minutes
- // Life of the referral cookie (in milliseconds)
- configReferralCookieTimeout = 15768000000, // 6 months
- // Is performance tracking enabled
- configPerformanceTrackingEnabled = true,
- // Generation time set from the server
- configPerformanceGenerationTime = 0,
- // Custom Variables read from cookie, scope "visit"
- customVariables = false,
- // Custom Variables, scope "page"
- customVariablesPage = {},
- // Custom Variables, scope "event"
- customVariablesEvent = {},
- // Custom Variables names and values are each truncated before being sent in the request or recorded in the cookie
- customVariableMaximumLength = 200,
- // Ecommerce items
- ecommerceItems = {},
- // Browser features via client-side data collection
- browserFeatures = {},
- // Guard against installing the link tracker more than once per Tracker instance
- linkTrackingInstalled = false,
- // Guard against installing the activity tracker more than once per Tracker instance
- activityTrackingInstalled = false,
- // Last activity timestamp
- lastActivityTime,
- // Internal state of the pseudo click handler
- lastButton,
- lastTarget,
- // Hash function
- hash = sha1,
- // Domain hash value
- domainHash,
- // Visitor UUID
- visitorUUID;
- /*
- * Set cookie value
- */
- function setCookie(cookieName, value, msToExpire, path, domain, secure) {
- if (configCookiesDisabled) {
- return;
- }
- var expiryDate;
- // relative time to expire in milliseconds
- if (msToExpire) {
- expiryDate = new Date();
- expiryDate.setTime(expiryDate.getTime() + msToExpire);
- }
- documentAlias.cookie = cookieName + '=' + encodeWrapper(value) +
- (msToExpire ? ';expires=' + expiryDate.toGMTString() : '') +
- ';path=' + (path || '/') +
- (domain ? ';domain=' + domain : '') +
- (secure ? ';secure' : '');
- }
- /*
- * Get cookie value
- */
- function getCookie(cookieName) {
- if (configCookiesDisabled) {
- return 0;
- }
- var cookiePattern = new RegExp('(^|;)[ ]*' + cookieName + '=([^;]*)'),
- cookieMatch = cookiePattern.exec(documentAlias.cookie);
- return cookieMatch ? decodeWrapper(cookieMatch[2]) : 0;
- }
- /*
- * Removes hash tag from the URL
- *
- * URLs are purified before being recorded in the cookie,
- * or before being sent as GET parameters
- */
- function purify(url) {
- var targetPattern;
- if (configDiscardHashTag) {
- targetPattern = new RegExp('#.*');
- return url.replace(targetPattern, '');
- }
- return url;
- }
- /*
- * Resolve relative reference
- *
- * Note: not as described in rfc3986 section 5.2
- */
- function resolveRelativeReference(baseUrl, url) {
- var protocol = getProtocolScheme(url),
- i;
- if (protocol) {
- return url;
- }
- if (url.slice(0, 1) === '/') {
- return getProtocolScheme(baseUrl) + '://' + getHostName(baseUrl) + url;
- }
- baseUrl = purify(baseUrl);
- i = baseUrl.indexOf('?');
- if (i >= 0) {
- baseUrl = baseUrl.slice(0, i);
- }
- i = baseUrl.lastIndexOf('/');
- if (i !== baseUrl.length - 1) {
- baseUrl = baseUrl.slice(0, i + 1);
- }
- return baseUrl + url;
- }
- /*
- * Is the host local? (i.e., not an outlink)
- */
- function isSiteHostName(hostName) {
- var i,
- alias,
- offset;
- for (i = 0; i < configHostsAlias.length; i++) {
- alias = domainFixup(configHostsAlias[i].toLowerCase());
- if (hostName === alias) {
- return true;
- }
- if (alias.slice(0, 1) === '.') {
- if (hostName === alias.slice(1)) {
- return true;
- }
- offset = hostName.length - alias.length;
- if ((offset > 0) && (hostName.slice(offset) === alias)) {
- return true;
- }
- }
- }
- return false;
- }
- /*
- * Send image request to Piwik server using GET.
- * The infamous web bug (or beacon) is a transparent, single pixel (1x1) image
- */
- function getImage(request) {
- var image = new Image(1, 1);
- image.onload = function () {
- iterator = 0; // To avoid JSLint warning of empty block
- };
- image.src = configTrackerUrl + (configTrackerUrl.indexOf('?') < 0 ? '?' : '&') + request;
- }
- /*
- * POST request to Piwik server using XMLHttpRequest.
- */
- function sendXmlHttpRequest(request) {
- try {
- // we use the progid Microsoft.XMLHTTP because
- // IE5.5 included MSXML 2.5; the progid MSXML2.XMLHTTP
- // is pinned to MSXML2.XMLHTTP.3.0
- var xhr = windowAlias.XMLHttpRequest
- ? new windowAlias.XMLHttpRequest()
- : windowAlias.ActiveXObject
- ? new ActiveXObject('Microsoft.XMLHTTP')
- : null;
- xhr.open('POST', configTrackerUrl, true);
- // fallback on error
- xhr.onreadystatechange = function () {
- if (this.readyState === 4 && this.status !== 200) {
- getImage(request);
- }
- };
- // see XMLHttpRequest Level 2 spec, section 4.7.2 for invalid headers
- // @link http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
- xhr.send(request);
- } catch (e) {
- // fallback
- getImage(request);
- }
- }
- /*
- * Send request
- */
- function sendRequest(request, delay) {
- var now = new Date();
- if (!configDoNotTrack) {
- if (configRequestMethod === 'POST') {
- sendXmlHttpRequest(request);
- } else {
- getImage(request);
- }
- expireDateTime = now.getTime() + delay;
- }
- }
- /*
- * Get cookie name with prefix and domain hash
- */
- function getCookieName(baseName) {
- // NOTE: If the cookie name is changed, we must also update the PiwikTracker.php which
- // will attempt to discover first party cookies. eg. See the PHP Client method getVisitorId()
- return configCookieNamePrefix + baseName + '.' + configTrackerSiteId + '.' + domainHash;
- }
- /*
- * Does browser have cookies enabled (for this site)?
- */
- function hasCookies() {
- if (configCookiesDisabled) {
- return '0';
- }
- if (!isDefined(navigatorAlias.cookieEnabled)) {
- var testCookieName = getCookieName('testcookie');
- setCookie(testCookieName, '1');
- return getCookie(testCookieName) === '1' ? '1' : '0';
- }
- return navigatorAlias.cookieEnabled ? '1' : '0';
- }
- /*
- * Update domain hash
- */
- function updateDomainHash() {
- domainHash = hash((configCookieDomain || domainAlias) + (configCookiePath || '/')).slice(0, 4); // 4 hexits = 16 bits
- }
- /*
- * Inits the custom variables object
- */
- function getCustomVariablesFromCookie() {
- var cookieName = getCookieName('cvar'),
- cookie = getCookie(cookieName);
- if (cookie.length) {
- cookie = JSON2.parse(cookie);
- if (isObject(cookie)) {
- return cookie;
- }
- }
- return {};
- }
- /*
- * Lazy loads the custom variables from the cookie, only once during this page view
- */
- function loadCustomVariables() {
- if (customVariables === false) {
- customVariables = getCustomVariablesFromCookie();
- }
- }
- /*
- * Process all "activity" events.
- * For performance, this function must have low overhead.
- */
- function activityHandler() {
- var now = new Date();
- lastActivityTime = now.getTime();
- }
- /*
- * Sets the Visitor ID cookie: either the first time loadVisitorIdCookie is called
- * or when there is a new visit or a new page view
- */
- function setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs, lastEcommerceOrderTs) {
- setCookie(getCookieName('id'), uuid + '.' + createTs + '.' + visitCount + '.'