/*!
* Piwik - free/libre analytics platform
*
* 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/ BSD-3 Clause (also in js/LICENSE.txt)
* @license magnet:?xt=urn:btih:c80d50af7d3db9be66a4d0a86db0286e4fd33292&dn=bsd-3-clause.txt BSD-3-Clause
*/
// 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, white: 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, piwikAsyncInit,
createElement, appendChild, characterSet, charset,
addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies,
cookie, domain, readyState, documentElement, doScroll, title, text,
location, top, onerror, 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, setUserId, getUserId, setSiteId, setTrackerUrl, appendToTrackingUrl, getRequest, addPlugin,
getAttributionInfo, getAttributionCampaignName, getAttributionCampaignKeyword,
getAttributionReferrerTimestamp, getAttributionReferrerUrl,
setCustomData, getCustomData,
setCustomRequestProcessing,
setCustomVariable, getCustomVariable, deleteCustomVariable, storeCustomVariablesInCookie,
setDownloadExtensions, addDownloadExtensions,
setDomains, setIgnoreClasses, setRequestMethod, setRequestContentType,
setReferrerUrl, setCustomUrl, setAPIUrl, setDocumentTitle,
setDownloadClasses, setLinkClasses,
setCampaignNameKey, setCampaignKeywordKey,
discardHashTag,
setCookieNamePrefix, setCookieDomain, setCookiePath, setVisitorIdCookie,
setVisitorCookieTimeout, setSessionCookieTimeout, setReferralCookieTimeout,
setConversionAttributionFirstReferrer,
disablePerformanceTracking, setGenerationTimeMs,
doNotTrack, setDoNotTrack, msDoNotTrack,
addListener, enableLinkTracking, enableJSErrorTracking, setLinkTrackingTimer,
setHeartBeatTimer, killFrame, redirectFile, setCountPreRendered,
trackGoal, trackLink, trackPageView, trackSiteSearch, trackEvent,
setEcommerceView, addEcommerceItem, trackEcommerceOrder, trackEcommerceCartUpdate,
deleteCookies, offsetTop, offsetLeft, offsetHeight, offsetWidth, nodeType, defaultView,
innerHTML, scrollLeft, scrollTop, currentStyle, getComputedStyle, querySelectorAll, splice,
getAttribute, hasAttribute, attributes, nodeName, findContentNodes, findContentNodes, findContentNodesWithinNode,
findPieceNode, findTargetNodeNoDefault, findTargetNode, findContentPiece, children, hasNodeCssClass,
getAttributeValueFromNode, hasNodeAttributeWithValue, hasNodeAttribute, findNodesByTagName, findMultiple,
makeNodesUnique, concat, find, htmlCollectionToArray, offsetParent, value, nodeValue, findNodesHavingAttribute,
findFirstNodeHavingAttribute, findFirstNodeHavingAttributeWithValue, getElementsByClassName,
findNodesHavingCssClass, findFirstNodeHavingClass, isLinkElement, findParentContentNode, removeDomainIfIsInLink,
findContentName, findMediaUrlInNode, toAbsoluteUrl, findContentTarget, getLocation, origin, host, isSameDomain,
search, trim, getBoundingClientRect, bottom, right, left, innerWidth, innerHeight, clientWidth, clientHeight,
isOrWasNodeInViewport, isNodeVisible, buildInteractionRequestParams, buildImpressionRequestParams,
shouldIgnoreInteraction, setHrefAttribute, setAttribute, buildContentBlock, collectContent, setLocation,
CONTENT_ATTR, CONTENT_CLASS, CONTENT_NAME_ATTR, CONTENT_PIECE_ATTR, CONTENT_PIECE_CLASS,
CONTENT_TARGET_ATTR, CONTENT_TARGET_CLASS, CONTENT_IGNOREINTERACTION_ATTR, CONTENT_IGNOREINTERACTION_CLASS,
trackCallbackOnLoad, trackCallbackOnReady, buildContentImpressionsRequests, wasContentImpressionAlreadyTracked,
getQuery, getContent, getContentImpressionsRequestsFromNodes, buildContentInteractionTrackingRedirectUrl,
buildContentInteractionRequestNode, buildContentInteractionRequest, buildContentImpressionRequest,
appendContentInteractionToRequestIfPossible, setupInteractionsTracking, trackContentImpressionClickInteraction,
internalIsNodeVisible, clearTrackedContentImpressions, getTrackerUrl, trackAllContentImpressions,
getTrackedContentImpressions, getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet,
contentInteractionTrackingSetupDone, contains, match, pathname, piece, trackContentInteractionNode,
trackContentInteractionNode, trackContentImpressionsWithinNode, trackContentImpression,
enableTrackOnlyVisibleContent, trackContentInteraction, clearEnableTrackOnlyVisibleContent,
trackVisibleContentImpressions, isTrackOnlyVisibleContentEnabled, port, isUrlToCurrentDomain,
isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink, getConfigDownloadExtensions, disableLinkTracking,
substr, setAnyAttribute, wasContentTargetAttrReplaced, max, abs, childNodes, compareDocumentPosition, body,
getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout
*/
/*global _paq:true */
/*members push */
/*global Piwik:true */
/*members addPlugin, getTracker, getAsyncTracker */
/*global Piwik_Overlay_Client */
/*global AnalyticsTracker:true */
/*members initialize */
/*global define */
/*members amd */
/*global console:true */
/*members error */
// 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;
}
function getChildrenFromNode(node)
{
if (!node) {
return [];
}
if (!isDefined(node.children) && isDefined(node.childNodes)) {
return node.children;
}
if (isDefined(node.children)) {
return node.children;
}
return [];
}
function containsNodeElement(node, containedNode)
{
if (!node || !containedNode) {
return false;
}
if (node.contains) {
return node.contains(containedNode);
}
if (node === containedNode) {
return true;
}
if (node.compareDocumentPosition) {
return !!(node.compareDocumentPosition(containedNode) & 16);
}
return false;
}
// Polyfill for IndexOf for IE6-IE8
function indexOfArray(theArray, searchElement)
{
if (theArray && theArray.indexOf) {
return theArray.indexOf(searchElement);
}
// 1. Let O be the result of calling ToObject passing
// the this value as the argument.
if (!isDefined(theArray) || theArray === null) {
return -1;
}
if (!theArray.length) {
return -1;
}
var len = theArray.length;
if (len === 0) {
return -1;
}
var k = 0;
// 9. Repeat, while k < len
while (k < len) {
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the
// HasProperty internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
// i. Let elementK be the result of calling the Get
// internal method of O with the argument ToString(k).
// ii. Let same be the result of applying the
// Strict Equality Comparison Algorithm to
// searchElement and elementK.
// iii. If same is true, return k.
if (theArray[k] === searchElement) {
return k;
}
k++;
}
return -1;
}
/************************************************************
* Element Visiblility
************************************************************/
/**
* Author: Jason Farrell
* Author URI: http://useallfive.com/
*
* Description: Checks if a DOM element is truly visible.
* Package URL: https://github.com/UseAllFive/true-visibility
* License: MIT (https://github.com/UseAllFive/true-visibility/blob/master/LICENSE.txt)
*/
function isVisible(node) {
if (!node) {
return false;
}
//-- Cross browser method to get style properties:
function _getStyle(el, property) {
if (windowAlias.getComputedStyle) {
return documentAlias.defaultView.getComputedStyle(el,null)[property];
}
if (el.currentStyle) {
return el.currentStyle[property];
}
}
function _elementInDocument(element) {
element = element.parentNode;
while (element) {
if (element === documentAlias) {
return true;
}
element = element.parentNode;
}
return false;
}
/**
* Checks if a DOM element is visible. Takes into
* consideration its parents and overflow.
*
* @param (el) the DOM element to check if is visible
*
* These params are optional that are sent in recursively,
* you typically won't use these:
*
* @param (t) Top corner position number
* @param (r) Right corner position number
* @param (b) Bottom corner position number
* @param (l) Left corner position number
* @param (w) Element width number
* @param (h) Element height number
*/
function _isVisible(el, t, r, b, l, w, h) {
var p = el.parentNode,
VISIBLE_PADDING = 1; // has to be visible at least one px of the element
if (!_elementInDocument(el)) {
return false;
}
//-- Return true for document node
if (9 === p.nodeType) {
return true;
}
//-- Return false if our element is invisible
if (
'0' === _getStyle(el, 'opacity') ||
'none' === _getStyle(el, 'display') ||
'hidden' === _getStyle(el, 'visibility')
) {
return false;
}
if (!isDefined(t) ||
!isDefined(r) ||
!isDefined(b) ||
!isDefined(l) ||
!isDefined(w) ||
!isDefined(h)) {
t = el.offsetTop;
l = el.offsetLeft;
b = t + el.offsetHeight;
r = l + el.offsetWidth;
w = el.offsetWidth;
h = el.offsetHeight;
}
if (node === el && (0 === h || 0 === w) && 'hidden' === _getStyle(el, 'overflow')) {
return false;
}
//-- If we have a parent, let's continue:
if (p) {
//-- Check if the parent can hide its children.
if (('hidden' === _getStyle(p, 'overflow') || 'scroll' === _getStyle(p, 'overflow'))) {
//-- Only check if the offset is different for the parent
if (
//-- If the target element is to the right of the parent elm
l + VISIBLE_PADDING > p.offsetWidth + p.scrollLeft ||
//-- If the target element is to the left of the parent elm
l + w - VISIBLE_PADDING < p.scrollLeft ||
//-- If the target element is under the parent elm
t + VISIBLE_PADDING > p.offsetHeight + p.scrollTop ||
//-- If the target element is above the parent elm
t + h - VISIBLE_PADDING < p.scrollTop
) {
//-- Our target element is out of bounds:
return false;
}
}
//-- Add the offset parent's left/top coords to our element's offset:
if (el.offsetParent === p) {
l += p.offsetLeft;
t += p.offsetTop;
}
//-- Let's recursively check upwards:
return _isVisible(p, t, r, b, l, w, h);
}
return true;
}
return _isVisible(node);
}
/************************************************************
* Query
************************************************************/
var query = {
htmlCollectionToArray: function (foundNodes)
{
var nodes = [], index;
if (!foundNodes || !foundNodes.length) {
return nodes;
}
for (index = 0; index < foundNodes.length; index++) {
nodes.push(foundNodes[index]);
}
return nodes;
},
find: function (selector)
{
// we use querySelectorAll only on document, not on nodes because of its unexpected behavior. See for
// instance http://stackoverflow.com/questions/11503534/jquery-vs-document-queryselectorall and
// http://jsfiddle.net/QdMc5/ and http://ejohn.org/blog/thoughts-on-queryselectorall
if (!document.querySelectorAll || !selector) {
return []; // we do not support all browsers
}
var foundNodes = document.querySelectorAll(selector);
return this.htmlCollectionToArray(foundNodes);
},
findMultiple: function (selectors)
{
if (!selectors || !selectors.length) {
return [];
}
var index, foundNodes;
var nodes = [];
for (index = 0; index < selectors.length; index++) {
foundNodes = this.find(selectors[index]);
nodes = nodes.concat(foundNodes);
}
nodes = this.makeNodesUnique(nodes);
return nodes;
},
findNodesByTagName: function (node, tagName)
{
if (!node || !tagName || !node.getElementsByTagName) {
return [];
}
var foundNodes = node.getElementsByTagName(tagName);
return this.htmlCollectionToArray(foundNodes);
},
makeNodesUnique: function (nodes)
{
var copy = [].concat(nodes);
nodes.sort(function(n1, n2){
if (n1 === n2) {
return 0;
}
var index1 = indexOfArray(copy, n1);
var index2 = indexOfArray(copy, n2);
if (index1 === index2) {
return 0;
}
return index1 > index2 ? -1 : 1;
});
if (nodes.length <= 1) {
return nodes;
}
var index = 0;
var numDuplicates = 0;
var duplicates = [];
var node;
node = nodes[index++];
while (node) {
if (node === nodes[index]) {
numDuplicates = duplicates.push(index);
}
node = nodes[index++] || null;
}
while (numDuplicates--) {
nodes.splice(duplicates[numDuplicates], 1);
}
return nodes;
},
getAttributeValueFromNode: function (node, attributeName)
{
if (!this.hasNodeAttribute(node, attributeName)) {
return;
}
if (node && node.getAttribute) {
return node.getAttribute(attributeName);
}
if (!node || !node.attributes) {
return;
}
var typeOfAttr = (typeof node.attributes[attributeName]);
if ('undefined' === typeOfAttr) {
return;
}
if (node.attributes[attributeName].value) {
return node.attributes[attributeName].value; // nodeValue is deprecated ie Chrome
}
if (node.attributes[attributeName].nodeValue) {
return node.attributes[attributeName].nodeValue;
}
var index;
var attrs = node.attributes;
if (!attrs) {
return;
}
for (index = 0; index < attrs.length; index++) {
if (attrs[index].nodeName === attributeName) {
return attrs[index].nodeValue;
}
}
return null;
},
hasNodeAttributeWithValue: function (node, attributeName)
{
var value = this.getAttributeValueFromNode(node, attributeName);
return !!value;
},
hasNodeAttribute: function (node, attributeName)
{
if (node && node.hasAttribute) {
return node.hasAttribute(attributeName);
}
if (node && node.attributes) {
var typeOfAttr = (typeof node.attributes[attributeName]);
return 'undefined' !== typeOfAttr;
}
return false;
},
hasNodeCssClass: function (node, className)
{
if (node && className && node.className) {
var classes = node.className.split(' ');
if (-1 !== indexOfArray(classes, className)) {
return true;
}
}
return false;
},
findNodesHavingAttribute: function (nodeToSearch, attributeName, nodes)
{
if (!nodes) {
nodes = [];
}
if (!nodeToSearch || !attributeName) {
return nodes;
}
var children = getChildrenFromNode(nodeToSearch);
if (!children || !children.length) {
return nodes;
}
var index, child;
for (index = 0; index < children.length; index++) {
child = children[index];
if (this.hasNodeAttribute(child, attributeName)) {
nodes.push(child);
}
nodes = this.findNodesHavingAttribute(child, attributeName, nodes);
}
return nodes;
},
findFirstNodeHavingAttribute: function (node, attributeName)
{
if (!node || !attributeName) {
return;
}
if (this.hasNodeAttribute(node, attributeName)) {
return node;
}
var nodes = this.findNodesHavingAttribute(node, attributeName);
if (nodes && nodes.length) {
return nodes[0];
}
},
findFirstNodeHavingAttributeWithValue: function (node, attributeName)
{
if (!node || !attributeName) {
return;
}
if (this.hasNodeAttributeWithValue(node, attributeName)) {
return node;
}
var nodes = this.findNodesHavingAttribute(node, attributeName);
if (!nodes || !nodes.length) {
return;
}
var index;
for (index = 0; index < nodes.length; index++) {
if (this.getAttributeValueFromNode(nodes[index], attributeName)) {
return nodes[index];
}
}
},
findNodesHavingCssClass: function (nodeToSearch, className, nodes)
{
if (!nodes) {
nodes = [];
}
if (!nodeToSearch || !className) {
return nodes;
}
if (nodeToSearch.getElementsByClassName) {
var foundNodes = nodeToSearch.getElementsByClassName(className);
return this.htmlCollectionToArray(foundNodes);
}
var children = getChildrenFromNode(nodeToSearch);
if (!children || !children.length) {
return [];
}
var index, child;
for (index = 0; index < children.length; index++) {
child = children[index];
if (this.hasNodeCssClass(child, className)) {
nodes.push(child);
}
nodes = this.findNodesHavingCssClass(child, className, nodes);
}
return nodes;
},
findFirstNodeHavingClass: function (node, className)
{
if (!node || !className) {
return;
}
if (this.hasNodeCssClass(node, className)) {
return node;
}
var nodes = this.findNodesHavingCssClass(node, className);
if (nodes && nodes.length) {
return nodes[0];
}
},
isLinkElement: function (node)
{
if (!node) {
return false;
}
var elementName = String(node.nodeName).toLowerCase();
var linkElementNames = ['a', 'area'];
var pos = indexOfArray(linkElementNames, elementName);
return pos !== -1;
},
setAnyAttribute: function (node, attrName, attrValue)
{
if (!node || !attrName) {
return;
}
if (node.setAttribute) {
node.setAttribute(attrName, attrValue);
} else {
node[attrName] = attrValue;
}
}
};
/************************************************************
* Content Tracking
************************************************************/
var content = {
CONTENT_ATTR: 'data-track-content',
CONTENT_CLASS: 'piwikTrackContent',
CONTENT_NAME_ATTR: 'data-content-name',
CONTENT_PIECE_ATTR: 'data-content-piece',
CONTENT_PIECE_CLASS: 'piwikContentPiece',
CONTENT_TARGET_ATTR: 'data-content-target',
CONTENT_TARGET_CLASS: 'piwikContentTarget',
CONTENT_IGNOREINTERACTION_ATTR: 'data-content-ignoreinteraction',
CONTENT_IGNOREINTERACTION_CLASS: 'piwikContentIgnoreInteraction',
location: undefined,
findContentNodes: function ()
{
var cssSelector = '.' + this.CONTENT_CLASS;
var attrSelector = '[' + this.CONTENT_ATTR + ']';
var contentNodes = query.findMultiple([cssSelector, attrSelector]);
return contentNodes;
},
findContentNodesWithinNode: function (node)
{
if (!node) {
return [];
}
// NOTE: we do not use query.findMultiple here as querySelectorAll would most likely not deliver the result we want
var nodes1 = query.findNodesHavingCssClass(node, this.CONTENT_CLASS);
var nodes2 = query.findNodesHavingAttribute(node, this.CONTENT_ATTR);
if (nodes2 && nodes2.length) {
var index;
for (index = 0; index < nodes2.length; index++) {
nodes1.push(nodes2[index]);
}
}
if (query.hasNodeAttribute(node, this.CONTENT_ATTR)) {
nodes1.push(node);
} else if (query.hasNodeCssClass(node, this.CONTENT_CLASS)) {
nodes1.push(node);
}
nodes1 = query.makeNodesUnique(nodes1);
return nodes1;
},
findParentContentNode: function (anyNode)
{
if (!anyNode) {
return;
}
var node = anyNode;
var counter = 0;
while (node && node !== documentAlias && node.parentNode) {
if (query.hasNodeAttribute(node, this.CONTENT_ATTR)) {
return node;
}
if (query.hasNodeCssClass(node, this.CONTENT_CLASS)) {
return node;
}
node = node.parentNode;
if (counter > 1000) {
break; // prevent loop, should not happen anyway but better we do this
}
counter++;
}
},
findPieceNode: function (node)
{
var contentPiece;
contentPiece = query.findFirstNodeHavingAttribute(node, this.CONTENT_PIECE_ATTR);
if (!contentPiece) {
contentPiece = query.findFirstNodeHavingClass(node, this.CONTENT_PIECE_CLASS);
}
if (contentPiece) {
return contentPiece;
}
return node;
},
findTargetNodeNoDefault: function (node)
{
if (!node) {
return;
}
var target = query.findFirstNodeHavingAttributeWithValue(node, this.CONTENT_TARGET_ATTR);
if (target) {
return target;
}
target = query.findFirstNodeHavingAttribute(node, this.CONTENT_TARGET_ATTR);
if (target) {
return target;
}
target = query.findFirstNodeHavingClass(node, this.CONTENT_TARGET_CLASS);
if (target) {
return target;
}
},
findTargetNode: function (node)
{
var target = this.findTargetNodeNoDefault(node);
if (target) {
return target;
}
return node;
},
findContentName: function (node)
{
if (!node) {
return;
}
var nameNode = query.findFirstNodeHavingAttributeWithValue(node, this.CONTENT_NAME_ATTR);
if (nameNode) {
return query.getAttributeValueFromNode(nameNode, this.CONTENT_NAME_ATTR);
}
var contentPiece = this.findContentPiece(node);
if (contentPiece) {
return this.removeDomainIfIsInLink(contentPiece);
}
if (query.hasNodeAttributeWithValue(node, 'title')) {
return query.getAttributeValueFromNode(node, 'title');
}
var clickUrlNode = this.findPieceNode(node);
if (query.hasNodeAttributeWithValue(clickUrlNode, 'title')) {
return query.getAttributeValueFromNode(clickUrlNode, 'title');
}
var targetNode = this.findTargetNode(node);
if (query.hasNodeAttributeWithValue(targetNode, 'title')) {
return query.getAttributeValueFromNode(targetNode, 'title');
}
},
findContentPiece: function (node)
{
if (!node) {
return;
}
var nameNode = query.findFirstNodeHavingAttributeWithValue(node, this.CONTENT_PIECE_ATTR);
if (nameNode) {
return query.getAttributeValueFromNode(nameNode, this.CONTENT_PIECE_ATTR);
}
var contentNode = this.findPieceNode(node);
var media = this.findMediaUrlInNode(contentNode);
if (media) {
return this.toAbsoluteUrl(media);
}
},
findContentTarget: function (node)
{
if (!node) {
return;
}
var targetNode = this.findTargetNode(node);
if (query.hasNodeAttributeWithValue(targetNode, this.CONTENT_TARGET_ATTR)) {
return query.getAttributeValueFromNode(targetNode, this.CONTENT_TARGET_ATTR);
}
var href;
if (query.hasNodeAttributeWithValue(targetNode, 'href')) {
href = query.getAttributeValueFromNode(targetNode, 'href');
return this.toAbsoluteUrl(href);
}
var contentNode = this.findPieceNode(node);
if (query.hasNodeAttributeWithValue(contentNode, 'href')) {
href = query.getAttributeValueFromNode(contentNode, 'href');
return this.toAbsoluteUrl(href);
}
},
isSameDomain: function (url)
{
if (!url || !url.indexOf) {
return false;
}
if (0 === url.indexOf(this.getLocation().origin)) {
return true;
}
var posHost = url.indexOf(this.getLocation().host);
if (8 >= posHost && 0 <= posHost) {
return true;
}
return false;
},
removeDomainIfIsInLink: function (text)
{
// we will only remove if domain === location.origin meaning is not an outlink
var regexContainsProtocol = '^https?:\/\/[^\/]+';
var regexReplaceDomain = '^.*\/\/[^\/]+';
if (text &&
text.search &&
-1 !== text.search(new RegExp(regexContainsProtocol))
&& this.isSameDomain(text)) {
text = text.replace(new RegExp(regexReplaceDomain), '');
if (!text) {
text = '/';
}
}
return text;
},
findMediaUrlInNode: function (node)
{
if (!node) {
return;
}
var mediaElements = ['img', 'embed', 'video', 'audio'];
var elementName = node.nodeName.toLowerCase();
if (-1 !== indexOfArray(mediaElements, elementName) &&
query.findFirstNodeHavingAttributeWithValue(node, 'src')) {
var sourceNode = query.findFirstNodeHavingAttributeWithValue(node, 'src');
return query.getAttributeValueFromNode(sourceNode, 'src');
}
if (elementName === 'object' &&
query.hasNodeAttributeWithValue(node, 'data')) {
return query.getAttributeValueFromNode(node, 'data');
}
if (elementName === 'object') {
var params = query.findNodesByTagName(node, 'param');
if (params && params.length) {
var index;
for (index = 0; index < params.length; index++) {
if ('movie' === query.getAttributeValueFromNode(params[index], 'name') &&
query.hasNodeAttributeWithValue(params[index], 'value')) {
return query.getAttributeValueFromNode(params[index], 'value');
}
}
}
var embed = query.findNodesByTagName(node, 'embed');
if (embed && embed.length) {
return this.findMediaUrlInNode(embed[0]);
}
}
},
trim: function (text)
{
if (text && String(text) === text) {
return text.replace(/^\s+|\s+$/g, '');
}
return text;
},
isOrWasNodeInViewport: function (node)
{
if (!node || !node.getBoundingClientRect || node.nodeType !== 1) {
return true;
}
var rect = node.getBoundingClientRect();
var html = documentAlias.documentElement || {};
var wasVisible = rect.top < 0;
if (wasVisible && node.offsetTop) {
wasVisible = (node.offsetTop + rect.height) > 0;
}
var docWidth = html.clientWidth; // The clientWidth attribute returns the viewport width excluding the size of a rendered scroll bar
if (windowAlias.innerWidth && docWidth > windowAlias.innerWidth) {
docWidth = windowAlias.innerWidth; // The innerWidth attribute must return the viewport width including the size of a rendered scroll bar
}
var docHeight = html.clientHeight; // The clientWidth attribute returns the viewport width excluding the size of a rendered scroll bar
if (windowAlias.innerHeight && docHeight > windowAlias.innerHeight) {
docHeight = windowAlias.innerHeight; // The innerWidth attribute must return the viewport width including the size of a rendered scroll bar
}
return (
(rect.bottom > 0 || wasVisible) &&
rect.right > 0 &&
rect.left < docWidth &&
((rect.top < docHeight) || wasVisible) // rect.top < 0 we assume user has seen all the ones that are above the current viewport
);
},
isNodeVisible: function (node)
{
var isItVisible = isVisible(node);
var isInViewport = this.isOrWasNodeInViewport(node);
return isItVisible && isInViewport;
},
buildInteractionRequestParams: function (interaction, name, piece, target)
{
var params = '';
if (interaction) {
params += 'c_i='+ encodeWrapper(interaction);
}
if (name) {
if (params) {
params += '&';
}
params += 'c_n='+ encodeWrapper(name);
}
if (piece) {
if (params) {
params += '&';
}
params += 'c_p='+ encodeWrapper(piece);
}
if (target) {
if (params) {
params += '&';
}
params += 'c_t='+ encodeWrapper(target);
}
return params;
},
buildImpressionRequestParams: function (name, piece, target)
{
var params = 'c_n=' + encodeWrapper(name) +
'&c_p=' + encodeWrapper(piece);
if (target) {
params += '&c_t=' + encodeWrapper(target);
}
return params;
},
buildContentBlock: function (node)
{
if (!node) {
return;
}
var name = this.findContentName(node);
var piece = this.findContentPiece(node);
var target = this.findContentTarget(node);
name = this.trim(name);
piece = this.trim(piece);
target = this.trim(target);
return {
name: name || 'Unknown',
piece: piece || 'Unknown',
target: target || ''
};
},
collectContent: function (contentNodes)
{
if (!contentNodes || !contentNodes.length) {
return [];
}
var contents = [];
var index, contentBlock;
for (index = 0; index < contentNodes.length; index++) {
contentBlock = this.buildContentBlock(contentNodes[index]);
if (isDefined(contentBlock)) {
contents.push(contentBlock);
}
}
return contents;
},
setLocation: function (location)
{
this.location = location;
},
getLocation: function ()
{
var locationAlias = this.location || windowAlias.location;
if (!locationAlias.origin) {
locationAlias.origin = locationAlias.protocol + "//" + locationAlias.hostname + (locationAlias.port ? ':' + locationAlias.port: '');
}
return locationAlias;
},
toAbsoluteUrl: function (url)
{
if ((!url || String(url) !== url) && url !== '') {
// we only handle strings
return url;
}
if ('' === url) {
return this.getLocation().href;
}
// Eg //example.com/test.jpg
if (url.search(/^\/\//) !== -1) {
return this.getLocation().protocol + url;
}
// Eg http://example.com/test.jpg
if (url.search(/:\/\//) !== -1) {
return url;
}
// Eg #test.jpg
if (0 === url.indexOf('#')) {
return this.getLocation().origin + this.getLocation().pathname + url;
}
// Eg ?x=5
if (0 === url.indexOf('?')) {
return this.getLocation().origin + this.getLocation().pathname + url;
}
// Eg mailto:x@y.z tel:012345, ... market:... sms:..., javasript:... ecmascript: ... and many more
if (0 === url.search('^[a-zA-Z]{2,11}:')) {
return url;
}
// Eg /test.jpg
if (url.search(/^\//) !== -1) {
return this.getLocation().origin + url;
}
// Eg test.jpg
var regexMatchDir = '(.*\/)';
var base = this.getLocation().origin + this.getLocation().pathname.match(new RegExp(regexMatchDir))[0];
return base + url;
},
isUrlToCurrentDomain: function (url) {
var absoluteUrl = this.toAbsoluteUrl(url);
if (!absoluteUrl) {
return false;
}
var origin = this.getLocation().origin;
if (origin === absoluteUrl) {
return true;
}
if (0 === String(absoluteUrl).indexOf(origin)) {
if (':' === String(absoluteUrl).substr(origin.length, 1)) {
return false; // url has port whereas origin has not => different URL
}
return true;
}
return false;
},
setHrefAttribute: function (node, url)
{
if (!node || !url) {
return;
}
query.setAnyAttribute(node, 'href', url);
},
shouldIgnoreInteraction: function (targetNode)
{
var hasAttr = query.hasNodeAttribute(targetNode, this.CONTENT_IGNOREINTERACTION_ATTR);
var hasClass = query.hasNodeCssClass(targetNode, this.CONTENT_IGNOREINTERACTION_CLASS);
return hasAttr || hasClass;
}
};
/************************************************************
* 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, uuid) {
/************************************************************
* Private members
************************************************************/
var
/*