Skip to content
Extraits de code Groupes Projets
piwik.js 115 ko
Newer Older
  • Learn to ignore specific revisions
  • robocoder's avatar
    robocoder a validé
     * JavaScript tracking client
     *
    
     * @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)
    
    mattab's avatar
    mattab a validé
    // Refer to README.md for build instructions when minifying this file for distribution.
    
    robocoder's avatar
    robocoder a validé
    
    
    /*
     * 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
    
     ************************************************************/
    
    robocoder's avatar
    robocoder a validé
    /*jslint evil: true, regexp: false, bitwise: true */
    
    /*global JSON2:true */
    /*members "", "\b", "\t", "\n", "\f", "\r", "\"", "\\", apply,
    
    robocoder's avatar
    robocoder a validé
        call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
        getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
        lastIndex, length, parse, prototype, push, replace, slice, stringify,
    
        test, toJSON, toString, valueOf,
        objectToJSON
    
    robocoder's avatar
    robocoder a validé
    */
    
    robocoder's avatar
    robocoder a validé
    
    // 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 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()     + '-' +
    
    robocoder's avatar
    robocoder a validé
                        f(value.getUTCMonth() + 1) + '-' +
                        f(value.getUTCDate())      + 'T' +
                        f(value.getUTCHours())     + ':' +
                        f(value.getUTCMinutes())   + ':' +
    
            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);
            }
    
    robocoder's avatar
    robocoder a validé
        var cx = new RegExp('[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]', 'g'),
    
    robocoder's avatar
    robocoder a validé
        // hack: workaround Snort false positive (sid 8443)
    
    robocoder's avatar
    robocoder a validé
            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(',') + ']';
    
                    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) {
    
    robocoder's avatar
    robocoder a validé
                        if (typeof rep[i] === 'string') {
                            k = rep[i];
    
                            if (v) {
                                partial.push(quote(k) + (gap ? ': ' : ':') + v);
                            }
                        }
                    }
                } else {
    
    // Otherwise, iterate through all of the keys in the object.
    
                    for (k in value) {
    
    robocoder's avatar
    robocoder a validé
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
    
                            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(',') + '}';
    
                return v;
            }
        }
    
    // If the JSON object does not yet have a stringify method, give it one.
    
    
    robocoder's avatar
    robocoder a validé
        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')) {
    
                }
    
    // 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.
    
    
    robocoder's avatar
    robocoder a validé
        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) {
    
    robocoder's avatar
    robocoder a validé
                            if (Object.prototype.hasOwnProperty.call(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'), '@')
    
    robocoder's avatar
    robocoder a validé
                            .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.
    
    
            };
        }
    }());
    /************************************************************
     * end JSON
     ************************************************************/
    
    
    robocoder's avatar
    robocoder a validé
    /*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,
    
    robocoder's avatar
    robocoder a validé
        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,
    
    robocoder's avatar
    robocoder a validé
        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,
    
    robocoder's avatar
    robocoder a validé
        round, random,
        exec,
    
        res, width, height, devicePixelRatio,
    
    robocoder's avatar
    robocoder a validé
        pdf, qt, realp, wma, dir, fla, java, gears, ag,
    
    mattab's avatar
    mattab a validé
        hook, getHook, getVisitorId, getVisitorInfo, setTrackerUrl, appendToTrackingUrl, setSiteId,
    
    robocoder's avatar
    robocoder a validé
        getAttributionInfo, getAttributionCampaignName, getAttributionCampaignKeyword,
        getAttributionReferrerTimestamp, getAttributionReferrerUrl,
        setCustomData, getCustomData,
        setCustomVariable, getCustomVariable, deleteCustomVariable,
        setDownloadExtensions, addDownloadExtensions,
        setDomains, setIgnoreClasses, setRequestMethod,
    
        setReferrerUrl, setCustomUrl, setAPIUrl, setDocumentTitle,
    
    robocoder's avatar
    robocoder a validé
        setDownloadClasses, setLinkClasses,
        setCampaignNameKey, setCampaignKeywordKey,
        discardHashTag,
        setCookieNamePrefix, setCookieDomain, setCookiePath, setVisitorIdCookie,
        setVisitorCookieTimeout, setSessionCookieTimeout, setReferralCookieTimeout,
        setConversionAttributionFirstReferrer,
        doNotTrack, setDoNotTrack, msDoNotTrack,
        addListener, enableLinkTracking, setLinkTrackingTimer,
        setHeartBeatTimer, killFrame, redirectFile, setCountPreRendered,
    
        trackGoal, trackLink, trackPageView, trackSiteSearch,
    
        setEcommerceView, addEcommerceItem, trackEcommerceOrder, trackEcommerceCartUpdate
    
    /*global _paq:true */
    /*members push */
    /*global Piwik:true */
    /*members addPlugin, getTracker, getAsyncTracker */
    
    /*global Piwik_Overlay_Client */
    /*members initialize */
    /*global define */
    /*members amd */
    
    robocoder's avatar
    robocoder a validé
    
    
    // asynchronous tracker (or proxy)
    if (typeof _paq !== 'object') {
        _paq = [];
    }
    
    // Piwik singleton and namespace
    if (typeof Piwik !== 'object') {
        Piwik = (function () {
            'use strict';
    
    robocoder's avatar
    robocoder a validé
    
            /************************************************************
             * 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,
    
    robocoder's avatar
    robocoder a validé
    
                /* DOM Ready */
                hasLoaded = false,
                registeredOnLoadHandlers = [],
    
                /* encode */
                encodeWrapper = windowAlias.encodeURIComponent,
    
                /* decode */
                decodeWrapper = windowAlias.decodeURIComponent,
    
                /* urldecode */
                urldecode = unescape,
    
                /* asynchronous tracker */
                asyncTracker,
    
                /* iterator */
    
    robocoder's avatar
    robocoder a validé
    
            /************************************************************
             * Private methods
             ************************************************************/
    
            /*
             * Is property defined?
             */
            function isDefined(property) {
                // workaround https://github.com/douglascrockford/JSLint/commit/24f63ada2f9d7ad65afc90e6d949f631935c2480
    
                var propertyType = typeof property;
    
                return propertyType !== 'undefined';
    
    robocoder's avatar
    robocoder a validé
            }
    
            /*
             * 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);
    
    robocoder's avatar
    robocoder a validé
                    return true;
                }
    
    robocoder's avatar
    robocoder a validé
                if (element.attachEvent) {
                    return element.attachEvent('on' + eventType, eventHandler);
                }
    
    robocoder's avatar
    robocoder a validé
                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];
    
    robocoder's avatar
    robocoder a validé
                        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]();
                    }
                }
    
    robocoder's avatar
    robocoder a validé
                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);
    
    robocoder's avatar
    robocoder a validé
                                    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);
            }
    
    
    robocoder's avatar
    robocoder a validé
            /*
             * 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 = '';
                        }
                    }
                }
    
    robocoder's avatar
    robocoder a validé
                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) {
                // scheme : // [username [: password] @] hostame [: port] [/ [path] [? query] [# fragment]]
                var e = new RegExp('^(?:https?|ftp)(?::/*(?:[^?]+)[?])([^#]+)'),
                    matches = e.exec(url),
                    f = new RegExp('(?:^|&)' + name + '=([^&]*)'),
                    result = matches ? f.exec(matches[1]) : 0;
    
                return result ? decodeWrapper(result[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)
             ************************************************************/
    
    robocoder's avatar
    robocoder a validé
            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 str = '',
                            i,
                            v;
    
                        for (i = 7; i >= 0; i--) {
                            v = (val >>> (i * 4)) & 0x0f;
                            str += v.toString(16);
                        }
    
    robocoder's avatar
    robocoder a validé
                        return str;
                    },
    
                    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);
    
    robocoder's avatar
    robocoder a validé
                return temp.toLowerCase();
            }
    
    robocoder's avatar
    robocoder a validé
            /************************************************************
             * 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
    
    robocoder's avatar
    robocoder a validé
                    if (referrer === '') {
                        referrer = href;
                    }
    
    robocoder's avatar
    robocoder a validé
                    href = getParameter(href, 'u');
                    hostName = getHostName(href);
    
                } else if (hostName === 'cc.bingj.com' ||                   // Bing
    
    robocoder's avatar
    robocoder a validé
                        hostName === 'webcache.googleusercontent.com' ||    // Google
    
                        hostName.slice(0, 5) === '74.6.') {                 // Yahoo (via Inktomi 74.6.0.0/16)
    
    robocoder's avatar
    robocoder a validé
                    href = documentAlias.links[0].href;
                    hostName = getHostName(href);
                }
    
    robocoder's avatar
    robocoder a validé
                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);
                }
    
    robocoder's avatar
    robocoder a validé
                // remove leading '*'
                if (domain.slice(0, 2) === '*.') {
                    domain = domain.slice(1);
                }
    
    robocoder's avatar
    robocoder a validé
                return domain;
            }
    
            /*
             * Title fixup
             */
            function titleFixup(title) {
    
                title = title && title.text ? title.text : title;
    
    robocoder's avatar
    robocoder a validé
    
    
                if (!isString(title)) {
    
    robocoder's avatar
    robocoder a validé
                    var tmp = documentAlias.getElementsByTagName('title');
    
    robocoder's avatar
    robocoder a validé
                    if (tmp && isDefined(tmp[0])) {
                        title = tmp[0].text;
                    }
                }
    
    robocoder's avatar
    robocoder a validé
                return title;
            }
    
    mattpiwik's avatar
    mattpiwik a validé
    
    
            /************************************************************
             * Page Overlay
             ************************************************************/
    
    
            function getPiwikUrlForOverlay(trackerUrl, apiUrl) {
    
                if (apiUrl) {
                    return apiUrl;
                }
    
    
    mattab's avatar
    mattab a validé
                if (trackerUrl.slice(-9) === 'piwik.php') {
                    trackerUrl = trackerUrl.slice(0, trackerUrl.length - 9);
                }
    
    mattab's avatar
    mattab a validé
                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';