From 77a6412ff7a8bdb2f23fb00a2d8ce22e7a35fcee Mon Sep 17 00:00:00 2001 From: Thomas Steur <tsteur@users.noreply.github.com> Date: Sun, 28 Aug 2016 18:33:55 +1200 Subject: [PATCH] Split piwik.js into multiple files and provide a merged one (#10441) refs #6106 --- .gitignore | 1 + CHANGELOG.md | 5 + config/global.ini.php | 1 + core/DataTable/Map.php | 10 + core/Filechecks.php | 2 +- core/Plugin/Report.php | 9 + core/Updates/2.16.3-b2.php | 28 + core/Version.php | 2 +- js/README.md | 6 +- js/piwik.js | 2543 +++++++++-------- js/piwik.min.js | 71 + piwik.js | 95 +- .../Actions/javascripts/actionsDataTable.js | 2 + plugins/CoreHome/javascripts/dataTable.js | 9 + .../Visualizations/HtmlTable/AllColumns.php | 3 +- .../Visualizations/JqplotGraph/Bar.php | 6 +- plugins/CustomPiwikJs/.gitignore | 1 + .../CustomPiwikJs/Commands/UpdateTracker.php | 62 + plugins/CustomPiwikJs/CustomPiwikJs.php | 35 + .../CustomPiwikJs/Diagnostic/PiwikJsCheck.php | 54 + .../Exception/AccessDeniedException.php | 15 + plugins/CustomPiwikJs/File.php | 75 + plugins/CustomPiwikJs/Tasks.php | 23 + plugins/CustomPiwikJs/TrackerUpdater.php | 78 + .../TrackingCode/JsTestPluginTrackerFiles.php | 27 + .../TrackingCode/PiwikJsManipulator.php | 57 + .../TrackingCode/PluginTrackerFiles.php | 87 + plugins/CustomPiwikJs/config/config.php | 7 + plugins/CustomPiwikJs/lang/en.json | 7 + .../Framework/Mock/PluginTrackerFilesMock.php | 36 + .../tests/Integration/FileTest.php | 125 + .../Integration/PiwikJsManipulatorTest.php | 73 + .../Integration/PluginTrackerFilesTest.php | 124 + .../tests/Integration/TrackerUpdaterTest.php | 121 + .../tests/System/PiwikJsContentTest.php | 39 + .../tests/resources/MyTestTarget2.js | 16 + plugins/CustomPiwikJs/tests/resources/test.js | 2 + .../tests/resources/testpiwik.js | 6 + .../CustomPiwikJs/tests/resources/tracker.js | 4 + .../tests/resources/tracker.min.js | 2 + plugins/Live/javascripts/live.js | 9 +- .../Integration/ReleaseCheckListTest.php | 8 +- tests/UI/expected-ui-screenshots | 2 +- tests/angularjs/Gruntfile.js | 23 +- tests/angularjs/karma.conf.js | 4 +- tests/angularjs/package.json | 5 +- tests/javascript/index.php | 31 +- 47 files changed, 2686 insertions(+), 1265 deletions(-) create mode 100644 core/Updates/2.16.3-b2.php create mode 100644 js/piwik.min.js create mode 100644 plugins/CustomPiwikJs/.gitignore create mode 100644 plugins/CustomPiwikJs/Commands/UpdateTracker.php create mode 100644 plugins/CustomPiwikJs/CustomPiwikJs.php create mode 100644 plugins/CustomPiwikJs/Diagnostic/PiwikJsCheck.php create mode 100644 plugins/CustomPiwikJs/Exception/AccessDeniedException.php create mode 100644 plugins/CustomPiwikJs/File.php create mode 100644 plugins/CustomPiwikJs/Tasks.php create mode 100644 plugins/CustomPiwikJs/TrackerUpdater.php create mode 100644 plugins/CustomPiwikJs/TrackingCode/JsTestPluginTrackerFiles.php create mode 100644 plugins/CustomPiwikJs/TrackingCode/PiwikJsManipulator.php create mode 100644 plugins/CustomPiwikJs/TrackingCode/PluginTrackerFiles.php create mode 100644 plugins/CustomPiwikJs/config/config.php create mode 100644 plugins/CustomPiwikJs/lang/en.json create mode 100644 plugins/CustomPiwikJs/tests/Framework/Mock/PluginTrackerFilesMock.php create mode 100644 plugins/CustomPiwikJs/tests/Integration/FileTest.php create mode 100644 plugins/CustomPiwikJs/tests/Integration/PiwikJsManipulatorTest.php create mode 100644 plugins/CustomPiwikJs/tests/Integration/PluginTrackerFilesTest.php create mode 100644 plugins/CustomPiwikJs/tests/Integration/TrackerUpdaterTest.php create mode 100644 plugins/CustomPiwikJs/tests/System/PiwikJsContentTest.php create mode 100644 plugins/CustomPiwikJs/tests/resources/MyTestTarget2.js create mode 100644 plugins/CustomPiwikJs/tests/resources/test.js create mode 100644 plugins/CustomPiwikJs/tests/resources/testpiwik.js create mode 100644 plugins/CustomPiwikJs/tests/resources/tracker.js create mode 100644 plugins/CustomPiwikJs/tests/resources/tracker.min.js diff --git a/.gitignore b/.gitignore index a1cd27d0cd..8a53c53e11 100644 --- a/.gitignore +++ b/.gitignore @@ -61,6 +61,7 @@ php_errors.log /tests/PHPUnit/proxy/piwik.js /tests/PHPUnit/proxy/plugins /tests/PHPUnit/proxy/tests +/tests/resources/piwik.test.js /config/*.config.ini.php /Vagrantfile /misc/vagrant diff --git a/CHANGELOG.md b/CHANGELOG.md index adb48086fb..c8eec8149f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ This is the Developer Changelog for Piwik platform developers. All changes in ou The Product Changelog at **[piwik.org/changelog](http://piwik.org/changelog)** lets you see more details about any Piwik release, such as the list of new guides and FAQs, security fixes, and links to all closed issues. +## Piwik 2.16.3 + +### New APIs +* The Piwik JavaScript tracker has a new method `trackRequest` that allows you to send any tracking parameters to Piwik. For example `_paq.push(['trackRequest', 'te=foo&bar=baz'])` + ## Piwik 2.16.2 ### New APIs diff --git a/config/global.ini.php b/config/global.ini.php index 5f904efe27..edf9b867a2 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -807,6 +807,7 @@ Plugins[] = Heartbeat Plugins[] = Intl Plugins[] = ProfessionalServices Plugins[] = UserId +Plugins[] = CustomPiwikJs [PluginsInstalled] PluginsInstalled[] = Diagnostics diff --git a/core/DataTable/Map.php b/core/DataTable/Map.php index e7bc510194..e767d2f49a 100644 --- a/core/DataTable/Map.php +++ b/core/DataTable/Map.php @@ -227,6 +227,16 @@ class Map implements DataTableInterface } } + /** + * See {@link DataTable::disableFilter()}. + */ + public function disableFilter($className) + { + foreach ($this->getDataTables() as $table) { + $table->disableFilter($className); + } + } + /** * @ignore */ diff --git a/core/Filechecks.php b/core/Filechecks.php index 417b13bbca..274453e96d 100644 --- a/core/Filechecks.php +++ b/core/Filechecks.php @@ -205,7 +205,7 @@ class Filechecks return $message; } - private static function getUserAndGroup() + public static function getUserAndGroup() { $user = self::getUser(); if (!function_exists('shell_exec')) { diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php index 053760d2d2..06165f6c32 100644 --- a/core/Plugin/Report.php +++ b/core/Plugin/Report.php @@ -544,6 +544,15 @@ class Report } } + /** + * Get report documentation. + * @return string + */ + public function getDocumentation() + { + return $this->documentation; + } + /** * Builts the report metadata for this report. Can be useful in case you want to change the behavior of * {@link configureReportMetadata()}. diff --git a/core/Updates/2.16.3-b2.php b/core/Updates/2.16.3-b2.php new file mode 100644 index 0000000000..3a1e75722d --- /dev/null +++ b/core/Updates/2.16.3-b2.php @@ -0,0 +1,28 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ + +namespace Piwik\Updates; + +use Piwik\Updater; +use Piwik\Updates as PiwikUpdates; + +/** + * Update for version 2.16.3b2 + */ +class Updates_2_16_3_b2 extends PiwikUpdates +{ + + public function doUpdate(Updater $updater) + { + try { + \Piwik\Plugin\Manager::getInstance()->activatePlugin('CustomPiwikJs'); + } catch (\Exception $e) { + } + } +} diff --git a/core/Version.php b/core/Version.php index d2214508d3..f5eb167b2a 100644 --- a/core/Version.php +++ b/core/Version.php @@ -20,7 +20,7 @@ final class Version * The current Piwik version. * @var string */ - const VERSION = '2.16.3-b1'; + const VERSION = '2.16.3-b2'; public function isStableVersion($version) { diff --git a/js/README.md b/js/README.md index 9556dc402e..b3700827eb 100644 --- a/js/README.md +++ b/js/README.md @@ -46,11 +46,11 @@ The js/ folder contains: ```bash $ cd /path/to/piwik/js/ - $ sed '/<DEBUG>/,/<\/DEBUG>/d' < piwik.js | sed 's/eval/replacedEvilString/' | java -jar yuicompressor-2.4.7/build/yuicompressor-2.4.7.jar --type js --line-break 1000 | sed 's/replacedEvilString/eval/' | sed 's/^[/][*]/\/*!/' > piwik-min.js && cp piwik-min.js ../piwik.js + $ sed '/<DEBUG>/,/<\/DEBUG>/d' < piwik.js | sed 's/eval/replacedEvilString/' | java -jar yuicompressor-2.4.7/build/yuicompressor-2.4.7.jar --type js --line-break 1000 | sed 's/replacedEvilString/eval/' | sed 's/^[/][*]/\/*!/' > piwik.min.js && cp piwik.min.js ../piwik.js ``` - This will generate the minify /path/to/piwik/js/piwik-min.js and copy it to - /path/to/piwik/piwik.js + This will generate the minify /path/to/piwik/js/piwik.min.js and copy it to + /path/to/piwik/piwik.js. Both "js/piwik.min.js" and "piwik.js" need to be committed. * In a production environment, the tests/javascript folder is not used and can be removed (if present). diff --git a/js/piwik.js b/js/piwik.js index 5d847d5ba8..9a135e5edc 100644 --- a/js/piwik.js +++ b/js/piwik.js @@ -957,7 +957,7 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON. /*global unescape */ /*global ActiveXObject */ /*members Piwik, encodeURIComponent, decodeURIComponent, getElementsByTagName, - shift, unshift, piwikAsyncInit, frameElement, self, hasFocus, + shift, unshift, piwikAsyncInit, piwikPluginAsyncInit, frameElement, self, hasFocus, createElement, appendChild, characterSet, charset, all, addEventListener, attachEvent, removeEventListener, detachEvent, disableCookies, cookie, domain, readyState, documentElement, doScroll, title, text, @@ -974,7 +974,7 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON. exec, res, width, height, pdf, qt, realp, wma, dir, fla, java, gears, ag, - hook, getHook, getVisitorId, getVisitorInfo, setUserId, getUserId, setSiteId, getSiteId, setTrackerUrl, getTrackerUrl, appendToTrackingUrl, getRequest, addPlugin, + initialized, hook, getHook, getVisitorId, getVisitorInfo, setUserId, getUserId, setSiteId, getSiteId, setTrackerUrl, getTrackerUrl, appendToTrackingUrl, getRequest, addPlugin, getAttributionInfo, getAttributionCampaignName, getAttributionCampaignKeyword, getAttributionReferrerTimestamp, getAttributionReferrerUrl, setCustomData, getCustomData, @@ -993,7 +993,7 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON. doNotTrack, setDoNotTrack, msDoNotTrack, getValuesFromVisitorIdCookie, addListener, enableLinkTracking, enableJSErrorTracking, setLinkTrackingTimer, enableHeartBeatTimer, disableHeartBeatTimer, killFrame, redirectFile, setCountPreRendered, - trackGoal, trackLink, trackPageView, trackSiteSearch, trackEvent, + trackGoal, trackLink, trackPageView, trackRequest, trackSiteSearch, trackEvent, setEcommerceView, addEcommerceItem, trackEcommerceOrder, trackEcommerceCartUpdate, deleteCookie, deleteCookies, offsetTop, offsetLeft, offsetHeight, offsetWidth, nodeType, defaultView, innerHTML, scrollLeft, scrollTop, currentStyle, getComputedStyle, querySelectorAll, splice, @@ -1030,7 +1030,7 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON. /*global _paq:true */ /*members push */ /*global Piwik:true */ -/*members addPlugin, getTracker, getAsyncTracker */ +/*members addPlugin, getTracker, getAsyncTracker, getAsyncTrackers, addTracker, trigger, on, off */ /*global Piwik_Overlay_Client */ /*global AnalyticsTracker:true */ /*members initialize */ @@ -1059,6 +1059,8 @@ if (typeof window.Piwik !== 'object') { /* plugins */ plugins = {}, + eventHandlers = {}, + /* alias frequently used globals for added minification */ documentAlias = document, navigatorAlias = navigator, @@ -1184,13 +1186,38 @@ if (typeof window.Piwik !== 'object') { for (j = 0; j < asyncTrackers.length; j++) { if (isString(f)) { + var context = asyncTrackers[j]; + var fParts; + + var isStaticPluginCall = f.indexOf('::') > 0; + if (isStaticPluginCall) { + fParts = f.split('::'); + context = fParts[0]; + f = fParts[1]; + + if ('object' === typeof Piwik[context] && 'function' === typeof Piwik[context][f]) { + Piwik[context][f].apply(Piwik[context], parameterArray); + } + + return; + } + + var isPluginTrackerCall = f.indexOf('.') > 0; + + if (isPluginTrackerCall) { + fParts = f.split('.'); + context = context[fParts[0]]; + f = fParts[1]; + } - if(asyncTrackers[j][f]) { - asyncTrackers[j][f].apply(asyncTrackers[j], parameterArray); + if (context[f]) { + context[f].apply(context, parameterArray); } else { var message = 'The method \'' + f + '\' was not found in "_paq" variable. Please have a look at the Piwik tracker documentation: http://developer.piwik.org/api-reference/tracking-javascript'; logConsoleError(message); - throw new TypeError(message); + if (!isPluginTrackerCall) { + throw new TypeError(message); + } } if (f === 'addTracker') { @@ -5130,1330 +5157,1339 @@ if (typeof window.Piwik !== 'object') { * Public data and methods ************************************************************/ - return { + /*<DEBUG>*/ - /* - * Test hook accessors - */ - hook: registeredHooks, - getHook: function (hookName) { - return registeredHooks[hookName]; - }, - getQuery: function () { - return query; - }, - getContent: function () { - return content; - }, + /* + * Test hook accessors + */ + this.hook = registeredHooks; + this.getHook = function (hookName) { + return registeredHooks[hookName]; + }; + this.getQuery = function () { + return query; + }; + this.getContent = function () { + return content; + }; - buildContentImpressionRequest: buildContentImpressionRequest, - buildContentInteractionRequest: buildContentInteractionRequest, - buildContentInteractionRequestNode: buildContentInteractionRequestNode, - buildContentInteractionTrackingRedirectUrl: buildContentInteractionTrackingRedirectUrl, - getContentImpressionsRequestsFromNodes: getContentImpressionsRequestsFromNodes, - getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet: getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet, - trackCallbackOnLoad: trackCallbackOnLoad, - trackCallbackOnReady: trackCallbackOnReady, - buildContentImpressionsRequests: buildContentImpressionsRequests, - wasContentImpressionAlreadyTracked: wasContentImpressionAlreadyTracked, - appendContentInteractionToRequestIfPossible: getContentInteractionToRequestIfPossible, - setupInteractionsTracking: setupInteractionsTracking, - trackContentImpressionClickInteraction: trackContentImpressionClickInteraction, - internalIsNodeVisible: isVisible, - isNodeAuthorizedToTriggerInteraction: isNodeAuthorizedToTriggerInteraction, - replaceHrefIfInternalLink: replaceHrefIfInternalLink, - getDomains: function () { - return configHostsAlias; - }, - getConfigCookiePath: function () { - return configCookiePath; - }, - getConfigDownloadExtensions: function () { - return configDownloadExtensions; - }, - enableTrackOnlyVisibleContent: function (checkOnScroll, timeIntervalInMs) { - return enableTrackOnlyVisibleContent(checkOnScroll, timeIntervalInMs, this); - }, - clearTrackedContentImpressions: function () { - trackedContentImpressions = []; - }, - getTrackedContentImpressions: function () { - return trackedContentImpressions; - }, - clearEnableTrackOnlyVisibleContent: function () { - isTrackOnlyVisibleContentEnabled = false; - }, - disableLinkTracking: function () { - linkTrackingInstalled = false; - linkTrackingEnabled = false; - }, - getConfigVisitorCookieTimeout: function () { - return configVisitorCookieTimeout; - }, - removeAllAsyncTrackersButFirst: function () { - var firstTracker = asyncTrackers[0]; - asyncTrackers = [firstTracker]; - }, - getRemainingVisitorCookieTimeout: getRemainingVisitorCookieTimeout, + this.buildContentImpressionRequest = buildContentImpressionRequest; + this.buildContentInteractionRequest = buildContentInteractionRequest; + this.buildContentInteractionRequestNode = buildContentInteractionRequestNode; + this.buildContentInteractionTrackingRedirectUrl = buildContentInteractionTrackingRedirectUrl; + this.getContentImpressionsRequestsFromNodes = getContentImpressionsRequestsFromNodes; + this.getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet; + this.trackCallbackOnLoad = trackCallbackOnLoad; + this.trackCallbackOnReady = trackCallbackOnReady; + this.buildContentImpressionsRequests = buildContentImpressionsRequests; + this.wasContentImpressionAlreadyTracked = wasContentImpressionAlreadyTracked; + this.appendContentInteractionToRequestIfPossible = getContentInteractionToRequestIfPossible; + this.setupInteractionsTracking = setupInteractionsTracking; + this.trackContentImpressionClickInteraction = trackContentImpressionClickInteraction; + this.internalIsNodeVisible = isVisible; + this.isNodeAuthorizedToTriggerInteraction = isNodeAuthorizedToTriggerInteraction; + this.replaceHrefIfInternalLink = replaceHrefIfInternalLink; + this.getDomains = function () { + return configHostsAlias; + }; + this.getConfigCookiePath = function () { + return configCookiePath; + }; + this.getConfigDownloadExtensions = function () { + return configDownloadExtensions; + }; + this.enableTrackOnlyVisibleContent = function (checkOnScroll, timeIntervalInMs) { + return enableTrackOnlyVisibleContent(checkOnScroll, timeIntervalInMs, this); + }; + this.clearTrackedContentImpressions = function () { + trackedContentImpressions = []; + }; + this.getTrackedContentImpressions = function () { + return trackedContentImpressions; + }; + this.clearEnableTrackOnlyVisibleContent = function () { + isTrackOnlyVisibleContentEnabled = false; + }; + this.disableLinkTracking = function () { + linkTrackingInstalled = false; + linkTrackingEnabled = false; + }; + this.getConfigVisitorCookieTimeout = function () { + return configVisitorCookieTimeout; + }; + this.removeAllAsyncTrackersButFirst = function () { + var firstTracker = asyncTrackers[0]; + asyncTrackers = [firstTracker]; + }; + this.getRemainingVisitorCookieTimeout = getRemainingVisitorCookieTimeout; /*</DEBUG>*/ - /** - * Get visitor ID (from first party cookie) - * - * @return string Visitor ID in hexits (or empty string, if not yet known) - */ - getVisitorId: function () { - return getValuesFromVisitorIdCookie().uuid; - }, - - /** - * Get the visitor information (from first party cookie) - * - * @return array - */ - getVisitorInfo: function () { - // Note: in a new method, we could return also return getValuesFromVisitorIdCookie() - // which returns named parameters rather than returning integer indexed array - return loadVisitorIdCookie(); - }, + /** + * Get visitor ID (from first party cookie) + * + * @return string Visitor ID in hexits (or empty string, if not yet known) + */ + this.getVisitorId = function () { + return getValuesFromVisitorIdCookie().uuid; + }; - /** - * Get the Attribution information, which is an array that contains - * the Referrer used to reach the site as well as the campaign name and keyword - * It is useful only when used in conjunction with Tracker API function setAttributionInfo() - * To access specific data point, you should use the other functions getAttributionReferrer* and getAttributionCampaign* - * - * @return array Attribution array, Example use: - * 1) Call JSON2.stringify(piwikTracker.getAttributionInfo()) - * 2) Pass this json encoded string to the Tracking API (php or java client): setAttributionInfo() - */ - getAttributionInfo: function () { - return loadReferrerAttributionCookie(); - }, + /** + * Get the visitor information (from first party cookie) + * + * @return array + */ + this.getVisitorInfo = function () { + // Note: in a new method, we could return also return getValuesFromVisitorIdCookie() + // which returns named parameters rather than returning integer indexed array + return loadVisitorIdCookie(); + }; - /** - * Get the Campaign name that was parsed from the landing page URL when the visitor - * landed on the site originally - * - * @return string - */ - getAttributionCampaignName: function () { - return loadReferrerAttributionCookie()[0]; - }, + /** + * Get the Attribution information, which is an array that contains + * the Referrer used to reach the site as well as the campaign name and keyword + * It is useful only when used in conjunction with Tracker API function setAttributionInfo() + * To access specific data point, you should use the other functions getAttributionReferrer* and getAttributionCampaign* + * + * @return array Attribution array, Example use: + * 1) Call JSON2.stringify(piwikTracker.getAttributionInfo()) + * 2) Pass this json encoded string to the Tracking API (php or java client): setAttributionInfo() + */ + this.getAttributionInfo = function () { + return loadReferrerAttributionCookie(); + }; - /** - * Get the Campaign keyword that was parsed from the landing page URL when the visitor - * landed on the site originally - * - * @return string - */ - getAttributionCampaignKeyword: function () { - return loadReferrerAttributionCookie()[1]; - }, + /** + * Get the Campaign name that was parsed from the landing page URL when the visitor + * landed on the site originally + * + * @return string + */ + this.getAttributionCampaignName = function () { + return loadReferrerAttributionCookie()[0]; + }; - /** - * Get the time at which the referrer (used for Goal Attribution) was detected - * - * @return int Timestamp or 0 if no referrer currently set - */ - getAttributionReferrerTimestamp: function () { - return loadReferrerAttributionCookie()[2]; - }, + /** + * Get the Campaign keyword that was parsed from the landing page URL when the visitor + * landed on the site originally + * + * @return string + */ + this.getAttributionCampaignKeyword = function () { + return loadReferrerAttributionCookie()[1]; + }; - /** - * Get the full referrer URL that will be used for Goal Attribution - * - * @return string Raw URL, or empty string '' if no referrer currently set - */ - getAttributionReferrerUrl: function () { - return loadReferrerAttributionCookie()[3]; - }, + /** + * Get the time at which the referrer (used for Goal Attribution) was detected + * + * @return int Timestamp or 0 if no referrer currently set + */ + this.getAttributionReferrerTimestamp = function () { + return loadReferrerAttributionCookie()[2]; + }; - /** - * Specify the Piwik server URL - * - * @param string trackerUrl - */ - setTrackerUrl: function (trackerUrl) { - configTrackerUrl = trackerUrl; - }, + /** + * Get the full referrer URL that will be used for Goal Attribution + * + * @return string Raw URL, or empty string '' if no referrer currently set + */ + this.getAttributionReferrerUrl = function () { + return loadReferrerAttributionCookie()[3]; + }; + /** + * Specify the Piwik server URL + * + * @param string trackerUrl + */ + this.setTrackerUrl = function (trackerUrl) { + configTrackerUrl = trackerUrl; + }; - /** - * Returns the Piwik server URL - * @returns string - */ - getTrackerUrl: function () { - return configTrackerUrl; - }, + /** + * Returns the Piwik server URL + * @returns string + */ + this.getTrackerUrl = function () { + return configTrackerUrl; + }; - /** - * Adds a new tracker. All sent requests will be also sent to the given siteId and piwikUrl. - * If piwikUrl is not set, current url will be used. - * - * @param null|string piwikUrl If null, will reuse the same tracker URL of the current tracker instance - * @param int|string siteId - * @return Tracker - */ - addTracker: function (piwikUrl, siteId) { - if (!siteId) { - throw new Error('A siteId must be given to add a new tracker'); - } + /** + * Adds a new tracker. All sent requests will be also sent to the given siteId and piwikUrl. + * If piwikUrl is not set, current url will be used. + * + * @param null|string piwikUrl If null, will reuse the same tracker URL of the current tracker instance + * @param int|string siteId + * @return Tracker + */ + this.addTracker = function (piwikUrl, siteId) { + if (!siteId) { + throw new Error('A siteId must be given to add a new tracker'); + } - if (!isDefined(piwikUrl) || null === piwikUrl) { - piwikUrl = this.getTrackerUrl(); - } + if (!isDefined(piwikUrl) || null === piwikUrl) { + piwikUrl = this.getTrackerUrl(); + } - var tracker = new Tracker(piwikUrl, siteId); + var tracker = new Tracker(piwikUrl, siteId); - asyncTrackers.push(tracker); + asyncTrackers.push(tracker); - return tracker; - }, + return tracker; + }; - /** - * Returns the site ID - * - * @returns int - */ - getSiteId: function() { - return configTrackerSiteId; - }, + /** + * Returns the site ID + * + * @returns int + */ + this.getSiteId = function() { + return configTrackerSiteId; + }; - /** - * Specify the site ID - * - * @param int|string siteId - */ - setSiteId: function (siteId) { - setSiteId(siteId); - }, + /** + * Specify the site ID + * + * @param int|string siteId + */ + this.setSiteId = function (siteId) { + setSiteId(siteId); + }; - /** - * Sets a User ID to this user (such as an email address or a username) - * - * @param string User ID - */ - setUserId: function (userId) { - if(!isDefined(userId) || !userId.length) { - return; - } - configUserId = userId; - visitorUUID = hash(configUserId).substr(0, 16); - }, + /** + * Sets a User ID to this user (such as an email address or a username) + * + * @param string User ID + */ + this.setUserId = function (userId) { + if(!isDefined(userId) || !userId.length) { + return; + } + configUserId = userId; + visitorUUID = hash(configUserId).substr(0, 16); + }; - /** - * Gets the User ID if set. - * - * @returns string User ID - */ - getUserId: function() { - return configUserId; - }, + /** + * Gets the User ID if set. + * + * @returns string User ID + */ + this.getUserId = function() { + return configUserId; + }; - /** - * Pass custom data to the server - * - * Examples: - * tracker.setCustomData(object); - * tracker.setCustomData(key, value); - * - * @param mixed key_or_obj - * @param mixed opt_value - */ - setCustomData: function (key_or_obj, opt_value) { - if (isObject(key_or_obj)) { - configCustomData = key_or_obj; - } else { - if (!configCustomData) { - configCustomData = {}; - } - configCustomData[key_or_obj] = opt_value; + /** + * Pass custom data to the server + * + * Examples: + * tracker.setCustomData(object); + * tracker.setCustomData(key, value); + * + * @param mixed key_or_obj + * @param mixed opt_value + */ + this.setCustomData = function (key_or_obj, opt_value) { + if (isObject(key_or_obj)) { + configCustomData = key_or_obj; + } else { + if (!configCustomData) { + configCustomData = {}; } - }, - - /** - * Get custom data - * - * @return mixed - */ - getCustomData: function () { - return configCustomData; - }, - - /** - * Configure function with custom request content processing logic. - * It gets called after request content in form of query parameters string has been prepared and before request content gets sent. - * - * Examples: - * tracker.setCustomRequestProcessing(function(request){ - * var pairs = request.split('&'); - * var result = {}; - * pairs.forEach(function(pair) { - * pair = pair.split('='); - * result[pair[0]] = decodeURIComponent(pair[1] || ''); - * }); - * return JSON.stringify(result); - * }); - * - * @param function customRequestContentProcessingLogic - */ - setCustomRequestProcessing: function (customRequestContentProcessingLogic) { - configCustomRequestContentProcessing = customRequestContentProcessingLogic; - }, - - /** - * Appends the specified query string to the piwik.php?... Tracking API URL - * - * @param string queryString eg. 'lat=140&long=100' - */ - appendToTrackingUrl: function (queryString) { - configAppendToTrackingUrl = queryString; - }, - - /** - * Returns the query string for the current HTTP Tracking API request. - * Piwik would prepend the hostname and path to Piwik: http://example.org/piwik/piwik.php? - * prior to sending the request. - * - * @param request eg. "param=value¶m2=value2" - */ - getRequest: function (request) { - return getRequest(request); - }, + configCustomData[key_or_obj] = opt_value; + } + }; - /** - * Add plugin defined by a name and a callback function. - * The callback function will be called whenever a tracking request is sent. - * This can be used to append data to the tracking request, or execute other custom logic. - * - * @param string pluginName - * @param Object pluginObj - */ - addPlugin: function (pluginName, pluginObj) { - plugins[pluginName] = pluginObj; - }, + /** + * Get custom data + * + * @return mixed + */ + this.getCustomData = function () { + return configCustomData; + }; - /** - * Set Custom Dimensions. Set Custom Dimensions will not be cleared after a tracked pageview and will - * be sent along all following tracking requests. It is possible to remove/clear a value via `deleteCustomDimension`. - * - * @param int index A Custom Dimension index - * @param string value - */ - setCustomDimension: function (customDimensionId, value) { - customDimensionId = parseInt(customDimensionId, 10); - if (customDimensionId > 0) { - if (!isDefined(value)) { - value = ''; - } - if (!isString(value)) { - value = String(value); - } - customDimensions[customDimensionId] = value; - } - }, + /** + * Configure function with custom request content processing logic. + * It gets called after request content in form of query parameters string has been prepared and before request content gets sent. + * + * Examples: + * tracker.setCustomRequestProcessing(function(request){ + * var pairs = request.split('&'); + * var result = {}; + * pairs.forEach(function(pair) { + * pair = pair.split('='); + * result[pair[0]] = decodeURIComponent(pair[1] || ''); + * }); + * return JSON.stringify(result); + * }); + * + * @param function customRequestContentProcessingLogic + */ + this.setCustomRequestProcessing = function (customRequestContentProcessingLogic) { + configCustomRequestContentProcessing = customRequestContentProcessingLogic; + }; - /** - * Get a stored value for a specific Custom Dimension index. - * - * @param int index A Custom Dimension index - */ - getCustomDimension: function (customDimensionId) { - customDimensionId = parseInt(customDimensionId, 10); - if (customDimensionId > 0 && Object.prototype.hasOwnProperty.call(customDimensions, customDimensionId)) { - return customDimensions[customDimensionId]; - } - }, + /** + * Appends the specified query string to the piwik.php?... Tracking API URL + * + * @param string queryString eg. 'lat=140&long=100' + */ + this.appendToTrackingUrl = function (queryString) { + configAppendToTrackingUrl = queryString; + }; - /** - * Delete a custom dimension. - * - * @param int index Custom dimension Id - */ - deleteCustomDimension: function (customDimensionId) { - customDimensionId = parseInt(customDimensionId, 10); - if (customDimensionId > 0) { - delete customDimensions[customDimensionId]; - } - }, + /** + * Returns the query string for the current HTTP Tracking API request. + * Piwik would prepend the hostname and path to Piwik: http://example.org/piwik/piwik.php? + * prior to sending the request. + * + * @param request eg. "param=value¶m2=value2" + */ + this.getRequest = function (request) { + return getRequest(request); + }; - /** - * Set custom variable within this visit - * - * @param int index Custom variable slot ID from 1-5 - * @param string name - * @param string value - * @param string scope Scope of Custom Variable: - * - "visit" will store the name/value in the visit and will persist it in the cookie for the duration of the visit, - * - "page" will store the name/value in the next page view tracked. - * - "event" will store the name/value in the next event tracked. - */ - setCustomVariable: function (index, name, value, scope) { - var toRecord; + /** + * Add plugin defined by a name and a callback function. + * The callback function will be called whenever a tracking request is sent. + * This can be used to append data to the tracking request, or execute other custom logic. + * + * @param string pluginName + * @param Object pluginObj + */ + this.addPlugin = function (pluginName, pluginObj) { + plugins[pluginName] = pluginObj; + }; - if (!isDefined(scope)) { - scope = 'visit'; - } - if (!isDefined(name)) { - return; - } + /** + * Set Custom Dimensions. Set Custom Dimensions will not be cleared after a tracked pageview and will + * be sent along all following tracking requests. It is possible to remove/clear a value via `deleteCustomDimension`. + * + * @param int index A Custom Dimension index + * @param string value + */ + this.setCustomDimension = function (customDimensionId, value) { + customDimensionId = parseInt(customDimensionId, 10); + if (customDimensionId > 0) { if (!isDefined(value)) { - value = ""; + value = ''; } - if (index > 0) { - name = !isString(name) ? String(name) : name; - value = !isString(value) ? String(value) : value; - toRecord = [name.slice(0, customVariableMaximumLength), value.slice(0, customVariableMaximumLength)]; - // numeric scope is there for GA compatibility - if (scope === 'visit' || scope === 2) { - loadCustomVariables(); - customVariables[index] = toRecord; - } else if (scope === 'page' || scope === 3) { - customVariablesPage[index] = toRecord; - } else if (scope === 'event') { /* GA does not have 'event' scope but we do */ - customVariablesEvent[index] = toRecord; - } + if (!isString(value)) { + value = String(value); } - }, + customDimensions[customDimensionId] = value; + } + }; - /** - * Get custom variable - * - * @param int index Custom variable slot ID from 1-5 - * @param string scope Scope of Custom Variable: "visit" or "page" or "event" - */ - getCustomVariable: function (index, scope) { - var cvar; + /** + * Get a stored value for a specific Custom Dimension index. + * + * @param int index A Custom Dimension index + */ + this.getCustomDimension = function (customDimensionId) { + customDimensionId = parseInt(customDimensionId, 10); + if (customDimensionId > 0 && Object.prototype.hasOwnProperty.call(customDimensions, customDimensionId)) { + return customDimensions[customDimensionId]; + } + }; - if (!isDefined(scope)) { - scope = "visit"; - } + /** + * Delete a custom dimension. + * + * @param int index Custom dimension Id + */ + this.deleteCustomDimension = function (customDimensionId) { + customDimensionId = parseInt(customDimensionId, 10); + if (customDimensionId > 0) { + delete customDimensions[customDimensionId]; + } + }; + + /** + * Set custom variable within this visit + * + * @param int index Custom variable slot ID from 1-5 + * @param string name + * @param string value + * @param string scope Scope of Custom Variable: + * - "visit" will store the name/value in the visit and will persist it in the cookie for the duration of the visit, + * - "page" will store the name/value in the next page view tracked. + * - "event" will store the name/value in the next event tracked. + */ + this.setCustomVariable = function (index, name, value, scope) { + var toRecord; - if (scope === "page" || scope === 3) { - cvar = customVariablesPage[index]; - } else if (scope === "event") { - cvar = customVariablesEvent[index]; - } else if (scope === "visit" || scope === 2) { + if (!isDefined(scope)) { + scope = 'visit'; + } + if (!isDefined(name)) { + return; + } + if (!isDefined(value)) { + value = ""; + } + if (index > 0) { + name = !isString(name) ? String(name) : name; + value = !isString(value) ? String(value) : value; + toRecord = [name.slice(0, customVariableMaximumLength), value.slice(0, customVariableMaximumLength)]; + // numeric scope is there for GA compatibility + if (scope === 'visit' || scope === 2) { loadCustomVariables(); - cvar = customVariables[index]; + customVariables[index] = toRecord; + } else if (scope === 'page' || scope === 3) { + customVariablesPage[index] = toRecord; + } else if (scope === 'event') { /* GA does not have 'event' scope but we do */ + customVariablesEvent[index] = toRecord; } + } + }; - if (!isDefined(cvar) - || (cvar && cvar[0] === '')) { - return false; - } + /** + * Get custom variable + * + * @param int index Custom variable slot ID from 1-5 + * @param string scope Scope of Custom Variable: "visit" or "page" or "event" + */ + this.getCustomVariable = function (index, scope) { + var cvar; - return cvar; - }, + if (!isDefined(scope)) { + scope = "visit"; + } - /** - * Delete custom variable - * - * @param int index Custom variable slot ID from 1-5 - * @param string scope - */ - deleteCustomVariable: function (index, scope) { - // Only delete if it was there already - if (this.getCustomVariable(index, scope)) { - this.setCustomVariable(index, '', '', scope); - } - }, + if (scope === "page" || scope === 3) { + cvar = customVariablesPage[index]; + } else if (scope === "event") { + cvar = customVariablesEvent[index]; + } else if (scope === "visit" || scope === 2) { + loadCustomVariables(); + cvar = customVariables[index]; + } - /** - * When called then the Custom Variables of scope "visit" will be stored (persisted) in a first party cookie - * for the duration of the visit. This is useful if you want to call getCustomVariable later in the visit. - * - * By default, Custom Variables of scope "visit" are not stored on the visitor's computer. - */ - storeCustomVariablesInCookie: function () { - configStoreCustomVariablesInCookie = true; - }, + if (!isDefined(cvar) + || (cvar && cvar[0] === '')) { + return false; + } - /** - * Set delay for link tracking (in milliseconds) - * - * @param int delay - */ - setLinkTrackingTimer: function (delay) { - configTrackerPause = delay; - }, + return cvar; + }; - /** - * Set list of file extensions to be recognized as downloads - * - * @param string|array extensions - */ - setDownloadExtensions: function (extensions) { - if(isString(extensions)) { - extensions = extensions.split('|'); - } - configDownloadExtensions = extensions; - }, + /** + * Delete custom variable + * + * @param int index Custom variable slot ID from 1-5 + * @param string scope + */ + this.deleteCustomVariable = function (index, scope) { + // Only delete if it was there already + if (this.getCustomVariable(index, scope)) { + this.setCustomVariable(index, '', '', scope); + } + }; - /** - * Specify additional file extensions to be recognized as downloads - * - * @param string|array extensions for example 'custom' or ['custom1','custom2','custom3'] - */ - addDownloadExtensions: function (extensions) { - var i; - if(isString(extensions)) { - extensions = extensions.split('|'); - } - for (i=0; i < extensions.length; i++) { - configDownloadExtensions.push(extensions[i]); - } - }, + /** + * When called then the Custom Variables of scope "visit" will be stored (persisted) in a first party cookie + * for the duration of the visit. This is useful if you want to call getCustomVariable later in the visit. + * + * By default, Custom Variables of scope "visit" are not stored on the visitor's computer. + */ + this.storeCustomVariablesInCookie = function () { + configStoreCustomVariablesInCookie = true; + }; - /** - * Removes specified file extensions from the list of recognized downloads - * - * @param string|array extensions for example 'custom' or ['custom1','custom2','custom3'] - */ - removeDownloadExtensions: function (extensions) { - var i, newExtensions = []; - if(isString(extensions)) { - extensions = extensions.split('|'); - } - for (i=0; i < configDownloadExtensions.length; i++) { - if (indexOfArray(extensions, configDownloadExtensions[i]) === -1) { - newExtensions.push(configDownloadExtensions[i]); - } - } - configDownloadExtensions = newExtensions; - }, + /** + * Set delay for link tracking (in milliseconds) + * + * @param int delay + */ + this.setLinkTrackingTimer = function (delay) { + configTrackerPause = delay; + }; - /** - * Set array of domains to be treated as local. Also supports path, eg '.piwik.org/subsite1'. In this - * case all links that don't go to '*.piwik.org/subsite1/ *' would be treated as outlinks. - * For example a link to 'piwik.org/' or 'piwik.org/subsite2' both would be treated as outlinks. - * - * Also supports page wildcard, eg 'piwik.org/index*'. In this case all links - * that don't go to piwik.org/index* would be treated as outlinks. - * - * The current domain will be added automatically if no given host alias contains a path and if no host - * alias is already given for the current host alias. Say you are on "example.org" and set - * "hostAlias = ['example.com', 'example.org/test']" then the current "example.org" domain will not be - * added as there is already a more restrictive hostAlias 'example.org/test' given. We also do not add - * it automatically if there was any other host specifying any path like - * "['example.com', 'example2.com/test']". In this case we would also not add the current - * domain "example.org" automatically as the "path" feature is used. As soon as someone uses the path - * feature, for Piwik JS Tracker to work correctly in all cases, one needs to specify all hosts - * manually. - * - * @param string|array hostsAlias - */ - setDomains: function (hostsAlias) { - configHostsAlias = isString(hostsAlias) ? [hostsAlias] : hostsAlias; + /** + * Set list of file extensions to be recognized as downloads + * + * @param string|array extensions + */ + this.setDownloadExtensions = function (extensions) { + if(isString(extensions)) { + extensions = extensions.split('|'); + } + configDownloadExtensions = extensions; + }; - var hasDomainAliasAlready = false, i = 0, alias; - for (i; i < configHostsAlias.length; i++) { - alias = String(configHostsAlias[i]); + /** + * Specify additional file extensions to be recognized as downloads + * + * @param string|array extensions for example 'custom' or ['custom1','custom2','custom3'] + */ + this.addDownloadExtensions = function (extensions) { + var i; + if(isString(extensions)) { + extensions = extensions.split('|'); + } + for (i=0; i < extensions.length; i++) { + configDownloadExtensions.push(extensions[i]); + } + }; - if (isSameHost(domainAlias, domainFixup(alias))) { - hasDomainAliasAlready = true; - break; - } + /** + * Removes specified file extensions from the list of recognized downloads + * + * @param string|array extensions for example 'custom' or ['custom1','custom2','custom3'] + */ + this.removeDownloadExtensions = function (extensions) { + var i, newExtensions = []; + if(isString(extensions)) { + extensions = extensions.split('|'); + } + for (i=0; i < configDownloadExtensions.length; i++) { + if (indexOfArray(extensions, configDownloadExtensions[i]) === -1) { + newExtensions.push(configDownloadExtensions[i]); + } + } + configDownloadExtensions = newExtensions; + }; - var pathName = getPathName(alias); - if (pathName && pathName !== '/' && pathName !== '/*') { - hasDomainAliasAlready = true; - break; - } + /** + * Set array of domains to be treated as local. Also supports path, eg '.piwik.org/subsite1'. In this + * case all links that don't go to '*.piwik.org/subsite1/ *' would be treated as outlinks. + * For example a link to 'piwik.org/' or 'piwik.org/subsite2' both would be treated as outlinks. + * + * Also supports page wildcard, eg 'piwik.org/index*'. In this case all links + * that don't go to piwik.org/index* would be treated as outlinks. + * + * The current domain will be added automatically if no given host alias contains a path and if no host + * alias is already given for the current host alias. Say you are on "example.org" and set + * "hostAlias = ['example.com', 'example.org/test']" then the current "example.org" domain will not be + * added as there is already a more restrictive hostAlias 'example.org/test' given. We also do not add + * it automatically if there was any other host specifying any path like + * "['example.com', 'example2.com/test']". In this case we would also not add the current + * domain "example.org" automatically as the "path" feature is used. As soon as someone uses the path + * feature, for Piwik JS Tracker to work correctly in all cases, one needs to specify all hosts + * manually. + * + * @param string|array hostsAlias + */ + this.setDomains = function (hostsAlias) { + configHostsAlias = isString(hostsAlias) ? [hostsAlias] : hostsAlias; + + var hasDomainAliasAlready = false, i = 0, alias; + for (i; i < configHostsAlias.length; i++) { + alias = String(configHostsAlias[i]); + + if (isSameHost(domainAlias, domainFixup(alias))) { + hasDomainAliasAlready = true; + break; } - // The current domain will be added automatically if no given host alias contains a path - // and if no host alias is already given for the current host alias. - if (!hasDomainAliasAlready) { - /** - * eg if domainAlias = 'piwik.org' and someone set hostsAlias = ['piwik.org/foo'] then we should - * not add piwik.org as it would increase the allowed scope. - */ - configHostsAlias.push(domainAlias); + var pathName = getPathName(alias); + if (pathName && pathName !== '/' && pathName !== '/*') { + hasDomainAliasAlready = true; + break; } - }, + } - /** - * Set array of classes to be ignored if present in link - * - * @param string|array ignoreClasses - */ - setIgnoreClasses: function (ignoreClasses) { - configIgnoreClasses = isString(ignoreClasses) ? [ignoreClasses] : ignoreClasses; - }, + // The current domain will be added automatically if no given host alias contains a path + // and if no host alias is already given for the current host alias. + if (!hasDomainAliasAlready) { + /** + * eg if domainAlias = 'piwik.org' and someone set hostsAlias = ['piwik.org/foo'] then we should + * not add piwik.org as it would increase the allowed scope. + */ + configHostsAlias.push(domainAlias); + } + }; - /** - * Set request method - * - * @param string method GET or POST; default is GET - */ - setRequestMethod: function (method) { - configRequestMethod = method || defaultRequestMethod; - }, + /** + * Set array of classes to be ignored if present in link + * + * @param string|array ignoreClasses + */ + this.setIgnoreClasses = function (ignoreClasses) { + configIgnoreClasses = isString(ignoreClasses) ? [ignoreClasses] : ignoreClasses; + }; - /** - * Set request Content-Type header value, applicable when POST request method is used for submitting tracking events. - * See XMLHttpRequest Level 2 spec, section 4.7.2 for invalid headers - * @link http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html - * - * @param string requestContentType; default is 'application/x-www-form-urlencoded; charset=UTF-8' - */ - setRequestContentType: function (requestContentType) { - configRequestContentType = requestContentType || defaultRequestContentType; - }, + /** + * Set request method + * + * @param string method GET or POST; default is GET + */ + this.setRequestMethod = function (method) { + configRequestMethod = method || defaultRequestMethod; + }; - /** - * Override referrer - * - * @param string url - */ - setReferrerUrl: function (url) { - configReferrerUrl = url; - }, + /** + * Set request Content-Type header value, applicable when POST request method is used for submitting tracking events. + * See XMLHttpRequest Level 2 spec, section 4.7.2 for invalid headers + * @link http://dvcs.w3.org/hg/xhr/raw-file/tip/Overview.html + * + * @param string requestContentType; default is 'application/x-www-form-urlencoded; charset=UTF-8' + */ + this.setRequestContentType = function (requestContentType) { + configRequestContentType = requestContentType || defaultRequestContentType; + }; - /** - * Override url - * - * @param string url - */ - setCustomUrl: function (url) { - configCustomUrl = resolveRelativeReference(locationHrefAlias, url); - }, + /** + * Override referrer + * + * @param string url + */ + this.setReferrerUrl = function (url) { + configReferrerUrl = url; + }; - /** - * Override document.title - * - * @param string title - */ - setDocumentTitle: function (title) { - configTitle = title; - }, + /** + * Override url + * + * @param string url + */ + this.setCustomUrl = function (url) { + configCustomUrl = resolveRelativeReference(locationHrefAlias, url); + }; - /** - * Set the URL of the Piwik API. It is used for Page Overlay. - * This method should only be called when the API URL differs from the tracker URL. - * - * @param string apiUrl - */ - setAPIUrl: function (apiUrl) { - configApiUrl = apiUrl; - }, + /** + * Override document.title + * + * @param string title + */ + this.setDocumentTitle = function (title) { + configTitle = title; + }; - /** - * Set array of classes to be treated as downloads - * - * @param string|array downloadClasses - */ - setDownloadClasses: function (downloadClasses) { - configDownloadClasses = isString(downloadClasses) ? [downloadClasses] : downloadClasses; - }, + /** + * Set the URL of the Piwik API. It is used for Page Overlay. + * This method should only be called when the API URL differs from the tracker URL. + * + * @param string apiUrl + */ + this.setAPIUrl = function (apiUrl) { + configApiUrl = apiUrl; + }; - /** - * Set array of classes to be treated as outlinks - * - * @param string|array linkClasses - */ - setLinkClasses: function (linkClasses) { - configLinkClasses = isString(linkClasses) ? [linkClasses] : linkClasses; - }, + /** + * Set array of classes to be treated as downloads + * + * @param string|array downloadClasses + */ + this.setDownloadClasses = function (downloadClasses) { + configDownloadClasses = isString(downloadClasses) ? [downloadClasses] : downloadClasses; + }; - /** - * Set array of campaign name parameters - * - * @see http://piwik.org/faq/how-to/#faq_120 - * @param string|array campaignNames - */ - setCampaignNameKey: function (campaignNames) { - configCampaignNameParameters = isString(campaignNames) ? [campaignNames] : campaignNames; - }, + /** + * Set array of classes to be treated as outlinks + * + * @param string|array linkClasses + */ + this.setLinkClasses = function (linkClasses) { + configLinkClasses = isString(linkClasses) ? [linkClasses] : linkClasses; + }; - /** - * Set array of campaign keyword parameters - * - * @see http://piwik.org/faq/how-to/#faq_120 - * @param string|array campaignKeywords - */ - setCampaignKeywordKey: function (campaignKeywords) { - configCampaignKeywordParameters = isString(campaignKeywords) ? [campaignKeywords] : campaignKeywords; - }, + /** + * Set array of campaign name parameters + * + * @see http://piwik.org/faq/how-to/#faq_120 + * @param string|array campaignNames + */ + this.setCampaignNameKey = function (campaignNames) { + configCampaignNameParameters = isString(campaignNames) ? [campaignNames] : campaignNames; + }; - /** - * Strip hash tag (or anchor) from URL - * Note: this can be done in the Piwik>Settings>Websites on a per-website basis - * - * @deprecated - * @param bool enableFilter - */ - discardHashTag: function (enableFilter) { - configDiscardHashTag = enableFilter; - }, + /** + * Set array of campaign keyword parameters + * + * @see http://piwik.org/faq/how-to/#faq_120 + * @param string|array campaignKeywords + */ + this.setCampaignKeywordKey = function (campaignKeywords) { + configCampaignKeywordParameters = isString(campaignKeywords) ? [campaignKeywords] : campaignKeywords; + }; - /** - * Set first-party cookie name prefix - * - * @param string cookieNamePrefix - */ - setCookieNamePrefix: function (cookieNamePrefix) { - configCookieNamePrefix = cookieNamePrefix; - // Re-init the Custom Variables cookie - customVariables = getCustomVariablesFromCookie(); - }, + /** + * Strip hash tag (or anchor) from URL + * Note: this can be done in the Piwik>Settings>Websites on a per-website basis + * + * @deprecated + * @param bool enableFilter + */ + this.discardHashTag = function (enableFilter) { + configDiscardHashTag = enableFilter; + }; - /** - * Set first-party cookie domain - * - * @param string domain - */ - setCookieDomain: function (domain) { - var domainFixed = domainFixup(domain); + /** + * Set first-party cookie name prefix + * + * @param string cookieNamePrefix + */ + this.setCookieNamePrefix = function (cookieNamePrefix) { + configCookieNamePrefix = cookieNamePrefix; + // Re-init the Custom Variables cookie + customVariables = getCustomVariablesFromCookie(); + }; - if (isPossibleToSetCookieOnDomain(domainFixed)) { - configCookieDomain = domainFixed; - updateDomainHash(); - } - }, + /** + * Set first-party cookie domain + * + * @param string domain + */ + this.setCookieDomain = function (domain) { + var domainFixed = domainFixup(domain); - /** - * Set first-party cookie path - * - * @param string domain - */ - setCookiePath: function (path) { - configCookiePath = path; + if (isPossibleToSetCookieOnDomain(domainFixed)) { + configCookieDomain = domainFixed; updateDomainHash(); - }, + } + }; - /** - * Set visitor cookie timeout (in seconds) - * Defaults to 13 months (timeout=33955200) - * - * @param int timeout - */ - setVisitorCookieTimeout: function (timeout) { - configVisitorCookieTimeout = timeout * 1000; - }, + /** + * Set first-party cookie path + * + * @param string domain + */ + this.setCookiePath = function (path) { + configCookiePath = path; + updateDomainHash(); + }; - /** - * Set session cookie timeout (in seconds). - * Defaults to 30 minutes (timeout=1800) - * - * @param int timeout - */ - setSessionCookieTimeout: function (timeout) { - configSessionCookieTimeout = timeout * 1000; - }, + /** + * Set visitor cookie timeout (in seconds) + * Defaults to 13 months (timeout=33955200) + * + * @param int timeout + */ + this.setVisitorCookieTimeout = function (timeout) { + configVisitorCookieTimeout = timeout * 1000; + }; - /** - * Set referral cookie timeout (in seconds). - * Defaults to 6 months (15768000000) - * - * @param int timeout - */ - setReferralCookieTimeout: function (timeout) { - configReferralCookieTimeout = timeout * 1000; - }, + /** + * Set session cookie timeout (in seconds). + * Defaults to 30 minutes (timeout=1800) + * + * @param int timeout + */ + this.setSessionCookieTimeout = function (timeout) { + configSessionCookieTimeout = timeout * 1000; + }; - /** - * Set conversion attribution to first referrer and campaign - * - * @param bool if true, use first referrer (and first campaign) - * if false, use the last referrer (or campaign) - */ - setConversionAttributionFirstReferrer: function (enable) { - configConversionAttributionFirstReferrer = enable; - }, + /** + * Set referral cookie timeout (in seconds). + * Defaults to 6 months (15768000000) + * + * @param int timeout + */ + this.setReferralCookieTimeout = function (timeout) { + configReferralCookieTimeout = timeout * 1000; + }; - /** - * Disables all cookies from being set - * - * Existing cookies will be deleted on the next call to track - */ - disableCookies: function () { - configCookiesDisabled = true; - browserFeatures.cookie = '0'; + /** + * Set conversion attribution to first referrer and campaign + * + * @param bool if true, use first referrer (and first campaign) + * if false, use the last referrer (or campaign) + */ + this.setConversionAttributionFirstReferrer = function (enable) { + configConversionAttributionFirstReferrer = enable; + }; - if (configTrackerSiteId) { - deleteCookies(); - } - }, + /** + * Disables all cookies from being set + * + * Existing cookies will be deleted on the next call to track + */ + this.disableCookies = function () { + configCookiesDisabled = true; + browserFeatures.cookie = '0'; - /** - * One off cookies clearing. Useful to call this when you know for sure a new visitor is using the same browser, - * it maybe helps to "reset" tracking cookies to prevent data reuse for different users. - */ - deleteCookies: function () { + if (configTrackerSiteId) { deleteCookies(); - }, + } + }; - /** - * Handle do-not-track requests - * - * @param bool enable If true, don't track if user agent sends 'do-not-track' header - */ - setDoNotTrack: function (enable) { - var dnt = navigatorAlias.doNotTrack || navigatorAlias.msDoNotTrack; - configDoNotTrack = enable && (dnt === 'yes' || dnt === '1'); + /** + * One off cookies clearing. Useful to call this when you know for sure a new visitor is using the same browser, + * it maybe helps to "reset" tracking cookies to prevent data reuse for different users. + */ + this.deleteCookies = function () { + deleteCookies(); + }; - // do not track also disables cookies and deletes existing cookies - if (configDoNotTrack) { - this.disableCookies(); - } - }, + /** + * Handle do-not-track requests + * + * @param bool enable If true, don't track if user agent sends 'do-not-track' header + */ + this.setDoNotTrack = function (enable) { + var dnt = navigatorAlias.doNotTrack || navigatorAlias.msDoNotTrack; + configDoNotTrack = enable && (dnt === 'yes' || dnt === '1'); - /** - * Add click listener to a specific link element. - * When clicked, Piwik will log the click automatically. - * - * @param DOMElement element - * @param bool enable If true, use pseudo click-handler (middle click + context menu) - */ - addListener: function (element, enable) { - addClickListener(element, enable); - }, + // do not track also disables cookies and deletes existing cookies + if (configDoNotTrack) { + this.disableCookies(); + } + }; - /** - * Install link tracker - * - * The default behaviour is to use actual click events. However, some browsers - * (e.g., Firefox, Opera, and Konqueror) don't generate click events for the middle mouse button. - * - * To capture more "clicks", the pseudo click-handler uses mousedown + mouseup events. - * This is not industry standard and is vulnerable to false positives (e.g., drag events). - * - * There is a Safari/Chrome/Webkit bug that prevents tracking requests from being sent - * by either click handler. The workaround is to set a target attribute (which can't - * be "_self", "_top", or "_parent"). - * - * @see https://bugs.webkit.org/show_bug.cgi?id=54783 - * - * @param bool enable If "true", use pseudo click-handler (treat middle click and open contextmenu as - * left click). A right click (or any click that opens the context menu) on a link - * will be tracked as clicked even if "Open in new tab" is not selected. If - * "false" (default), nothing will be tracked on open context menu or middle click. - * The context menu is usually opened to open a link / download in a new tab - * therefore you can get more accurate results by treat it as a click but it can lead - * to wrong click numbers. - */ - enableLinkTracking: function (enable) { - linkTrackingEnabled = true; + /** + * Add click listener to a specific link element. + * When clicked, Piwik will log the click automatically. + * + * @param DOMElement element + * @param bool enable If true, use pseudo click-handler (middle click + context menu) + */ + this.addListener = function (element, enable) { + addClickListener(element, enable); + }; - trackCallback(function () { - trackCallbackOnReady(function () { - addClickListeners(enable); - }); + /** + * Install link tracker + * + * The default behaviour is to use actual click events. However, some browsers + * (e.g., Firefox, Opera, and Konqueror) don't generate click events for the middle mouse button. + * + * To capture more "clicks", the pseudo click-handler uses mousedown + mouseup events. + * This is not industry standard and is vulnerable to false positives (e.g., drag events). + * + * There is a Safari/Chrome/Webkit bug that prevents tracking requests from being sent + * by either click handler. The workaround is to set a target attribute (which can't + * be "_self", "_top", or "_parent"). + * + * @see https://bugs.webkit.org/show_bug.cgi?id=54783 + * + * @param bool enable If "true", use pseudo click-handler (treat middle click and open contextmenu as + * left click). A right click (or any click that opens the context menu) on a link + * will be tracked as clicked even if "Open in new tab" is not selected. If + * "false" (default), nothing will be tracked on open context menu or middle click. + * The context menu is usually opened to open a link / download in a new tab + * therefore you can get more accurate results by treat it as a click but it can lead + * to wrong click numbers. + */ + this.enableLinkTracking = function (enable) { + linkTrackingEnabled = true; + + trackCallback(function () { + trackCallbackOnReady(function () { + addClickListeners(enable); }); - }, + }); + }; - /** - * Enable tracking of uncatched JavaScript errors - * - * If enabled, uncaught JavaScript Errors will be tracked as an event by defining a - * window.onerror handler. If a window.onerror handler is already defined we will make - * sure to call this previously registered error handler after tracking the error. - * - * By default we return false in the window.onerror handler to make sure the error still - * appears in the browser's console etc. Note: Some older browsers might behave differently - * so it could happen that an actual JavaScript error will be suppressed. - * If a window.onerror handler was registered we will return the result of this handler. - * - * Make sure not to overwrite the window.onerror handler after enabling the JS error - * tracking as the error tracking won't work otherwise. To capture all JS errors we - * recommend to include the Piwik JavaScript tracker in the HTML as early as possible. - * If possible directly in <head></head> before loading any other JavaScript. - */ - enableJSErrorTracking: function () { - if (enableJSErrorTracking) { - return; - } + /** + * Enable tracking of uncatched JavaScript errors + * + * If enabled, uncaught JavaScript Errors will be tracked as an event by defining a + * window.onerror handler. If a window.onerror handler is already defined we will make + * sure to call this previously registered error handler after tracking the error. + * + * By default we return false in the window.onerror handler to make sure the error still + * appears in the browser's console etc. Note: Some older browsers might behave differently + * so it could happen that an actual JavaScript error will be suppressed. + * If a window.onerror handler was registered we will return the result of this handler. + * + * Make sure not to overwrite the window.onerror handler after enabling the JS error + * tracking as the error tracking won't work otherwise. To capture all JS errors we + * recommend to include the Piwik JavaScript tracker in the HTML as early as possible. + * If possible directly in <head></head> before loading any other JavaScript. + */ + this.enableJSErrorTracking = function () { + if (enableJSErrorTracking) { + return; + } - enableJSErrorTracking = true; - var onError = windowAlias.onerror; + enableJSErrorTracking = true; + var onError = windowAlias.onerror; - windowAlias.onerror = function (message, url, linenumber, column, error) { - trackCallback(function () { - var category = 'JavaScript Errors'; + windowAlias.onerror = function (message, url, linenumber, column, error) { + trackCallback(function () { + var category = 'JavaScript Errors'; - var action = url + ':' + linenumber; - if (column) { - action += ':' + column; - } + var action = url + ':' + linenumber; + if (column) { + action += ':' + column; + } - logEvent(category, action, message); - }); + logEvent(category, action, message); + }); - if (onError) { - return onError(message, url, linenumber, column, error); - } + if (onError) { + return onError(message, url, linenumber, column, error); + } - return false; - }; - }, + return false; + }; + }; - /** - * Disable automatic performance tracking - */ - disablePerformanceTracking: function () { - configPerformanceTrackingEnabled = false; - }, + /** + * Disable automatic performance tracking + */ + this.disablePerformanceTracking = function () { + configPerformanceTrackingEnabled = false; + }; - /** - * Set the server generation time. - * If set, the browser's performance.timing API in not used anymore to determine the time. - * - * @param int generationTime - */ - setGenerationTimeMs: function (generationTime) { - configPerformanceGenerationTime = parseInt(generationTime, 10); - }, + /** + * Set the server generation time. + * If set, the browser's performance.timing API in not used anymore to determine the time. + * + * @param int generationTime + */ + this.setGenerationTimeMs = function (generationTime) { + configPerformanceGenerationTime = parseInt(generationTime, 10); + }; - /** - * Set heartbeat (in seconds) - * - * @param int heartBeatDelayInSeconds Defaults to 15. Cannot be lower than 1. - */ - enableHeartBeatTimer: function (heartBeatDelayInSeconds) { - heartBeatDelayInSeconds = Math.max(heartBeatDelayInSeconds, 1); - configHeartBeatDelay = (heartBeatDelayInSeconds || 15) * 1000; + /** + * Set heartbeat (in seconds) + * + * @param int heartBeatDelayInSeconds Defaults to 15. Cannot be lower than 1. + */ + this.enableHeartBeatTimer = function (heartBeatDelayInSeconds) { + heartBeatDelayInSeconds = Math.max(heartBeatDelayInSeconds, 1); + configHeartBeatDelay = (heartBeatDelayInSeconds || 15) * 1000; - // if a tracking request has already been sent, start the heart beat timeout - if (lastTrackerRequestTime !== null) { - setUpHeartBeat(); - } - }, + // if a tracking request has already been sent, start the heart beat timeout + if (lastTrackerRequestTime !== null) { + setUpHeartBeat(); + } + }; /*<DEBUG>*/ - /** - * Clear heartbeat. - */ - disableHeartBeatTimer: function () { - heartBeatDown(); - configHeartBeatDelay = null; + /** + * Clear heartbeat. + */ + this.disableHeartBeatTimer = function () { + heartBeatDown(); + configHeartBeatDelay = null; - window.removeEventListener('focus', heartBeatOnFocus); - window.removeEventListener('blur', heartBeatOnBlur); - }, + window.removeEventListener('focus', heartBeatOnFocus); + window.removeEventListener('blur', heartBeatOnBlur); + }; /*</DEBUG>*/ - /** - * Frame buster - */ - killFrame: function () { - if (windowAlias.location !== windowAlias.top.location) { - windowAlias.top.location = windowAlias.location; - } - }, + /** + * Frame buster + */ + this.killFrame = function () { + if (windowAlias.location !== windowAlias.top.location) { + windowAlias.top.location = windowAlias.location; + } + }; - /** - * Redirect if browsing offline (aka file: buster) - * - * @param string url Redirect to this URL - */ - redirectFile: function (url) { - if (windowAlias.location.protocol === 'file:') { - windowAlias.location = url; - } - }, + /** + * Redirect if browsing offline (aka file: buster) + * + * @param string url Redirect to this URL + */ + this.redirectFile = function (url) { + if (windowAlias.location.protocol === 'file:') { + windowAlias.location = url; + } + }; - /** - * Count sites in pre-rendered state - * - * @param bool enable If true, track when in pre-rendered state - */ - setCountPreRendered: function (enable) { - configCountPreRendered = enable; - }, + /** + * Count sites in pre-rendered state + * + * @param bool enable If true, track when in pre-rendered state + */ + this.setCountPreRendered = function (enable) { + configCountPreRendered = enable; + }; - /** - * Trigger a goal - * - * @param int|string idGoal - * @param int|float customRevenue - * @param mixed customData - */ - trackGoal: function (idGoal, customRevenue, customData) { + /** + * Trigger a goal + * + * @param int|string idGoal + * @param int|float customRevenue + * @param mixed customData + */ + this.trackGoal = function (idGoal, customRevenue, customData) { + trackCallback(function () { + logGoal(idGoal, customRevenue, customData); + }); + }; + + /** + * Manually log a click from your own code + * + * @param string sourceUrl + * @param string linkType + * @param mixed customData + * @param function callback + */ + this.trackLink = function (sourceUrl, linkType, customData, callback) { + trackCallback(function () { + logLink(sourceUrl, linkType, customData, callback); + }); + }; + + /** + * Log visit to this page + * + * @param string customTitle + * @param mixed customData + * @param function callback + */ + this.trackPageView = function (customTitle, customData, callback) { + trackedContentImpressions = []; + + if (isOverlaySession(configTrackerSiteId)) { trackCallback(function () { - logGoal(idGoal, customRevenue, customData); + injectOverlayScripts(configTrackerUrl, configApiUrl, configTrackerSiteId); }); - }, - - /** - * Manually log a click from your own code - * - * @param string sourceUrl - * @param string linkType - * @param mixed customData - * @param function callback - */ - trackLink: function (sourceUrl, linkType, customData, callback) { + } else { trackCallback(function () { - logLink(sourceUrl, linkType, customData, callback); + logPageView(customTitle, customData, callback); }); - }, + } + }; - /** - * Log visit to this page - * - * @param string customTitle - * @param mixed customData - * @param function callback - */ - trackPageView: function (customTitle, customData, callback) { - trackedContentImpressions = []; + /** + * Scans the entire DOM for all content blocks and tracks all impressions once the DOM ready event has + * been triggered. + * + * If you only want to track visible content impressions have a look at `trackVisibleContentImpressions()`. + * We do not track an impression of the same content block twice if you call this method multiple times + * unless `trackPageView()` is called meanwhile. This is useful for single page applications. + */ + this.trackAllContentImpressions = function () { + if (isOverlaySession(configTrackerSiteId)) { + return; + } - if (isOverlaySession(configTrackerSiteId)) { - trackCallback(function () { - injectOverlayScripts(configTrackerUrl, configApiUrl, configTrackerSiteId); - }); - } else { - trackCallback(function () { - logPageView(customTitle, customData, callback); - }); - } - }, + trackCallback(function () { + trackCallbackOnReady(function () { + // we have to wait till DOM ready + var contentNodes = content.findContentNodes(); + var requests = getContentImpressionsRequestsFromNodes(contentNodes); + + sendBulkRequest(requests, configTrackerPause); + }); + }); + }; + + /** + * Scans the entire DOM for all content blocks as soon as the page is loaded. It tracks an impression + * only if a content block is actually visible. Meaning it is not hidden and the content is or was at + * some point in the viewport. + * + * If you want to track all content blocks have a look at `trackAllContentImpressions()`. + * We do not track an impression of the same content block twice if you call this method multiple times + * unless `trackPageView()` is called meanwhile. This is useful for single page applications. + * + * Once you have called this method you can no longer change `checkOnScroll` or `timeIntervalInMs`. + * + * If you do want to only track visible content blocks but not want us to perform any automatic checks + * as they can slow down your frames per second you can call `trackVisibleContentImpressions()` or + * `trackContentImpressionsWithinNode()` manually at any time to rescan the entire DOM for newly + * visible content blocks. + * o Call `trackVisibleContentImpressions(false, 0)` to initially track only visible content impressions + * o Call `trackVisibleContentImpressions()` at any time again to rescan the entire DOM for newly visible content blocks or + * o Call `trackContentImpressionsWithinNode(node)` at any time to rescan only a part of the DOM for newly visible content blocks + * + * @param boolean [checkOnScroll=true] Optional, you can disable rescanning the entire DOM automatically + * after each scroll event by passing the value `false`. If enabled, + * we check whether a previously hidden content blocks became visible + * after a scroll and if so track the impression. + * Note: If a content block is placed within a scrollable element + * (`overflow: scroll`), we can currently not detect when this block + * becomes visible. + * @param integer [timeIntervalInMs=750] Optional, you can define an interval to rescan the entire DOM + * for new impressions every X milliseconds by passing + * for instance `timeIntervalInMs=500` (rescan DOM every 500ms). + * Rescanning the entire DOM and detecting the visible state of content + * blocks can take a while depending on the browser and amount of content. + * In case your frames per second goes down you might want to increase + * this value or disable it by passing the value `0`. + */ + this.trackVisibleContentImpressions = function (checkOnSroll, timeIntervalInMs) { + if (isOverlaySession(configTrackerSiteId)) { + return; + } + + if (!isDefined(checkOnSroll)) { + checkOnSroll = true; + } - /** - * Scans the entire DOM for all content blocks and tracks all impressions once the DOM ready event has - * been triggered. - * - * If you only want to track visible content impressions have a look at `trackVisibleContentImpressions()`. - * We do not track an impression of the same content block twice if you call this method multiple times - * unless `trackPageView()` is called meanwhile. This is useful for single page applications. - */ - trackAllContentImpressions: function () { - if (isOverlaySession(configTrackerSiteId)) { - return; - } + if (!isDefined(timeIntervalInMs)) { + timeIntervalInMs = 750; + } - trackCallback(function () { - trackCallbackOnReady(function () { - // we have to wait till DOM ready - var contentNodes = content.findContentNodes(); - var requests = getContentImpressionsRequestsFromNodes(contentNodes); + enableTrackOnlyVisibleContent(checkOnSroll, timeIntervalInMs, this); - sendBulkRequest(requests, configTrackerPause); - }); + trackCallback(function () { + trackCallbackOnLoad(function () { + // we have to wait till CSS parsed and applied + var contentNodes = content.findContentNodes(); + var requests = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet(contentNodes); + + sendBulkRequest(requests, configTrackerPause); }); - }, + }); + }; - /** - * Scans the entire DOM for all content blocks as soon as the page is loaded. It tracks an impression - * only if a content block is actually visible. Meaning it is not hidden and the content is or was at - * some point in the viewport. - * - * If you want to track all content blocks have a look at `trackAllContentImpressions()`. - * We do not track an impression of the same content block twice if you call this method multiple times - * unless `trackPageView()` is called meanwhile. This is useful for single page applications. - * - * Once you have called this method you can no longer change `checkOnScroll` or `timeIntervalInMs`. - * - * If you do want to only track visible content blocks but not want us to perform any automatic checks - * as they can slow down your frames per second you can call `trackVisibleContentImpressions()` or - * `trackContentImpressionsWithinNode()` manually at any time to rescan the entire DOM for newly - * visible content blocks. - * o Call `trackVisibleContentImpressions(false, 0)` to initially track only visible content impressions - * o Call `trackVisibleContentImpressions()` at any time again to rescan the entire DOM for newly visible content blocks or - * o Call `trackContentImpressionsWithinNode(node)` at any time to rescan only a part of the DOM for newly visible content blocks - * - * @param boolean [checkOnScroll=true] Optional, you can disable rescanning the entire DOM automatically - * after each scroll event by passing the value `false`. If enabled, - * we check whether a previously hidden content blocks became visible - * after a scroll and if so track the impression. - * Note: If a content block is placed within a scrollable element - * (`overflow: scroll`), we can currently not detect when this block - * becomes visible. - * @param integer [timeIntervalInMs=750] Optional, you can define an interval to rescan the entire DOM - * for new impressions every X milliseconds by passing - * for instance `timeIntervalInMs=500` (rescan DOM every 500ms). - * Rescanning the entire DOM and detecting the visible state of content - * blocks can take a while depending on the browser and amount of content. - * In case your frames per second goes down you might want to increase - * this value or disable it by passing the value `0`. - */ - trackVisibleContentImpressions: function (checkOnSroll, timeIntervalInMs) { - if (isOverlaySession(configTrackerSiteId)) { - return; - } + /** + * Tracks a content impression using the specified values. You should not call this method too often + * as each call causes an XHR tracking request and can slow down your site or your server. + * + * @param string contentName For instance "Ad Sale". + * @param string [contentPiece='Unknown'] For instance a path to an image or the text of a text ad. + * @param string [contentTarget] For instance the URL of a landing page. + */ + this.trackContentImpression = function (contentName, contentPiece, contentTarget) { + if (isOverlaySession(configTrackerSiteId)) { + return; + } - if (!isDefined(checkOnSroll)) { - checkOnSroll = true; - } + if (!contentName) { + return; + } - if (!isDefined(timeIntervalInMs)) { - timeIntervalInMs = 750; - } + contentPiece = contentPiece || 'Unknown'; - enableTrackOnlyVisibleContent(checkOnSroll, timeIntervalInMs, this); + trackCallback(function () { + var request = buildContentImpressionRequest(contentName, contentPiece, contentTarget); + sendRequest(request, configTrackerPause); + }); + }; - trackCallback(function () { + /** + * Scans the given DOM node and its children for content blocks and tracks an impression for them if + * no impression was already tracked for it. If you have called `trackVisibleContentImpressions()` + * upfront only visible content blocks will be tracked. You can use this method if you, for instance, + * dynamically add an element using JavaScript to your DOM after we have tracked the initial impressions. + * + * @param Element domNode + */ + this.trackContentImpressionsWithinNode = function (domNode) { + if (isOverlaySession(configTrackerSiteId) || !domNode) { + return; + } + + trackCallback(function () { + if (isTrackOnlyVisibleContentEnabled) { trackCallbackOnLoad(function () { // we have to wait till CSS parsed and applied - var contentNodes = content.findContentNodes(); - var requests = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet(contentNodes); + var contentNodes = content.findContentNodesWithinNode(domNode); + var requests = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet(contentNodes); sendBulkRequest(requests, configTrackerPause); }); - }); - }, - - /** - * Tracks a content impression using the specified values. You should not call this method too often - * as each call causes an XHR tracking request and can slow down your site or your server. - * - * @param string contentName For instance "Ad Sale". - * @param string [contentPiece='Unknown'] For instance a path to an image or the text of a text ad. - * @param string [contentTarget] For instance the URL of a landing page. - */ - trackContentImpression: function (contentName, contentPiece, contentTarget) { - if (isOverlaySession(configTrackerSiteId)) { - return; - } - - if (!contentName) { - return; - } - - contentPiece = contentPiece || 'Unknown'; - - trackCallback(function () { - var request = buildContentImpressionRequest(contentName, contentPiece, contentTarget); - sendRequest(request, configTrackerPause); - }); - }, + } else { + trackCallbackOnReady(function () { + // we have to wait till DOM ready + var contentNodes = content.findContentNodesWithinNode(domNode); - /** - * Scans the given DOM node and its children for content blocks and tracks an impression for them if - * no impression was already tracked for it. If you have called `trackVisibleContentImpressions()` - * upfront only visible content blocks will be tracked. You can use this method if you, for instance, - * dynamically add an element using JavaScript to your DOM after we have tracked the initial impressions. - * - * @param Element domNode - */ - trackContentImpressionsWithinNode: function (domNode) { - if (isOverlaySession(configTrackerSiteId) || !domNode) { - return; + var requests = getContentImpressionsRequestsFromNodes(contentNodes); + sendBulkRequest(requests, configTrackerPause); + }); } + }); + }; - trackCallback(function () { - if (isTrackOnlyVisibleContentEnabled) { - trackCallbackOnLoad(function () { - // we have to wait till CSS parsed and applied - var contentNodes = content.findContentNodesWithinNode(domNode); - - var requests = getCurrentlyVisibleContentImpressionsRequestsIfNotTrackedYet(contentNodes); - sendBulkRequest(requests, configTrackerPause); - }); - } else { - trackCallbackOnReady(function () { - // we have to wait till DOM ready - var contentNodes = content.findContentNodesWithinNode(domNode); - - var requests = getContentImpressionsRequestsFromNodes(contentNodes); - sendBulkRequest(requests, configTrackerPause); - }); - } - }); - }, - - /** - * Tracks a content interaction using the specified values. You should use this method only in conjunction - * with `trackContentImpression()`. The specified `contentName` and `contentPiece` has to be exactly the - * same as the ones that were used in `trackContentImpression()`. Otherwise the interaction will not count. - * - * @param string contentInteraction The type of interaction that happened. For instance 'click' or 'submit'. - * @param string contentName The name of the content. For instance "Ad Sale". - * @param string [contentPiece='Unknown'] The actual content. For instance a path to an image or the text of a text ad. - * @param string [contentTarget] For instance the URL of a landing page. - */ - trackContentInteraction: function (contentInteraction, contentName, contentPiece, contentTarget) { - if (isOverlaySession(configTrackerSiteId)) { - return; - } + /** + * Tracks a content interaction using the specified values. You should use this method only in conjunction + * with `trackContentImpression()`. The specified `contentName` and `contentPiece` has to be exactly the + * same as the ones that were used in `trackContentImpression()`. Otherwise the interaction will not count. + * + * @param string contentInteraction The type of interaction that happened. For instance 'click' or 'submit'. + * @param string contentName The name of the content. For instance "Ad Sale". + * @param string [contentPiece='Unknown'] The actual content. For instance a path to an image or the text of a text ad. + * @param string [contentTarget] For instance the URL of a landing page. + */ + this.trackContentInteraction = function (contentInteraction, contentName, contentPiece, contentTarget) { + if (isOverlaySession(configTrackerSiteId)) { + return; + } - if (!contentInteraction || !contentName) { - return; - } + if (!contentInteraction || !contentName) { + return; + } - contentPiece = contentPiece || 'Unknown'; + contentPiece = contentPiece || 'Unknown'; - trackCallback(function () { - var request = buildContentInteractionRequest(contentInteraction, contentName, contentPiece, contentTarget); - sendRequest(request, configTrackerPause); - }); - }, + trackCallback(function () { + var request = buildContentInteractionRequest(contentInteraction, contentName, contentPiece, contentTarget); + sendRequest(request, configTrackerPause); + }); + }; - /** - * Tracks an interaction with the given DOM node / content block. - * - * By default we track interactions on click but sometimes you might want to track interactions yourself. - * For instance you might want to track an interaction manually on a double click or a form submit. - * Make sure to disable the automatic interaction tracking in this case by specifying either the CSS - * class `piwikContentIgnoreInteraction` or the attribute `data-content-ignoreinteraction`. - * - * @param Element domNode This element itself or any of its parent elements has to be a content block - * element. Meaning one of those has to have a `piwikTrackContent` CSS class or - * a `data-track-content` attribute. - * @param string [contentInteraction='Unknown] The name of the interaction that happened. For instance - * 'click', 'formSubmit', 'DblClick', ... - */ - trackContentInteractionNode: function (domNode, contentInteraction) { - if (isOverlaySession(configTrackerSiteId) || !domNode) { - return; - } + /** + * Tracks an interaction with the given DOM node / content block. + * + * By default we track interactions on click but sometimes you might want to track interactions yourself. + * For instance you might want to track an interaction manually on a double click or a form submit. + * Make sure to disable the automatic interaction tracking in this case by specifying either the CSS + * class `piwikContentIgnoreInteraction` or the attribute `data-content-ignoreinteraction`. + * + * @param Element domNode This element itself or any of its parent elements has to be a content block + * element. Meaning one of those has to have a `piwikTrackContent` CSS class or + * a `data-track-content` attribute. + * @param string [contentInteraction='Unknown] The name of the interaction that happened. For instance + * 'click', 'formSubmit', 'DblClick', ... + */ + this.trackContentInteractionNode = function (domNode, contentInteraction) { + if (isOverlaySession(configTrackerSiteId) || !domNode) { + return; + } - trackCallback(function () { - var request = buildContentInteractionRequestNode(domNode, contentInteraction); - sendRequest(request, configTrackerPause); - }); - }, + trackCallback(function () { + var request = buildContentInteractionRequestNode(domNode, contentInteraction); + sendRequest(request, configTrackerPause); + }); + }; - /** - * Useful to debug content tracking. This method will log all detected content blocks to console - * (if the browser supports the console). It will list the detected name, piece, and target of each - * content block. - */ - logAllContentBlocksOnPage: function () { - var contentNodes = content.findContentNodes(); - var contents = content.collectContent(contentNodes); + /** + * Useful to debug content tracking. This method will log all detected content blocks to console + * (if the browser supports the console). It will list the detected name, piece, and target of each + * content block. + */ + this.logAllContentBlocksOnPage = function () { + var contentNodes = content.findContentNodes(); + var contents = content.collectContent(contentNodes); - if (console !== undefined && console && console.log) { - console.log(contents); - } - }, + if (console !== undefined && console && console.log) { + console.log(contents); + } + }; - /** - * Records an event - * - * @param string category The Event Category (Videos, Music, Games...) - * @param string action The Event's Action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...) - * @param string name (optional) The Event's object Name (a particular Movie name, or Song name, or File name...) - * @param float value (optional) The Event's value - * @param mixed customData - */ - trackEvent: function (category, action, name, value, customData) { - trackCallback(function () { - logEvent(category, action, name, value, customData); - }); - }, + /** + * Records an event + * + * @param string category The Event Category (Videos, Music, Games...) + * @param string action The Event's Action (Play, Pause, Duration, Add Playlist, Downloaded, Clicked...) + * @param string name (optional) The Event's object Name (a particular Movie name, or Song name, or File name...) + * @param float value (optional) The Event's value + * @param mixed customData + */ + this.trackEvent = function (category, action, name, value, customData) { + trackCallback(function () { + logEvent(category, action, name, value, customData); + }); + }; - /** - * Log special pageview: Internal search - * - * @param string keyword - * @param string category - * @param int resultsCount - * @param mixed customData - */ - trackSiteSearch: function (keyword, category, resultsCount, customData) { - trackCallback(function () { - logSiteSearch(keyword, category, resultsCount, customData); - }); - }, + /** + * Log special pageview: Internal search + * + * @param string keyword + * @param string category + * @param int resultsCount + * @param mixed customData + */ + this.trackSiteSearch = function (keyword, category, resultsCount, customData) { + trackCallback(function () { + logSiteSearch(keyword, category, resultsCount, customData); + }); + }; - /** - * Used to record that the current page view is an item (product) page view, or a Ecommerce Category page view. - * This must be called before trackPageView() on the product/category page. - * It will set 3 custom variables of scope "page" with the SKU, Name and Category for this page view. - * Note: Custom Variables of scope "page" slots 3, 4 and 5 will be used. - * - * On a category page, you can set the parameter category, and set the other parameters to empty string or false - * - * Tracking Product/Category page views will allow Piwik to report on Product & Categories - * conversion rates (Conversion rate = Ecommerce orders containing this product or category / Visits to the product or category) - * - * @param string sku Item's SKU code being viewed - * @param string name Item's Name being viewed - * @param string category Category page being viewed. On an Item's page, this is the item's category - * @param float price Item's display price, not use in standard Piwik reports, but output in API product reports. - */ - setEcommerceView: function (sku, name, category, price) { - if (!isDefined(category) || !category.length) { - category = ""; - } else if (category instanceof Array) { - category = JSON2.stringify(category); - } + /** + * Used to record that the current page view is an item (product) page view, or a Ecommerce Category page view. + * This must be called before trackPageView() on the product/category page. + * It will set 3 custom variables of scope "page" with the SKU, Name and Category for this page view. + * Note: Custom Variables of scope "page" slots 3, 4 and 5 will be used. + * + * On a category page, you can set the parameter category, and set the other parameters to empty string or false + * + * Tracking Product/Category page views will allow Piwik to report on Product & Categories + * conversion rates (Conversion rate = Ecommerce orders containing this product or category / Visits to the product or category) + * + * @param string sku Item's SKU code being viewed + * @param string name Item's Name being viewed + * @param string category Category page being viewed. On an Item's page, this is the item's category + * @param float price Item's display price, not use in standard Piwik reports, but output in API product reports. + */ + this.setEcommerceView = function (sku, name, category, price) { + if (!isDefined(category) || !category.length) { + category = ""; + } else if (category instanceof Array) { + category = JSON2.stringify(category); + } - customVariablesPage[5] = ['_pkc', category]; + customVariablesPage[5] = ['_pkc', category]; - if (isDefined(price) && String(price).length) { - customVariablesPage[2] = ['_pkp', price]; - } + if (isDefined(price) && String(price).length) { + customVariablesPage[2] = ['_pkp', price]; + } - // On a category page, do not track Product name not defined - if ((!isDefined(sku) || !sku.length) - && (!isDefined(name) || !name.length)) { - return; - } + // On a category page, do not track Product name not defined + if ((!isDefined(sku) || !sku.length) + && (!isDefined(name) || !name.length)) { + return; + } - if (isDefined(sku) && sku.length) { - customVariablesPage[3] = ['_pks', sku]; - } + if (isDefined(sku) && sku.length) { + customVariablesPage[3] = ['_pks', sku]; + } - if (!isDefined(name) || !name.length) { - name = ""; - } + if (!isDefined(name) || !name.length) { + name = ""; + } - customVariablesPage[4] = ['_pkn', name]; - }, + customVariablesPage[4] = ['_pkn', name]; + }; - /** - * Adds an item (product) that is in the current Cart or in the Ecommerce order. - * This function is called for every item (product) in the Cart or the Order. - * The only required parameter is sku. - * The items are deleted from this JavaScript object when the Ecommerce order is tracked via the method trackEcommerceOrder. - * - * @param string sku (required) Item's SKU Code. This is the unique identifier for the product. - * @param string name (optional) Item's name - * @param string name (optional) Item's category, or array of up to 5 categories - * @param float price (optional) Item's price. If not specified, will default to 0 - * @param float quantity (optional) Item's quantity. If not specified, will default to 1 - */ - addEcommerceItem: function (sku, name, category, price, quantity) { - if (sku.length) { - ecommerceItems[sku] = [ sku, name, category, price, quantity ]; - } - }, + /** + * Adds an item (product) that is in the current Cart or in the Ecommerce order. + * This function is called for every item (product) in the Cart or the Order. + * The only required parameter is sku. + * The items are deleted from this JavaScript object when the Ecommerce order is tracked via the method trackEcommerceOrder. + * + * @param string sku (required) Item's SKU Code. This is the unique identifier for the product. + * @param string name (optional) Item's name + * @param string name (optional) Item's category, or array of up to 5 categories + * @param float price (optional) Item's price. If not specified, will default to 0 + * @param float quantity (optional) Item's quantity. If not specified, will default to 1 + */ + this.addEcommerceItem = function (sku, name, category, price, quantity) { + if (sku.length) { + ecommerceItems[sku] = [ sku, name, category, price, quantity ]; + } + }; - /** - * Tracks an Ecommerce order. - * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order. - * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports. - * Parameters orderId and grandTotal are required. For others, you can set to false if you don't need to specify them. - * After calling this method, items added to the cart will be removed from this JavaScript object. - * - * @param string|int orderId (required) Unique Order ID. - * This will be used to count this order only once in the event the order page is reloaded several times. - * orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik. - * @param float grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.) - * @param float subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied) - * @param float tax (optional) Tax amount for this order - * @param float shipping (optional) Shipping amount for this order - * @param float discount (optional) Discounted amount in this order - */ - trackEcommerceOrder: function (orderId, grandTotal, subTotal, tax, shipping, discount) { - logEcommerceOrder(orderId, grandTotal, subTotal, tax, shipping, discount); - }, + /** + * Tracks an Ecommerce order. + * If the Ecommerce order contains items (products), you must call first the addEcommerceItem() for each item in the order. + * All revenues (grandTotal, subTotal, tax, shipping, discount) will be individually summed and reported in Piwik reports. + * Parameters orderId and grandTotal are required. For others, you can set to false if you don't need to specify them. + * After calling this method, items added to the cart will be removed from this JavaScript object. + * + * @param string|int orderId (required) Unique Order ID. + * This will be used to count this order only once in the event the order page is reloaded several times. + * orderId must be unique for each transaction, even on different days, or the transaction will not be recorded by Piwik. + * @param float grandTotal (required) Grand Total revenue of the transaction (including tax, shipping, etc.) + * @param float subTotal (optional) Sub total amount, typically the sum of items prices for all items in this order (before Tax and Shipping costs are applied) + * @param float tax (optional) Tax amount for this order + * @param float shipping (optional) Shipping amount for this order + * @param float discount (optional) Discounted amount in this order + */ + this.trackEcommerceOrder = function (orderId, grandTotal, subTotal, tax, shipping, discount) { + logEcommerceOrder(orderId, grandTotal, subTotal, tax, shipping, discount); + }; - /** - * Tracks a Cart Update (add item, remove item, update item). - * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart, including the items that haven't been updated since the last cart update. - * Then you can call this function with the Cart grandTotal (typically the sum of all items' prices) - * Calling this method does not remove from this JavaScript object the items that were added to the cart via addEcommerceItem - * - * @param float grandTotal (required) Items (products) amount in the Cart - */ - trackEcommerceCartUpdate: function (grandTotal) { - logEcommerceCartUpdate(grandTotal); - } + /** + * Tracks a Cart Update (add item, remove item, update item). + * On every Cart update, you must call addEcommerceItem() for each item (product) in the cart, including the items that haven't been updated since the last cart update. + * Then you can call this function with the Cart grandTotal (typically the sum of all items' prices) + * Calling this method does not remove from this JavaScript object the items that were added to the cart via addEcommerceItem + * + * @param float grandTotal (required) Items (products) amount in the Cart + */ + this.trackEcommerceCartUpdate = function (grandTotal) { + logEcommerceCartUpdate(grandTotal); + }; + /** + * Sends a tracking request with custom request parameters. + * Piwik will prepend the hostname and path to Piwik, as well as all other needed tracking request + * parameters prior to sending the request. Useful eg if you track custom dimensions via a plugin. + * + * @param request eg. "param=value¶m2=value2" + * @param customData + * @param callback + */ + this.trackRequest = function (request, customData, callback) { + trackCallback(function () { + var fullRequest = getRequest(request, customData); + sendRequest(fullRequest, configTrackerPause, callback); + }); }; - } - /************************************************************ - * Proxy object - * - this allows the caller to continue push()'ing to _paq - * after the Tracker has been initialized and loaded - ************************************************************/ + Piwik.trigger('TrackerSetup', [this]); + } function TrackerProxy() { return { @@ -6503,31 +6539,97 @@ if (typeof window.Piwik !== 'object') { * Constructor ************************************************************/ - // initialize the Piwik singleton - addEventListener(windowAlias, 'beforeunload', beforeUnloadHandler, false); - - Date.prototype.getTimeAlias = Date.prototype.getTime; + var applyFirst = ['addTracker', 'disableCookies', 'setTrackerUrl', 'setAPIUrl', 'setCookiePath', 'setCookieDomain', 'setDomains', 'setUserId', 'setSiteId', 'enableLinkTracking']; - asyncTrackers.push(new Tracker()); + function createFirstTracker(piwikUrl, siteId) + { + var tracker = new Tracker(piwikUrl, siteId); + asyncTrackers.push(tracker); - var applyFirst = ['addTracker', 'disableCookies', 'setTrackerUrl', 'setAPIUrl', 'setCookiePath', 'setCookieDomain', 'setDomains', 'setUserId', 'setSiteId', 'enableLinkTracking']; - _paq = applyMethodsInOrder(_paq, applyFirst); + _paq = applyMethodsInOrder(_paq, applyFirst); - // apply the queue of actions - for (iterator = 0; iterator < _paq.length; iterator++) { - if (_paq[iterator]) { - apply(_paq[iterator]); + // apply the queue of actions + for (iterator = 0; iterator < _paq.length; iterator++) { + if (_paq[iterator]) { + apply(_paq[iterator]); + } } + + // replace initialization array with proxy object + _paq = new TrackerProxy(); + + return tracker; } - // replace initialization array with proxy object - _paq = new TrackerProxy(); + /************************************************************ + * Proxy object + * - this allows the caller to continue push()'ing to _paq + * after the Tracker has been initialized and loaded + ************************************************************/ + + // initialize the Piwik singleton + addEventListener(windowAlias, 'beforeunload', beforeUnloadHandler, false); + + Date.prototype.getTimeAlias = Date.prototype.getTime; /************************************************************ * Public data and methods ************************************************************/ Piwik = { + initialized: false, + + /** + * Listen to an event and invoke the handler when a the event is triggered. + * + * @param string event + * @param function handler + */ + on: function (event, handler) { + if (!eventHandlers[event]) { + eventHandlers[event] = []; + } + + eventHandlers[event].push(handler); + }, + + /** + * Remove a handler to no longer listen to the event. Must pass the same handler that was used when + * attaching the event via ".on". + * @param string event + * @param function handler + */ + off: function (event, handler) { + if (!eventHandlers[event]) { + return; + } + + var i = 0; + for (i; i < eventHandlers[event].length; i++) { + if (eventHandlers[event][i] === handler) { + delete eventHandlers[event][i]; + } + } + }, + + /** + * Triggers the given event and passes the parameters to all handlers. + * + * @param string event + * @param Array extraParameters + * @param Object context If given the handler will be executed in this context + */ + trigger: function (event, extraParameters, context) { + if (!eventHandlers[event]) { + return; + } + + var i = 0; + for (i; i < eventHandlers[event].length; i++) { + eventHandlers[event][i].apply(context || windowAlias, extraParameters); + } + }, + /** * Add plugin * @@ -6556,6 +6658,31 @@ if (typeof window.Piwik !== 'object') { return new Tracker(piwikUrl, siteId); }, + /** + * Get all created async trackers + * + * @return Tracker[] + */ + getAsyncTrackers: function () { + return asyncTrackers; + }, + + /** + * Adds a new tracker. All sent requests will be also sent to the given siteId and piwikUrl. + * If piwikUrl is not set, current url will be used. + * + * @param null|string piwikUrl If null, will reuse the same tracker URL of the current tracker instance + * @param int|string siteId + * @return Tracker + */ + addTracker: function (piwikUrl, siteId) { + if (!asyncTrackers.length) { + createFirstTracker(piwikUrl, siteId); + } else { + asyncTrackers[0].addTracker(piwikUrl, siteId); + } + }, + /** * Get internal asynchronous tracker object. * @@ -6609,6 +6736,28 @@ if (typeof window.Piwik !== 'object') { }()); } +/*!! pluginTrackerHook */ + +(function () { + 'use strict'; + + if (window + && 'object' === typeof window.piwikPluginAsyncInit + && window.piwikPluginAsyncInit.length) { + var i = 0; + for (i; i < window.piwikPluginAsyncInit.length; i++) { + if (typeof window.piwikPluginAsyncInit[i] === 'function') { + window.piwikPluginAsyncInit[i](); + } + } + } + + window.Piwik.addTracker(); + + window.Piwik.trigger('PiwikInitialized', []); + window.Piwik.initialized = true; +}()); + if (window && window.piwikAsyncInit) { window.piwikAsyncInit(); } diff --git a/js/piwik.min.js b/js/piwik.min.js new file mode 100644 index 0000000000..002e4cb757 --- /dev/null +++ b/js/piwik.min.js @@ -0,0 +1,71 @@ +/*!! + * 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 + */ +if(typeof JSON2!=="object"&&typeof window.JSON==="object"&&window.JSON.stringify&&window.JSON.parse){JSON2=window.JSON}else{(function(){var a={}; +/*!! JSON v3.3.2 | http://bestiejs.github.io/json3 | Copyright 2012-2014, Kit Cambridge | http://kit.mit-license.org */ +(function(){var c=typeof define==="function"&&define.amd;var e={"function":true,object:true};var h=e[typeof a]&&a&&!a.nodeType&&a;var i=e[typeof window]&&window||this,b=h&&e[typeof module]&&module&&!module.nodeType&&typeof global=="object"&&global;if(b&&(b.global===b||b.window===b||b.self===b)){i=b}function j(ab,V){ab||(ab=i.Object());V||(V=i.Object());var K=ab.Number||i.Number,R=ab.String||i.String,x=ab.Object||i.Object,S=ab.Date||i.Date,T=ab.SyntaxError||i.SyntaxError,aa=ab.TypeError||i.TypeError,J=ab.Math||i.Math,Y=ab.JSON||i.JSON; +if(typeof Y=="object"&&Y){V.stringify=Y.stringify;V.parse=Y.parse}var n=x.prototype,u=n.toString,r,m,L;var B=new S(-3509827334573292);try{B=B.getUTCFullYear()==-109252&&B.getUTCMonth()===0&&B.getUTCDate()===1&&B.getUTCHours()==10&&B.getUTCMinutes()==37&&B.getUTCSeconds()==6&&B.getUTCMilliseconds()==708}catch(v){}function o(ac){if(o[ac]!==L){return o[ac]}var ad;if(ac=="bug-string-char-index"){ad="a"[0]!="a"}else{if(ac=="json"){ad=o("json-stringify")&&o("json-parse")}else{var ak,ah='{"a":[1,true,false,null,"\\u0000\\b\\n\\f\\r\\t"]}';if(ac=="json-stringify"){var ai=V.stringify,aj=typeof ai=="function"&&B;if(aj){(ak=function(){return 1}).toJSON=ak;try{aj=ai(0)==="0"&&ai(new K())==="0"&&ai(new R())=='""'&&ai(u)===L&&ai(L)===L&&ai()===L&&ai(ak)==="1"&&ai([ak])=="[1]"&&ai([L])=="[null]"&&ai(null)=="null"&&ai([L,u,null])=="[null,null,null]"&&ai({a:[ak,true,false,null,"\x00\b\n\f\r\t"]})==ah&&ai(null,ak)==="1"&&ai([1,2],null,1)=="[\n 1,\n 2\n]"&&ai(new S(-8640000000000000))=='"-271821-04-20T00:00:00.000Z"'&&ai(new S(8640000000000000))=='"+275760-09-13T00:00:00.000Z"'&&ai(new S(-62198755200000))=='"-000001-01-01T00:00:00.000Z"'&&ai(new S(-1))=='"1969-12-31T23:59:59.999Z"' +}catch(ae){aj=false}}ad=aj}if(ac=="json-parse"){var ag=V.parse;if(typeof ag=="function"){try{if(ag("0")===0&&!ag(false)){ak=ag(ah);var af=ak.a.length==5&&ak.a[0]===1;if(af){try{af=!ag('"\t"')}catch(ae){}if(af){try{af=ag("01")!==1}catch(ae){}}if(af){try{af=ag("1.")!==1}catch(ae){}}}}}catch(ae){af=false}}ad=af}}}return o[ac]=!!ad}if(!o("json")){var U="[object Function]",Q="[object Date]",N="[object Number]",O="[object String]",E="[object Array]",A="[object Boolean]";var F=o("bug-string-char-index");if(!B){var s=J.floor;var Z=[0,31,59,90,120,151,181,212,243,273,304,334];var D=function(ac,ad){return Z[ad]+365*(ac-1970)+s((ac-1969+(ad=+(ad>1)))/4)-s((ac-1901+ad)/100)+s((ac-1601+ad)/400)}}if(!(r=n.hasOwnProperty)){r=function(ae){var ac={},ad;if((ac.__proto__=null,ac.__proto__={toString:1},ac).toString!=u){r=function(ah){var ag=this.__proto__,af=ah in (this.__proto__=null,this);this.__proto__=ag;return af}}else{ad=ac.constructor;r=function(ag){var af=(this.constructor||ad).prototype;return ag in this&&!(ag in af&&this[ag]===af[ag]) +}}ac=null;return r.call(this,ae)}}m=function(ae,ah){var af=0,ac,ad,ag;(ac=function(){this.valueOf=0}).prototype.valueOf=0;ad=new ac();for(ag in ad){if(r.call(ad,ag)){af++}}ac=ad=null;if(!af){ad=["valueOf","toString","toLocaleString","propertyIsEnumerable","isPrototypeOf","hasOwnProperty","constructor"];m=function(aj,an){var am=u.call(aj)==U,al,ak;var ai=!am&&typeof aj.constructor!="function"&&e[typeof aj.hasOwnProperty]&&aj.hasOwnProperty||r;for(al in aj){if(!(am&&al=="prototype")&&ai.call(aj,al)){an(al)}}for(ak=ad.length;al=ad[--ak];ai.call(aj,al)&&an(al)){}}}else{if(af==2){m=function(aj,am){var ai={},al=u.call(aj)==U,ak;for(ak in aj){if(!(al&&ak=="prototype")&&!r.call(ai,ak)&&(ai[ak]=1)&&r.call(aj,ak)){am(ak)}}}}else{m=function(aj,am){var al=u.call(aj)==U,ak,ai;for(ak in aj){if(!(al&&ak=="prototype")&&r.call(aj,ak)&&!(ai=ak==="constructor")){am(ak)}}if(ai||r.call(aj,(ak="constructor"))){am(ak)}}}}return m(ae,ah)};if(!o("json-stringify")){var q={92:"\\\\",34:'\\"',8:"\\b",12:"\\f",10:"\\n",13:"\\r",9:"\\t"}; +var I="000000";var t=function(ac,ad){return(I+(ad||0)).slice(-ac)};var z="\\u00";var C=function(ai){var ad='"',ag=0,ah=ai.length,ac=!F||ah>10;var af=ac&&(F?ai.split(""):ai);for(;ag<ah;ag++){var ae=ai.charCodeAt(ag);switch(ae){case 8:case 9:case 10:case 12:case 13:case 34:case 92:ad+=q[ae];break;default:if(ae<32){ad+=z+t(2,ae.toString(16));break}ad+=ac?af[ag]:ai.charAt(ag)}}return ad+'"'};var p=function(ai,aA,ag,al,ax,ac,aj){var at,ae,ap,az,ay,ak,aw,au,aq,an,ar,ad,ah,af,av,ao;try{at=aA[ai]}catch(am){}if(typeof at=="object"&&at){ae=u.call(at);if(ae==Q&&!r.call(at,"toJSON")){if(at>-1/0&&at<1/0){if(D){ay=s(at/86400000);for(ap=s(ay/365.2425)+1970-1;D(ap+1,0)<=ay;ap++){}for(az=s((ay-D(ap,0))/30.42);D(ap,az+1)<=ay;az++){}ay=1+ay-D(ap,az);ak=(at%86400000+86400000)%86400000;aw=s(ak/3600000)%24;au=s(ak/60000)%60;aq=s(ak/1000)%60;an=ak%1000}else{ap=at.getUTCFullYear();az=at.getUTCMonth();ay=at.getUTCDate();aw=at.getUTCHours();au=at.getUTCMinutes();aq=at.getUTCSeconds();an=at.getUTCMilliseconds()}at=(ap<=0||ap>=10000?(ap<0?"-":"+")+t(6,ap<0?-ap:ap):t(4,ap))+"-"+t(2,az+1)+"-"+t(2,ay)+"T"+t(2,aw)+":"+t(2,au)+":"+t(2,aq)+"."+t(3,an)+"Z" +}else{at=null}}else{if(typeof at.toJSON=="function"&&((ae!=N&&ae!=O&&ae!=E)||r.call(at,"toJSON"))){at=at.toJSON(ai)}}}if(ag){at=ag.call(aA,ai,at)}if(at===null){return"null"}ae=u.call(at);if(ae==A){return""+at}else{if(ae==N){return at>-1/0&&at<1/0?""+at:"null"}else{if(ae==O){return C(""+at)}}}if(typeof at=="object"){for(af=aj.length;af--;){if(aj[af]===at){throw aa()}}aj.push(at);ar=[];av=ac;ac+=ax;if(ae==E){for(ah=0,af=at.length;ah<af;ah++){ad=p(ah,at,ag,al,ax,ac,aj);ar.push(ad===L?"null":ad)}ao=ar.length?(ax?"[\n"+ac+ar.join(",\n"+ac)+"\n"+av+"]":("["+ar.join(",")+"]")):"[]"}else{m(al||at,function(aC){var aB=p(aC,at,ag,al,ax,ac,aj);if(aB!==L){ar.push(C(aC)+":"+(ax?" ":"")+aB)}});ao=ar.length?(ax?"{\n"+ac+ar.join(",\n"+ac)+"\n"+av+"}":("{"+ar.join(",")+"}")):"{}"}aj.pop();return ao}};V.stringify=function(ac,ae,af){var ad,al,aj,ai;if(e[typeof ae]&&ae){if((ai=u.call(ae))==U){al=ae}else{if(ai==E){aj={};for(var ah=0,ag=ae.length,ak;ah<ag;ak=ae[ah++],((ai=u.call(ak)),ai==O||ai==N)&&(aj[ak]=1)){}}}}if(af){if((ai=u.call(af))==N){if((af-=af%1)>0){for(ad="",af>10&&(af=10); +ad.length<af;ad+=" "){}}}else{if(ai==O){ad=af.length<=10?af:af.slice(0,10)}}}return p("",(ak={},ak[""]=ac,ak),al,aj,ad,"",[])}}if(!o("json-parse")){var M=R.fromCharCode;var l={92:"\\",34:'"',47:"/",98:"\b",116:"\t",110:"\n",102:"\f",114:"\r"};var G,X;var H=function(){G=X=null;throw T()};var y=function(){var ah=X,af=ah.length,ag,ae,ac,ai,ad;while(G<af){ad=ah.charCodeAt(G);switch(ad){case 9:case 10:case 13:case 32:G++;break;case 123:case 125:case 91:case 93:case 58:case 44:ag=F?ah.charAt(G):ah[G];G++;return ag;case 34:for(ag="@",G++;G<af;){ad=ah.charCodeAt(G);if(ad<32){H()}else{if(ad==92){ad=ah.charCodeAt(++G);switch(ad){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:ag+=l[ad];G++;break;case 117:ae=++G;for(ac=G+4;G<ac;G++){ad=ah.charCodeAt(G);if(!(ad>=48&&ad<=57||ad>=97&&ad<=102||ad>=65&&ad<=70)){H()}}ag+=M("0x"+ah.slice(ae,G));break;default:H()}}else{if(ad==34){break}ad=ah.charCodeAt(G);ae=G;while(ad>=32&&ad!=92&&ad!=34){ad=ah.charCodeAt(++G)}ag+=ah.slice(ae,G)}}}if(ah.charCodeAt(G)==34){G++; +return ag}H();default:ae=G;if(ad==45){ai=true;ad=ah.charCodeAt(++G)}if(ad>=48&&ad<=57){if(ad==48&&((ad=ah.charCodeAt(G+1)),ad>=48&&ad<=57)){H()}ai=false;for(;G<af&&((ad=ah.charCodeAt(G)),ad>=48&&ad<=57);G++){}if(ah.charCodeAt(G)==46){ac=++G;for(;ac<af&&((ad=ah.charCodeAt(ac)),ad>=48&&ad<=57);ac++){}if(ac==G){H()}G=ac}ad=ah.charCodeAt(G);if(ad==101||ad==69){ad=ah.charCodeAt(++G);if(ad==43||ad==45){G++}for(ac=G;ac<af&&((ad=ah.charCodeAt(ac)),ad>=48&&ad<=57);ac++){}if(ac==G){H()}G=ac}return +ah.slice(ae,G)}if(ai){H()}if(ah.slice(G,G+4)=="true"){G+=4;return true}else{if(ah.slice(G,G+5)=="false"){G+=5;return false}else{if(ah.slice(G,G+4)=="null"){G+=4;return null}}}H()}}return"$"};var W=function(ad){var ac,ae;if(ad=="$"){H()}if(typeof ad=="string"){if((F?ad.charAt(0):ad[0])=="@"){return ad.slice(1)}if(ad=="["){ac=[];for(;;ae||(ae=true)){ad=y();if(ad=="]"){break}if(ae){if(ad==","){ad=y();if(ad=="]"){H()}}else{H()}}if(ad==","){H()}ac.push(W(ad))}return ac}else{if(ad=="{"){ac={};for(;;ae||(ae=true)){ad=y(); +if(ad=="}"){break}if(ae){if(ad==","){ad=y();if(ad=="}"){H()}}else{H()}}if(ad==","||typeof ad!="string"||(F?ad.charAt(0):ad[0])!="@"||y()!=":"){H()}ac[ad.slice(1)]=W(y())}return ac}}H()}return ad};var P=function(ae,ad,af){var ac=w(ae,ad,af);if(ac===L){delete ae[ad]}else{ae[ad]=ac}};var w=function(af,ae,ag){var ad=af[ae],ac;if(typeof ad=="object"&&ad){if(u.call(ad)==E){for(ac=ad.length;ac--;){P(ad,ac,ag)}}else{m(ad,function(ah){P(ad,ah,ag)})}}return ag.call(af,ae,ad)};V.parse=function(ae,af){var ac,ad;G=0;X=""+ae;ac=W(y());if(y()!="$"){H()}G=X=null;return af&&u.call(af)==U?w((ad={},ad[""]=ac,ad),"",af):ac}}}V.runInContext=j;return V}if(h&&!c){j(i,h)}else{var f=i.JSON,k=i.JSON3,d=false;var g=j(i,(i.JSON3={noConflict:function(){if(!d){d=true;i.JSON=f;i.JSON3=k;f=k=null}return g}}));i.JSON={parse:g.parse,stringify:g.stringify}}if(c){define(function(){return g})}}).call(this);JSON2=a})()}if(typeof _paq!=="object"){_paq=[]}if(typeof window.Piwik!=="object"){window.Piwik=(function(){var l,a={},r={},y=document,f=navigator,P=screen,L=window,g=L.performance||L.mozPerformance||L.msPerformance||L.webkitPerformance,n=L.encodeURIComponent,K=L.decodeURIComponent,i=unescape,A=[],x,d; +function k(ac){try{return K(ac)}catch(ad){return unescape(ac)}}function B(ad){var ac=typeof ad;return ac!=="undefined"}function t(ac){return typeof ac==="function"}function O(ac){return typeof ac==="object"}function q(ac){return typeof ac==="string"||ac instanceof String}function u(ad){if(!ad){return true}var ac;var ae=true;for(ac in ad){if(Object.prototype.hasOwnProperty.call(ad,ac)){ae=false}}return ae}function Y(ac){if(console!==undefined&&console&&console.error){console.error(ac)}}function V(){var ag,af,ai,ac;for(ag=0;ag<arguments.length;ag+=1){ac=arguments[ag];ai=ac.shift();for(af=0;af<A.length;af++){if(q(ai)){var ad=A[af];var ah;var ae=ai.indexOf("::")>0;if(ae){ah=ai.split("::");ad=ah[0];ai=ah[1];if("object"===typeof d[ad]&&"function"===typeof d[ad][ai]){d[ad][ai].apply(d[ad],ac)}return}var aj=ai.indexOf(".")>0;if(aj){ah=ai.split(".");ad=ad[ah[0]];ai=ah[1]}if(ad[ai]){ad[ai].apply(ad,ac)}else{var ak="The method '"+ai+'\' was not found in "_paq" variable. Please have a look at the Piwik tracker documentation: http://developer.piwik.org/api-reference/tracking-javascript'; +Y(ak);if(!aj){throw new TypeError(ak)}}if(ai==="addTracker"){break}if(ai==="setTrackerUrl"||ai==="setSiteId"){break}}else{ai.apply(A[af],ac)}}}}function aa(af,ae,ad,ac){if(af.addEventListener){af.addEventListener(ae,ad,ac);return true}if(af.attachEvent){return af.attachEvent("on"+ae,ad)}af["on"+ae]=ad}function S(ad,ah){var ac="",af,ae,ag;for(af in a){if(Object.prototype.hasOwnProperty.call(a,af)){ae=a[af][ad];if(t(ae)){ag=ae(ah);if(ag){ac+=ag}}}}return ac}function W(){var ac;S("unload");if(l){do{ac=new Date()}while(ac.getTimeAlias()<l)}}function j(ae,ad){var ac=y.createElement("script");ac.type="text/javascript";ac.src=ae;if(ac.readyState){ac.onreadystatechange=function(){var af=this.readyState;if(af==="loaded"||af==="complete"){ac.onreadystatechange=null;ad()}}}else{ac.onload=ad}y.getElementsByTagName("head")[0].appendChild(ac)}function C(){var ac="";try{ac=L.top.document.referrer}catch(ae){if(L.parent){try{ac=L.parent.document.referrer}catch(ad){ac=""}}}if(ac===""){ac=y.referrer}return ac +}function m(ac){var ae=new RegExp("^([a-z]+):"),ad=ae.exec(ac);return ad?ad[1]:null}function c(ac){var ae=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),ad=ae.exec(ac);return ad?ad[1]:ac}function N(ae,ad){var ac="[\\?&#]"+ad+"=([^&#]*)";var ag=new RegExp(ac);var af=ag.exec(ae);return af?K(af[1]):""}function w(ac){return unescape(n(ac))}function Z(ar){var ae=function(ay,ax){return(ay<<ax)|(ay>>>(32-ax))},at=function(aA){var ay="",az,ax;for(az=7;az>=0;az--){ax=(aA>>>(az*4))&15;ay+=ax.toString(16)}return ay},ah,av,au,ad=[],al=1732584193,aj=4023233417,ai=2562383102,ag=271733878,af=3285377520,aq,ap,ao,an,am,aw,ac,ak=[];ar=w(ar);ac=ar.length;for(av=0;av<ac-3;av+=4){au=ar.charCodeAt(av)<<24|ar.charCodeAt(av+1)<<16|ar.charCodeAt(av+2)<<8|ar.charCodeAt(av+3);ak.push(au)}switch(ac&3){case 0:av=2147483648;break;case 1:av=ar.charCodeAt(ac-1)<<24|8388608;break;case 2:av=ar.charCodeAt(ac-2)<<24|ar.charCodeAt(ac-1)<<16|32768;break;case 3:av=ar.charCodeAt(ac-3)<<24|ar.charCodeAt(ac-2)<<16|ar.charCodeAt(ac-1)<<8|128; +break}ak.push(av);while((ak.length&15)!==14){ak.push(0)}ak.push(ac>>>29);ak.push((ac<<3)&4294967295);for(ah=0;ah<ak.length;ah+=16){for(av=0;av<16;av++){ad[av]=ak[ah+av]}for(av=16;av<=79;av++){ad[av]=ae(ad[av-3]^ad[av-8]^ad[av-14]^ad[av-16],1)}aq=al;ap=aj;ao=ai;an=ag;am=af;for(av=0;av<=19;av++){aw=(ae(aq,5)+((ap&ao)|(~ap&an))+am+ad[av]+1518500249)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}for(av=20;av<=39;av++){aw=(ae(aq,5)+(ap^ao^an)+am+ad[av]+1859775393)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}for(av=40;av<=59;av++){aw=(ae(aq,5)+((ap&ao)|(ap&an)|(ao&an))+am+ad[av]+2400959708)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}for(av=60;av<=79;av++){aw=(ae(aq,5)+(ap^ao^an)+am+ad[av]+3395469782)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}al=(al+aq)&4294967295;aj=(aj+ap)&4294967295;ai=(ai+ao)&4294967295;ag=(ag+an)&4294967295;af=(af+am)&4294967295}aw=at(al)+at(aj)+at(ai)+at(ag)+at(af);return aw.toLowerCase()}function R(ae,ac,ad){if(!ae){ae=""}if(!ac){ac=""}if(ae==="translate.googleusercontent.com"){if(ad===""){ad=ac +}ac=N(ac,"u");ae=c(ac)}else{if(ae==="cc.bingj.com"||ae==="webcache.googleusercontent.com"||ae.slice(0,5)==="74.6."){ac=y.links[0].href;ae=c(ac)}}return[ae,ac,ad]}function D(ad){var ac=ad.length;if(ad.charAt(--ac)==="."){ad=ad.slice(0,ac)}if(ad.slice(0,2)==="*."){ad=ad.slice(1)}if(ad.indexOf("/")!==-1){ad=ad.substr(0,ad.indexOf("/"))}return ad}function ab(ad){ad=ad&&ad.text?ad.text:ad;if(!q(ad)){var ac=y.getElementsByTagName("title");if(ac&&B(ac[0])){ad=ac[0].text}}return ad}function H(ac){if(!ac){return[]}if(!B(ac.children)&&B(ac.childNodes)){return ac.children}if(B(ac.children)){return ac.children}return[]}function M(ad,ac){if(!ad||!ac){return false}if(ad.contains){return ad.contains(ac)}if(ad===ac){return true}if(ad.compareDocumentPosition){return !!(ad.compareDocumentPosition(ac)&16)}return false}function E(ae,af){if(ae&&ae.indexOf){return ae.indexOf(af)}if(!B(ae)||ae===null){return -1}if(!ae.length){return -1}var ac=ae.length;if(ac===0){return -1}var ad=0;while(ad<ac){if(ae[ad]===af){return ad +}ad++}return -1}function X(ad,ac){ad=String(ad);return ad.lastIndexOf(ac,0)===0}function J(ad,ac){ad=String(ad);return ad.indexOf(ac,ad.length-ac.length)!==-1}function s(ad,ac){ad=String(ad);return ad.indexOf(ac)!==-1}function e(ad,ac){ad=String(ad);return ad.substr(0,ad.length-ac)}function h(ae){if(!ae){return false}function ac(ag,ah){if(L.getComputedStyle){return y.defaultView.getComputedStyle(ag,null)[ah]}if(ag.currentStyle){return ag.currentStyle[ah]}}function af(ag){ag=ag.parentNode;while(ag){if(ag===y){return true}ag=ag.parentNode}return false}function ad(ai,ao,ag,al,aj,am,ak){var ah=ai.parentNode,an=1;if(!af(ai)){return false}if(9===ah.nodeType){return true}if("0"===ac(ai,"opacity")||"none"===ac(ai,"display")||"hidden"===ac(ai,"visibility")){return false}if(!B(ao)||!B(ag)||!B(al)||!B(aj)||!B(am)||!B(ak)){ao=ai.offsetTop;aj=ai.offsetLeft;al=ao+ai.offsetHeight;ag=aj+ai.offsetWidth;am=ai.offsetWidth;ak=ai.offsetHeight}if(ae===ai&&(0===ak||0===am)&&"hidden"===ac(ai,"overflow")){return false +}if(ah){if(("hidden"===ac(ah,"overflow")||"scroll"===ac(ah,"overflow"))){if(aj+an>ah.offsetWidth+ah.scrollLeft||aj+am-an<ah.scrollLeft||ao+an>ah.offsetHeight+ah.scrollTop||ao+ak-an<ah.scrollTop){return false}}if(ai.offsetParent===ah){aj+=ah.offsetLeft;ao+=ah.offsetTop}return ad(ah,ao,ag,al,aj,am,ak)}return true}return ad(ae)}var U={htmlCollectionToArray:function(ae){var ac=[],ad;if(!ae||!ae.length){return ac}for(ad=0;ad<ae.length;ad++){ac.push(ae[ad])}return ac},find:function(ac){if(!document.querySelectorAll||!ac){return[]}var ad=document.querySelectorAll(ac);return this.htmlCollectionToArray(ad)},findMultiple:function(ae){if(!ae||!ae.length){return[]}var ad,af;var ac=[];for(ad=0;ad<ae.length;ad++){af=this.find(ae[ad]);ac=ac.concat(af)}ac=this.makeNodesUnique(ac);return ac},findNodesByTagName:function(ad,ac){if(!ad||!ac||!ad.getElementsByTagName){return[]}var ae=ad.getElementsByTagName(ac);return this.htmlCollectionToArray(ae)},makeNodesUnique:function(ac){var ah=[].concat(ac);ac.sort(function(aj,ai){if(aj===ai){return 0 +}var al=E(ah,aj);var ak=E(ah,ai);if(al===ak){return 0}return al>ak?-1:1});if(ac.length<=1){return ac}var ad=0;var af=0;var ag=[];var ae;ae=ac[ad++];while(ae){if(ae===ac[ad]){af=ag.push(ad)}ae=ac[ad++]||null}while(af--){ac.splice(ag[af],1)}return ac},getAttributeValueFromNode:function(ag,ae){if(!this.hasNodeAttribute(ag,ae)){return}if(ag&&ag.getAttribute){return ag.getAttribute(ae)}if(!ag||!ag.attributes){return}var af=(typeof ag.attributes[ae]);if("undefined"===af){return}if(ag.attributes[ae].value){return ag.attributes[ae].value}if(ag.attributes[ae].nodeValue){return ag.attributes[ae].nodeValue}var ad;var ac=ag.attributes;if(!ac){return}for(ad=0;ad<ac.length;ad++){if(ac[ad].nodeName===ae){return ac[ad].nodeValue}}return null},hasNodeAttributeWithValue:function(ad,ac){var ae=this.getAttributeValueFromNode(ad,ac);return !!ae},hasNodeAttribute:function(ae,ac){if(ae&&ae.hasAttribute){return ae.hasAttribute(ac)}if(ae&&ae.attributes){var ad=(typeof ae.attributes[ac]);return"undefined"!==ad}return false +},hasNodeCssClass:function(ae,ac){if(ae&&ac&&ae.className){var ad=typeof ae.className==="string"?ae.className.split(" "):[];if(-1!==E(ad,ac)){return true}}return false},findNodesHavingAttribute:function(ag,ae,ac){if(!ac){ac=[]}if(!ag||!ae){return ac}var af=H(ag);if(!af||!af.length){return ac}var ad,ah;for(ad=0;ad<af.length;ad++){ah=af[ad];if(this.hasNodeAttribute(ah,ae)){ac.push(ah)}ac=this.findNodesHavingAttribute(ah,ae,ac)}return ac},findFirstNodeHavingAttribute:function(ae,ad){if(!ae||!ad){return}if(this.hasNodeAttribute(ae,ad)){return ae}var ac=this.findNodesHavingAttribute(ae,ad);if(ac&&ac.length){return ac[0]}},findFirstNodeHavingAttributeWithValue:function(af,ae){if(!af||!ae){return}if(this.hasNodeAttributeWithValue(af,ae)){return af}var ac=this.findNodesHavingAttribute(af,ae);if(!ac||!ac.length){return}var ad;for(ad=0;ad<ac.length;ad++){if(this.getAttributeValueFromNode(ac[ad],ae)){return ac[ad]}}},findNodesHavingCssClass:function(ag,af,ac){if(!ac){ac=[]}if(!ag||!af){return ac}if(ag.getElementsByClassName){var ah=ag.getElementsByClassName(af); +return this.htmlCollectionToArray(ah)}var ae=H(ag);if(!ae||!ae.length){return[]}var ad,ai;for(ad=0;ad<ae.length;ad++){ai=ae[ad];if(this.hasNodeCssClass(ai,af)){ac.push(ai)}ac=this.findNodesHavingCssClass(ai,af,ac)}return ac},findFirstNodeHavingClass:function(ae,ad){if(!ae||!ad){return}if(this.hasNodeCssClass(ae,ad)){return ae}var ac=this.findNodesHavingCssClass(ae,ad);if(ac&&ac.length){return ac[0]}},isLinkElement:function(ad){if(!ad){return false}var ac=String(ad.nodeName).toLowerCase();var af=["a","area"];var ae=E(af,ac);return ae!==-1},setAnyAttribute:function(ad,ac,ae){if(!ad||!ac){return}if(ad.setAttribute){ad.setAttribute(ac,ae)}else{ad[ac]=ae}}};var p={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 ad="."+this.CONTENT_CLASS; +var ac="["+this.CONTENT_ATTR+"]";var ae=U.findMultiple([ad,ac]);return ae},findContentNodesWithinNode:function(af){if(!af){return[]}var ad=U.findNodesHavingCssClass(af,this.CONTENT_CLASS);var ac=U.findNodesHavingAttribute(af,this.CONTENT_ATTR);if(ac&&ac.length){var ae;for(ae=0;ae<ac.length;ae++){ad.push(ac[ae])}}if(U.hasNodeAttribute(af,this.CONTENT_ATTR)){ad.push(af)}else{if(U.hasNodeCssClass(af,this.CONTENT_CLASS)){ad.push(af)}}ad=U.makeNodesUnique(ad);return ad},findParentContentNode:function(ad){if(!ad){return}var ae=ad;var ac=0;while(ae&&ae!==y&&ae.parentNode){if(U.hasNodeAttribute(ae,this.CONTENT_ATTR)){return ae}if(U.hasNodeCssClass(ae,this.CONTENT_CLASS)){return ae}ae=ae.parentNode;if(ac>1000){break}ac++}},findPieceNode:function(ad){var ac;ac=U.findFirstNodeHavingAttribute(ad,this.CONTENT_PIECE_ATTR);if(!ac){ac=U.findFirstNodeHavingClass(ad,this.CONTENT_PIECE_CLASS)}if(ac){return ac}return ad},findTargetNodeNoDefault:function(ac){if(!ac){return}var ad=U.findFirstNodeHavingAttributeWithValue(ac,this.CONTENT_TARGET_ATTR); +if(ad){return ad}ad=U.findFirstNodeHavingAttribute(ac,this.CONTENT_TARGET_ATTR);if(ad){return ad}ad=U.findFirstNodeHavingClass(ac,this.CONTENT_TARGET_CLASS);if(ad){return ad}},findTargetNode:function(ac){var ad=this.findTargetNodeNoDefault(ac);if(ad){return ad}return ac},findContentName:function(ad){if(!ad){return}var ag=U.findFirstNodeHavingAttributeWithValue(ad,this.CONTENT_NAME_ATTR);if(ag){return U.getAttributeValueFromNode(ag,this.CONTENT_NAME_ATTR)}var ac=this.findContentPiece(ad);if(ac){return this.removeDomainIfIsInLink(ac)}if(U.hasNodeAttributeWithValue(ad,"title")){return U.getAttributeValueFromNode(ad,"title")}var ae=this.findPieceNode(ad);if(U.hasNodeAttributeWithValue(ae,"title")){return U.getAttributeValueFromNode(ae,"title")}var af=this.findTargetNode(ad);if(U.hasNodeAttributeWithValue(af,"title")){return U.getAttributeValueFromNode(af,"title")}},findContentPiece:function(ad){if(!ad){return}var af=U.findFirstNodeHavingAttributeWithValue(ad,this.CONTENT_PIECE_ATTR);if(af){return U.getAttributeValueFromNode(af,this.CONTENT_PIECE_ATTR) +}var ac=this.findPieceNode(ad);var ae=this.findMediaUrlInNode(ac);if(ae){return this.toAbsoluteUrl(ae)}},findContentTarget:function(ae){if(!ae){return}var af=this.findTargetNode(ae);if(U.hasNodeAttributeWithValue(af,this.CONTENT_TARGET_ATTR)){return U.getAttributeValueFromNode(af,this.CONTENT_TARGET_ATTR)}var ad;if(U.hasNodeAttributeWithValue(af,"href")){ad=U.getAttributeValueFromNode(af,"href");return this.toAbsoluteUrl(ad)}var ac=this.findPieceNode(ae);if(U.hasNodeAttributeWithValue(ac,"href")){ad=U.getAttributeValueFromNode(ac,"href");return this.toAbsoluteUrl(ad)}},isSameDomain:function(ac){if(!ac||!ac.indexOf){return false}if(0===ac.indexOf(this.getLocation().origin)){return true}var ad=ac.indexOf(this.getLocation().host);if(8>=ad&&0<=ad){return true}return false},removeDomainIfIsInLink:function(ae){var ad="^https?://[^/]+";var ac="^.*//[^/]+";if(ae&&ae.search&&-1!==ae.search(new RegExp(ad))&&this.isSameDomain(ae)){ae=ae.replace(new RegExp(ac),"");if(!ae){ae="/"}}return ae},findMediaUrlInNode:function(ag){if(!ag){return +}var ae=["img","embed","video","audio"];var ac=ag.nodeName.toLowerCase();if(-1!==E(ae,ac)&&U.findFirstNodeHavingAttributeWithValue(ag,"src")){var af=U.findFirstNodeHavingAttributeWithValue(ag,"src");return U.getAttributeValueFromNode(af,"src")}if(ac==="object"&&U.hasNodeAttributeWithValue(ag,"data")){return U.getAttributeValueFromNode(ag,"data")}if(ac==="object"){var ah=U.findNodesByTagName(ag,"param");if(ah&&ah.length){var ad;for(ad=0;ad<ah.length;ad++){if("movie"===U.getAttributeValueFromNode(ah[ad],"name")&&U.hasNodeAttributeWithValue(ah[ad],"value")){return U.getAttributeValueFromNode(ah[ad],"value")}}}var ai=U.findNodesByTagName(ag,"embed");if(ai&&ai.length){return this.findMediaUrlInNode(ai[0])}}},trim:function(ac){if(ac&&String(ac)===ac){return ac.replace(/^\s+|\s+$/g,"")}return ac},isOrWasNodeInViewport:function(ah){if(!ah||!ah.getBoundingClientRect||ah.nodeType!==1){return true}var ag=ah.getBoundingClientRect();var af=y.documentElement||{};var ae=ag.top<0;if(ae&&ah.offsetTop){ae=(ah.offsetTop+ag.height)>0 +}var ad=af.clientWidth;if(L.innerWidth&&ad>L.innerWidth){ad=L.innerWidth}var ac=af.clientHeight;if(L.innerHeight&&ac>L.innerHeight){ac=L.innerHeight}return((ag.bottom>0||ae)&&ag.right>0&&ag.left<ad&&((ag.top<ac)||ae))},isNodeVisible:function(ad){var ac=h(ad);var ae=this.isOrWasNodeInViewport(ad);return ac&&ae},buildInteractionRequestParams:function(ac,ad,ae,af){var ag="";if(ac){ag+="c_i="+n(ac)}if(ad){if(ag){ag+="&"}ag+="c_n="+n(ad)}if(ae){if(ag){ag+="&"}ag+="c_p="+n(ae)}if(af){if(ag){ag+="&"}ag+="c_t="+n(af)}return ag},buildImpressionRequestParams:function(ac,ad,ae){var af="c_n="+n(ac)+"&c_p="+n(ad);if(ae){af+="&c_t="+n(ae)}return af},buildContentBlock:function(ae){if(!ae){return}var ac=this.findContentName(ae);var ad=this.findContentPiece(ae);var af=this.findContentTarget(ae);ac=this.trim(ac);ad=this.trim(ad);af=this.trim(af);return{name:ac||"Unknown",piece:ad||"Unknown",target:af||""}},collectContent:function(af){if(!af||!af.length){return[]}var ae=[];var ac,ad;for(ac=0;ac<af.length; +ac++){ad=this.buildContentBlock(af[ac]);if(B(ad)){ae.push(ad)}}return ae},setLocation:function(ac){this.location=ac},getLocation:function(){var ac=this.location||L.location;if(!ac.origin){ac.origin=ac.protocol+"//"+ac.hostname+(ac.port?":"+ac.port:"")}return ac},toAbsoluteUrl:function(ad){if((!ad||String(ad)!==ad)&&ad!==""){return ad}if(""===ad){return this.getLocation().href}if(ad.search(/^\/\//)!==-1){return this.getLocation().protocol+ad}if(ad.search(/:\/\//)!==-1){return ad}if(0===ad.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+ad}if(0===ad.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+ad}if(0===ad.search("^[a-zA-Z]{2,11}:")){return ad}if(ad.search(/^\//)!==-1){return this.getLocation().origin+ad}var ac="(.*/)";var ae=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(ac))[0];return ae+ad},isUrlToCurrentDomain:function(ad){var ae=this.toAbsoluteUrl(ad);if(!ae){return false}var ac=this.getLocation().origin; +if(ac===ae){return true}if(0===String(ae).indexOf(ac)){if(":"===String(ae).substr(ac.length,1)){return false}return true}return false},setHrefAttribute:function(ad,ac){if(!ad||!ac){return}U.setAnyAttribute(ad,"href",ac)},shouldIgnoreInteraction:function(ae){var ad=U.hasNodeAttribute(ae,this.CONTENT_IGNOREINTERACTION_ATTR);var ac=U.hasNodeCssClass(ae,this.CONTENT_IGNOREINTERACTION_CLASS);return ad||ac}};function G(ad,ag){if(ag){return ag}if(s(ad,"?")){var af=ad.indexOf("?");ad=ad.slice(0,af)}if(J(ad,"piwik.php")){ad=e(ad,"piwik.php".length)}else{if(J(ad,".php")){var ac=ad.lastIndexOf("/");var ae=1;ad=ad.slice(0,ac+ae)}}if(J(ad,"/js/")){ad=e(ad,"js/".length)}return ad}function F(ai){var ak="Piwik_Overlay";var ad=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$");var ae=ad.exec(y.referrer);if(ae){var ag=ae[1];if(ag!==String(ai)){return false}var ah=ae[2],ac=ae[3],af=ae[4];if(!af){af=""}else{if(af.indexOf("&segment=")===0){af=af.substr("&segment=".length) +}}L.name=ak+"###"+ah+"###"+ac+"###"+af}var aj=L.name.split("###");return aj.length===4&&aj[0]===ak}function Q(ad,aj,af){var ai=L.name.split("###"),ah=ai[1],ac=ai[2],ag=ai[3],ae=G(ad,aj);j(ae+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(ae,af,ah,ac,ag)})}function o(){var ae;try{ae=L.frameElement}catch(ad){return true}if(B(ae)){return(ae&&String(ae.nodeName).toLowerCase()==="iframe")?true:false}try{return L.self!==L.top}catch(ac){return true}}function I(bK,bE){var bA=R(y.domain,L.location.href,C()),cj=D(bA[0]),bk=k(bA[1]),aZ=k(bA[2]),ch=false,bO="GET",cv=bO,aq="application/x-www-form-urlencoded; charset=UTF-8",b0=aq,am=bK||"",bf="",cn="",bC=bE||"",a8="",bl="",aK,aV="",cs=["7z","aac","apk","arc","arj","asf","asx","avi","azw3","bin","csv","deb","dmg","doc","docx","epub","exe","flv","gif","gz","gzip","hqx","ibooks","jar","jpg","jpeg","js","mobi","mp2","mp3","mp4","mpg","mpeg","mov","movie","msi","msp","odb","odf","odg","ods","odt","ogg","ogv","pdf","phps","png","ppt","pptx","qt","qtm","ra","ram","rar","rpm","sea","sit","tar","tbz","tbz2","bz","bz2","tgz","torrent","txt","wav","wma","wmv","wpd","xls","xlsx","xml","z","zip"],ai=[cj],a9=[],bi=[],aN=[],bg=500,ca,aL,bo,bm,ac,bW=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],be=["pk_kwd","piwik_kwd","utm_term"],aW="_pk_",cl,a1,aX=false,cf,aT,a5,cb=33955200000,bU=1800000,cr=15768000000,aI=true,bS=0,bn=false,ax=false,bH,bs={},bR={},aY={},a4=200,co={},ct={},bG=[],bL=false,b4=false,ad=false,cu=false,av=false,aS=o(),cm=null,bI,ay,ba,bD=Z,a0; +try{aV=y.title}catch(b2){aV=""}function cy(cI,cF,cE,cH,cD,cG){if(aX){return}var cC;if(cE){cC=new Date();cC.setTime(cC.getTime()+cE)}y.cookie=cI+"="+n(cF)+(cE?";expires="+cC.toGMTString():"")+";path="+(cH||"/")+(cD?";domain="+cD:"")+(cG?";secure":"")}function al(cE){if(aX){return 0}var cC=new RegExp("(^|;)[ ]*"+cE+"=([^;]*)"),cD=cC.exec(y.cookie);return cD?K(cD[2]):0}function by(cC){var cD;if(bm){cD=new RegExp("#.*");return cC.replace(cD,"")}return cC}function br(cE,cC){var cF=m(cC),cD;if(cF){return cC}if(cC.slice(0,1)==="/"){return m(cE)+"://"+c(cE)+cC}cE=by(cE);cD=cE.indexOf("?");if(cD>=0){cE=cE.slice(0,cD)}cD=cE.lastIndexOf("/");if(cD!==cE.length-1){cE=cE.slice(0,cD+1)}return cE+cC}function b8(cE,cC){var cD;cE=String(cE).toLowerCase();cC=String(cC).toLowerCase();if(cE===cC){return true}if(cC.slice(0,1)==="."){if(cE===cC.slice(1)){return true}cD=cE.length-cC.length;if((cD>0)&&(cE.slice(cD)===cC)){return true}}return false}function bQ(cC){var cD=document.createElement("a");if(cC.indexOf("//")!==0&&cC.indexOf("http")!==0){if(cC.indexOf("*")===0){cC=cC.substr(1) +}if(cC.indexOf(".")===0){cC=cC.substr(1)}cC="http://"+cC}cD.href=p.toAbsoluteUrl(cC);if(cD.pathname){return cD.pathname}return""}function aJ(cD,cC){if(!X(cC,"/")){cC="/"+cC}if(!X(cD,"/")){cD="/"+cD}var cE=(cC==="/"||cC==="/*");if(cE){return true}if(cD===cC){return true}cC=String(cC).toLowerCase();cD=String(cD).toLowerCase();if(J(cC,"*")){cC=cC.slice(0,-1);cE=(!cC||cC==="/");if(cE){return true}if(cD===cC){return true}return cD.indexOf(cC)===0}if(!J(cD,"/")){cD+="/"}if(!J(cC,"/")){cC+="/"}return cD.indexOf(cC)===0}function af(cG,cI){var cD,cC,cE,cF,cH;for(cD=0;cD<ai.length;cD++){cF=D(ai[cD]);cH=bQ(ai[cD]);if(b8(cG,cF)&&aJ(cI,cH)){return true}}return false}function aC(cF){var cD,cC,cE;for(cD=0;cD<ai.length;cD++){cC=D(ai[cD].toLowerCase());if(cF===cC){return true}if(cC.slice(0,1)==="."){if(cF===cC.slice(1)){return true}cE=cF.length-cC.length;if((cE>0)&&(cF.slice(cE)===cC)){return true}}}return false}function bV(cC,cE){var cD=new Image(1,1);cD.onload=function(){x=0;if(typeof cE==="function"){cE() +}};cC=cC.replace("send_image=0","send_image=1");cD.src=am+(am.indexOf("?")<0?"?":"&")+cC}function cq(cD,cG,cC){if(!B(cC)||null===cC){cC=true}try{var cF=L.XMLHttpRequest?new L.XMLHttpRequest():L.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cF.open("POST",am,true);cF.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cC){bV(cD,cG)}else{if(this.readyState===4&&(typeof cG==="function")){cG()}}};cF.setRequestHeader("Content-Type",b0);cF.send(cD)}catch(cE){if(cC){bV(cD,cG)}}}function bM(cD){var cC=new Date();var cE=cC.getTime()+cD;if(!l||cE>l){l=cE}}function bT(cC){if(bI||!aL){return}bI=setTimeout(function cD(){bI=null;if(!aS){aS=(!y.hasFocus||y.hasFocus())}if(!aS){bT(aL);return}if(bo()){return}var cE=new Date(),cF=aL-(cE.getTime()-cm);cF=Math.min(aL,cF);bT(cF)},cC||aL)}function bh(){if(!bI){return}clearTimeout(bI);bI=null}function aP(){aS=true;if(bo()){return}bT()}function aj(){bh()}function cA(){if(av||!aL){return}av=true;aa(L,"focus",aP); +aa(L,"blur",aj);bT()}function b5(cG){var cD=new Date();var cC=cD.getTime();cm=cC;if(b4&&cC<b4){var cE=b4-cC;setTimeout(cG,cE);bM(cE+50);b4+=50;return}if(b4===false){var cF=800;b4=cC+cF}cG()}function bd(cD,cC,cE){if(!cf&&cD){b5(function(){if(cv==="POST"){cq(cD,cE)}else{bV(cD,cE)}bM(cC)})}if(!av){cA()}else{bT()}}function bP(cC){if(cf){return false}return(cC&&cC.length)}function cz(cE,cC){if(!bP(cE)){return}var cD='{"requests":["?'+cE.join('","?')+'"]}';b5(function(){cq(cD,null,false);bM(cC)})}function aA(cC){return aW+cC+"."+bC+"."+a0}function bB(){if(aX){return"0"}if(!B(f.cookieEnabled)){var cC=aA("testcookie");cy(cC,"1");return al(cC)==="1"?"1":"0"}return f.cookieEnabled?"1":"0"}function aU(){a0=bD((cl||cj)+(a1||"/")).slice(0,4)}function bt(){var cD=aA("cvar"),cC=al(cD);if(cC.length){cC=JSON2.parse(cC);if(O(cC)){return cC}}return{}}function b6(){if(ax===false){ax=bt()}}function cg(){return bD((f.userAgent||"")+(f.platform||"")+JSON2.stringify(ct)+(new Date()).getTime()+Math.random()).slice(0,16) +}function cd(){var cE=new Date(),cC=Math.round(cE.getTime()/1000),cD=aA("id"),cH=al(cD),cG,cF;if(cH){cG=cH.split(".");cG.unshift("0");if(bl.length){cG[1]=bl}return cG}if(bl.length){cF=bl}else{if("0"===bB()){cF=""}else{cF=cg()}}cG=["1",cF,cC,0,cC,"",""];return cG}function aE(){var cJ=cd(),cF=cJ[0],cG=cJ[1],cD=cJ[2],cC=cJ[3],cH=cJ[4],cE=cJ[5];if(!B(cJ[6])){cJ[6]=""}var cI=cJ[6];return{newVisitor:cF,uuid:cG,createTs:cD,visitCount:cC,currentVisitTs:cH,lastVisitTs:cE,lastEcommerceOrderTs:cI}}function ap(){var cF=new Date(),cD=cF.getTime(),cG=aE().createTs;var cC=parseInt(cG,10);var cE=(cC*1000)+cb-cD;return cE}function at(cC){if(!bC){return}var cE=new Date(),cD=Math.round(cE.getTime()/1000);if(!B(cC)){cC=aE()}var cF=cC.uuid+"."+cC.createTs+"."+cC.visitCount+"."+cD+"."+cC.lastVisitTs+"."+cC.lastEcommerceOrderTs;cy(aA("id"),cF,ap(),a1,cl)}function bj(){var cC=al(aA("ref"));if(cC.length){try{cC=JSON2.parse(cC);if(O(cC)){return cC}}catch(cD){}}return["","",0,""]}function bu(cE,cD,cC){cy(cE,"",-86400,cD,cC) +}function a6(cD){var cC="testvalue";cy("test",cC,10000,null,cD);if(al("test")===cC){bu("test",null,cD);return true}return false}function an(){var cE=aX;aX=false;var cC=["id","ses","cvar","ref"];var cD,cF;for(cD=0;cD<cC.length;cD++){cF=aA(cC[cD]);if(0!==al(cF)){bu(cF,a1,cl)}}aX=cE}function bz(cC){bC=cC;at()}function cB(cG){if(!cG||!O(cG)){return}var cF=[];var cE;for(cE in cG){if(Object.prototype.hasOwnProperty.call(cG,cE)){cF.push(cE)}}var cH={};cF.sort();var cC=cF.length;var cD;for(cD=0;cD<cC;cD++){cH[cF[cD]]=cG[cF[cD]]}return cH}function bJ(){cy(aA("ses"),"*",bU,a1,cl)}function bX(cE,cZ,c0,cF){var cY,cD=new Date(),cM=Math.round(cD.getTime()/1000),cJ,cX,cG=1024,c5,cN,cV=ax,cH=aA("ses"),cT=aA("ref"),cQ=aA("cvar"),cR=al(cH),cW=bj(),c2=aK||bk,cK,cC;if(aX){an()}if(cf){return""}var cS=aE();if(!B(cF)){cF=""}var cP=y.characterSet||y.charset;if(!cP||cP.toLowerCase()==="utf-8"){cP=null}cK=cW[0];cC=cW[1];cJ=cW[2];cX=cW[3];if(!cR){var c1=bU/1000;if(!cS.lastVisitTs||(cM-cS.lastVisitTs)>c1){cS.visitCount++; +cS.lastVisitTs=cS.currentVisitTs}if(!a5||!cK.length){for(cY in bW){if(Object.prototype.hasOwnProperty.call(bW,cY)){cK=N(c2,bW[cY]);if(cK.length){break}}}for(cY in be){if(Object.prototype.hasOwnProperty.call(be,cY)){cC=N(c2,be[cY]);if(cC.length){break}}}}c5=c(aZ);cN=cX.length?c(cX):"";if(c5.length&&!aC(c5)&&(!a5||!cN.length||aC(cN))){cX=aZ}if(cX.length||cK.length){cJ=cM;cW=[cK,cC,cJ,by(cX.slice(0,cG))];cy(cT,JSON2.stringify(cW),cr,a1,cl)}}cE+="&idsite="+bC+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cD.getHours()+"&m="+cD.getMinutes()+"&s="+cD.getSeconds()+"&url="+n(by(c2))+(aZ.length?"&urlref="+n(by(aZ)):"")+((a8&&a8.length)?"&uid="+n(a8):"")+"&_id="+cS.uuid+"&_idts="+cS.createTs+"&_idvc="+cS.visitCount+"&_idn="+cS.newVisitor+(cK.length?"&_rcn="+n(cK):"")+(cC.length?"&_rck="+n(cC):"")+"&_refts="+cJ+"&_viewts="+cS.lastVisitTs+(String(cS.lastEcommerceOrderTs).length?"&_ects="+cS.lastEcommerceOrderTs:"")+(String(cX).length?"&_ref="+n(by(cX.slice(0,cG))):"")+(cP?"&cs="+n(cP):"")+"&send_image=0"; +for(cY in ct){if(Object.prototype.hasOwnProperty.call(ct,cY)){cE+="&"+cY+"="+ct[cY]}}var c4=[];if(cZ){for(cY in cZ){if(Object.prototype.hasOwnProperty.call(cZ,cY)&&/^dimension\d+$/.test(cY)){var cI=cY.replace("dimension","");c4.push(parseInt(cI,10));c4.push(String(cI));cE+="&"+cY+"="+cZ[cY];delete cZ[cY]}}}if(cZ&&u(cZ)){cZ=null}for(cY in aY){if(Object.prototype.hasOwnProperty.call(aY,cY)){var cO=(-1===E(c4,cY));if(cO){cE+="&dimension"+cY+"="+aY[cY]}}}if(cZ){cE+="&data="+n(JSON2.stringify(cZ))}else{if(ac){cE+="&data="+n(JSON2.stringify(ac))}}function cL(c6,c7){var c8=JSON2.stringify(c6);if(c8.length>2){return"&"+c7+"="+n(c8)}return""}var c3=cB(bs);var cU=cB(bR);cE+=cL(c3,"cvar");cE+=cL(cU,"e_cvar");if(ax){cE+=cL(ax,"_cvar");for(cY in cV){if(Object.prototype.hasOwnProperty.call(cV,cY)){if(ax[cY][0]===""||ax[cY][1]===""){delete ax[cY]}}}if(bn){cy(cQ,JSON2.stringify(ax),bU,a1,cl)}}if(aI){if(bS){cE+=">_ms="+bS}else{if(g&&g.timing&&g.timing.requestStart&&g.timing.responseEnd){cE+=">_ms="+(g.timing.responseEnd-g.timing.requestStart) +}}}cS.lastEcommerceOrderTs=B(cF)&&String(cF).length?cF:cS.lastEcommerceOrderTs;at(cS);bJ();cE+=S(c0);if(cn.length){cE+="&"+cn}if(t(bH)){cE=bH(cE)}return cE}bo=function aM(){var cC=new Date();if(cm+aL<=cC.getTime()){var cD=bX("ping=1",null,"ping");bd(cD,bg);return true}return false};function a2(cF,cE,cK,cG,cC,cN){var cI="idgoal=0",cJ,cD=new Date(),cL=[],cM,cH=String(cF).length;if(cH){cI+="&ec_id="+n(cF);cJ=Math.round(cD.getTime()/1000)}cI+="&revenue="+cE;if(String(cK).length){cI+="&ec_st="+cK}if(String(cG).length){cI+="&ec_tx="+cG}if(String(cC).length){cI+="&ec_sh="+cC}if(String(cN).length){cI+="&ec_dt="+cN}if(co){for(cM in co){if(Object.prototype.hasOwnProperty.call(co,cM)){if(!B(co[cM][1])){co[cM][1]=""}if(!B(co[cM][2])){co[cM][2]=""}if(!B(co[cM][3])||String(co[cM][3]).length===0){co[cM][3]=0}if(!B(co[cM][4])||String(co[cM][4]).length===0){co[cM][4]=1}cL.push(co[cM])}}cI+="&ec_items="+n(JSON2.stringify(cL))}cI=bX(cI,ac,"ecommerce",cJ);bd(cI,bg);if(cH){co={}}}function bv(cC,cG,cF,cE,cD,cH){if(String(cC).length&&B(cG)){a2(cC,cG,cF,cE,cD,cH) +}}function a3(cC){if(B(cC)){a2("",cC,"","","","")}}function bw(cD,cF,cE){var cC=bX("action_name="+n(ab(cD||aV)),cF,"log");bd(cC,bg,cE)}function aG(cE,cD){var cF,cC="(^| )(piwik[_-]"+cD;if(cE){for(cF=0;cF<cE.length;cF++){cC+="|"+cE[cF]}}cC+=")( |$)";return new RegExp(cC)}function aB(cC){return(am&&cC&&0===String(cC).indexOf(am))}function bY(cG,cC,cH,cD){if(aB(cC)){return 0}var cF=aG(bi,"download"),cE=aG(aN,"link"),cI=new RegExp("\\.("+cs.join("|")+")([?&#]|$)","i");if(cE.test(cG)){return"link"}if(cD||cF.test(cG)||cI.test(cC)){return"download"}if(cH){return 0}return"link"}function ag(cD){var cC;cC=cD.parentNode;while(cC!==null&&B(cC)){if(U.isLinkElement(cD)){break}cD=cC;cC=cD.parentNode}return cD}function cw(cH){cH=ag(cH);if(!U.hasNodeAttribute(cH,"href")){return}if(!B(cH.href)){return}var cG=U.getAttributeValueFromNode(cH,"href");if(aB(cG)){return}var cD=cH.pathname||bQ(cH.href);var cI=cH.hostname||c(cH.href);var cJ=cI.toLowerCase();var cE=cH.href.replace(cI,cJ);var cF=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto|tel):","i"); +if(!cF.test(cE)){var cC=bY(cH.className,cE,af(cJ,cD),U.hasNodeAttribute(cH,"download"));if(cC){return{type:cC,href:cE}}}}function aw(cC,cD,cE,cF){var cG=p.buildInteractionRequestParams(cC,cD,cE,cF);if(!cG){return}return bX(cG,null,"contentInteraction")}function cc(cE,cF,cJ,cC,cD){if(!B(cE)){return}if(aB(cE)){return cE}var cH=p.toAbsoluteUrl(cE);var cG="redirecturl="+n(cH)+"&";cG+=aw(cF,cJ,cC,(cD||cE));var cI="&";if(am.indexOf("?")<0){cI="?"}return am+cI+cG}function aQ(cC,cD){if(!cC||!cD){return false}var cE=p.findTargetNode(cC);if(p.shouldIgnoreInteraction(cE)){return false}cE=p.findTargetNodeNoDefault(cC);if(cE&&!M(cE,cD)){return false}return true}function bZ(cE,cD,cG){if(!cE){return}var cC=p.findParentContentNode(cE);if(!cC){return}if(!aQ(cC,cE)){return}var cF=p.buildContentBlock(cC);if(!cF){return}if(!cF.target&&cG){cF.target=cG}return p.buildInteractionRequestParams(cD,cF.name,cF.piece,cF.target)}function aD(cD){if(!bG||!bG.length){return false}var cC,cE;for(cC=0;cC<bG.length;cC++){cE=bG[cC]; +if(cE&&cE.name===cD.name&&cE.piece===cD.piece&&cE.target===cD.target){return true}}return false}function bc(cF){if(!cF){return false}var cI=p.findTargetNode(cF);if(!cI||p.shouldIgnoreInteraction(cI)){return false}var cJ=cw(cI);if(cu&&cJ&&cJ.type){return false}if(U.isLinkElement(cI)&&U.hasNodeAttributeWithValue(cI,"href")){var cC=String(U.getAttributeValueFromNode(cI,"href"));if(0===cC.indexOf("#")){return false}if(aB(cC)){return true}if(!p.isUrlToCurrentDomain(cC)){return false}var cG=p.buildContentBlock(cF);if(!cG){return}var cE=cG.name;var cK=cG.piece;var cH=cG.target;if(!U.hasNodeAttributeWithValue(cI,p.CONTENT_TARGET_ATTR)||cI.wasContentTargetAttrReplaced){cI.wasContentTargetAttrReplaced=true;cH=p.toAbsoluteUrl(cC);U.setAnyAttribute(cI,p.CONTENT_TARGET_ATTR,cH)}var cD=cc(cC,"click",cE,cK,cH);p.setHrefAttribute(cI,cD);return true}return false}function au(cD){if(!cD||!cD.length){return}var cC;for(cC=0;cC<cD.length;cC++){bc(cD[cC])}}function aF(cC){return function(cD){if(!cC){return}var cG=p.findParentContentNode(cC); +var cH;if(cD){cH=cD.target||cD.srcElement}if(!cH){cH=cC}if(!aQ(cG,cH)){return}bM(bg);if(U.isLinkElement(cC)&&U.hasNodeAttributeWithValue(cC,"href")&&U.hasNodeAttributeWithValue(cC,p.CONTENT_TARGET_ATTR)){var cE=U.getAttributeValueFromNode(cC,"href");if(!aB(cE)&&cC.wasContentTargetAttrReplaced){U.setAnyAttribute(cC,p.CONTENT_TARGET_ATTR,"")}}var cL=cw(cC);if(ad&&cL&&cL.type){return cL.type}if(bc(cG)){return"href"}var cI=p.buildContentBlock(cG);if(!cI){return}var cF=cI.name;var cM=cI.piece;var cK=cI.target;var cJ=aw("click",cF,cM,cK);bd(cJ,bg);return cJ}}function bx(cE){if(!cE||!cE.length){return}var cC,cD;for(cC=0;cC<cE.length;cC++){cD=p.findTargetNode(cE[cC]);if(cD&&!cD.contentInteractionTrackingSetupDone){cD.contentInteractionTrackingSetupDone=true;aa(cD,"click",aF(cD))}}}function a7(cE,cF){if(!cE||!cE.length){return[]}var cC,cD;for(cC=0;cC<cE.length;cC++){if(aD(cE[cC])){cE.splice(cC,1);cC--}else{bG.push(cE[cC])}}if(!cE||!cE.length){return[]}au(cF);bx(cF);var cG=[];for(cC=0;cC<cE.length; +cC++){cD=bX(p.buildImpressionRequestParams(cE[cC].name,cE[cC].piece,cE[cC].target),undefined,"contentImpressions");if(cD){cG.push(cD)}}return cG}function b3(cD){var cC=p.collectContent(cD);return a7(cC,cD)}function aO(cD){if(!cD||!cD.length){return[]}var cC;for(cC=0;cC<cD.length;cC++){if(!p.isNodeVisible(cD[cC])){cD.splice(cC,1);cC--}}if(!cD||!cD.length){return[]}return b3(cD)}function ao(cE,cC,cD){var cF=p.buildImpressionRequestParams(cE,cC,cD);return bX(cF,null,"contentImpression")}function cx(cF,cD){if(!cF){return}var cC=p.findParentContentNode(cF);var cE=p.buildContentBlock(cC);if(!cE){return}if(!cD){cD="Unknown"}return aw(cD,cE.name,cE.piece,cE.target)}function ce(cD,cF,cC,cE){return"e_c="+n(cD)+"&e_a="+n(cF)+(B(cC)?"&e_n="+n(cC):"")+(B(cE)?"&e_v="+n(cE):"")}function ah(cE,cG,cC,cF,cH){if(String(cE).length===0||String(cG).length===0){return false}var cD=bX(ce(cE,cG,cC,cF),cH,"event");bd(cD,bg)}function bF(cC,cF,cD,cG){var cE=bX("search="+n(cC)+(cF?"&search_cat="+n(cF):"")+(B(cD)?"&search_count="+cD:""),cG,"sitesearch"); +bd(cE,bg)}function ci(cC,cF,cE){var cD=bX("idgoal="+cC+(cF?"&revenue="+cF:""),cE,"goal");bd(cD,bg)}function cp(cF,cC,cJ,cI,cE){var cH=cC+"="+n(by(cF));var cD=bZ(cE,"click",cF);if(cD){cH+="&"+cD}var cG=bX(cH,cJ,"link");bd(cG,(cI?0:bg),cI)}function bp(cD,cC){if(cD!==""){return cD+cC.charAt(0).toUpperCase()+cC.slice(1)}return cC}function bN(cH){var cG,cC,cF=["","webkit","ms","moz"],cE;if(!aT){for(cC=0;cC<cF.length;cC++){cE=cF[cC];if(Object.prototype.hasOwnProperty.call(y,bp(cE,"hidden"))){if(y[bp(cE,"visibilityState")]==="prerender"){cG=true}break}}}if(cG){aa(y,cE+"visibilitychange",function cD(){y.removeEventListener(cE+"visibilitychange",cD,false);cH()});return}cH()}function ar(cC){if(y.readyState==="complete"){cC()}else{if(L.addEventListener){L.addEventListener("load",cC)}else{if(L.attachEvent){L.attachEvent("onload",cC)}}}}function aR(cF){var cC=false;if(y.attachEvent){cC=y.readyState==="complete"}else{cC=y.readyState!=="loading"}if(cC){cF();return}var cE;if(y.addEventListener){aa(y,"DOMContentLoaded",function cD(){y.removeEventListener("DOMContentLoaded",cD,false); +if(!cC){cC=true;cF()}})}else{if(y.attachEvent){y.attachEvent("onreadystatechange",function cD(){if(y.readyState==="complete"){y.detachEvent("onreadystatechange",cD);if(!cC){cC=true;cF()}}});if(y.documentElement.doScroll&&L===L.top){(function cD(){if(!cC){try{y.documentElement.doScroll("left")}catch(cG){setTimeout(cD,0);return}cC=true;cF()}}())}}}aa(L,"load",function(){if(!cC){cC=true;cF()}},false)}function b9(cC){var cD=cw(cC);if(cD&&cD.type){cD.href=k(cD.href);cp(cD.href,cD.type,undefined,null,cC)}}function b1(){return y.all&&!y.addEventListener}function ck(cC){var cE=cC.which;var cD=(typeof cC.button);if(!cE&&cD!=="undefined"){if(b1()){if(cC.button&1){cE=1}else{if(cC.button&2){cE=3}else{if(cC.button&4){cE=2}}}}else{if(cC.button===0||cC.button==="0"){cE=1}else{if(cC.button&1){cE=2}else{if(cC.button&2){cE=3}}}}}return cE}function bq(cC){switch(ck(cC)){case 1:return"left";case 2:return"middle";case 3:return"right"}}function aH(cC){return cC.target||cC.srcElement}function ak(cC){return function(cF){cF=cF||L.event; +var cE=bq(cF);var cG=aH(cF);if(cF.type==="click"){var cD=false;if(cC&&cE==="middle"){cD=true}if(cG&&!cD){b9(cG)}}else{if(cF.type==="mousedown"){if(cE==="middle"&&cG){ay=cE;ba=cG}else{ay=ba=null}}else{if(cF.type==="mouseup"){if(cE===ay&&cG===ba){b9(cG)}ay=ba=null}else{if(cF.type==="contextmenu"){b9(cG)}}}}}}function ae(cD,cC){aa(cD,"click",ak(cC),false);if(cC){aa(cD,"mouseup",ak(cC),false);aa(cD,"mousedown",ak(cC),false);aa(cD,"contextmenu",ak(cC),false)}}function bb(cD){if(!ad){ad=true;var cE,cC=aG(a9,"ignore"),cF=y.links;if(cF){for(cE=0;cE<cF.length;cE++){if(!cC.test(cF[cE].className)){ae(cF[cE],cD)}}}}}function az(cE,cG,cH){if(bL){return true}bL=true;var cI=false;var cF,cD;function cC(){cI=true}ar(function(){function cJ(cL){setTimeout(function(){if(!bL){return}cI=false;cH.trackVisibleContentImpressions();cJ(cL)},cL)}function cK(cL){setTimeout(function(){if(!bL){return}if(cI){cI=false;cH.trackVisibleContentImpressions()}cK(cL)},cL)}if(cE){cF=["scroll","resize"];for(cD=0;cD<cF.length;cD++){if(y.addEventListener){y.addEventListener(cF[cD],cC) +}else{L.attachEvent("on"+cF[cD],cC)}}cK(100)}if(cG&&cG>0){cG=parseInt(cG,10);cJ(cG)}})}function b7(){var cD,cF,cG={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"};if(!((new RegExp("MSIE")).test(f.userAgent))){if(f.mimeTypes&&f.mimeTypes.length){for(cD in cG){if(Object.prototype.hasOwnProperty.call(cG,cD)){cF=f.mimeTypes[cG[cD]];ct[cD]=(cF&&cF.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&B(f.javaEnabled)&&f.javaEnabled()){ct.java="1"}if(t(L.GearsFactory)){ct.gears="1"}ct.cookie=bB()}var cE=parseInt(P.width,10);var cC=parseInt(P.height,10);ct.res=parseInt(cE,10)+"x"+parseInt(cC,10)}b7();aU();at();this.getVisitorId=function(){return aE().uuid};this.getVisitorInfo=function(){return cd()};this.getAttributionInfo=function(){return bj()};this.getAttributionCampaignName=function(){return bj()[0] +};this.getAttributionCampaignKeyword=function(){return bj()[1]};this.getAttributionReferrerTimestamp=function(){return bj()[2]};this.getAttributionReferrerUrl=function(){return bj()[3]};this.setTrackerUrl=function(cC){am=cC};this.getTrackerUrl=function(){return am};this.addTracker=function(cC,cE){if(!cE){throw new Error("A siteId must be given to add a new tracker")}if(!B(cC)||null===cC){cC=this.getTrackerUrl()}var cD=new I(cC,cE);A.push(cD);return cD};this.getSiteId=function(){return bC};this.setSiteId=function(cC){bz(cC)};this.setUserId=function(cC){if(!B(cC)||!cC.length){return}a8=cC;bl=bD(a8).substr(0,16)};this.getUserId=function(){return a8};this.setCustomData=function(cC,cD){if(O(cC)){ac=cC}else{if(!ac){ac={}}ac[cC]=cD}};this.getCustomData=function(){return ac};this.setCustomRequestProcessing=function(cC){bH=cC};this.appendToTrackingUrl=function(cC){cn=cC};this.getRequest=function(cC){return bX(cC)};this.addPlugin=function(cC,cD){a[cC]=cD};this.setCustomDimension=function(cC,cD){cC=parseInt(cC,10); +if(cC>0){if(!B(cD)){cD=""}if(!q(cD)){cD=String(cD)}aY[cC]=cD}};this.getCustomDimension=function(cC){cC=parseInt(cC,10);if(cC>0&&Object.prototype.hasOwnProperty.call(aY,cC)){return aY[cC]}};this.deleteCustomDimension=function(cC){cC=parseInt(cC,10);if(cC>0){delete aY[cC]}};this.setCustomVariable=function(cD,cC,cG,cE){var cF;if(!B(cE)){cE="visit"}if(!B(cC)){return}if(!B(cG)){cG=""}if(cD>0){cC=!q(cC)?String(cC):cC;cG=!q(cG)?String(cG):cG;cF=[cC.slice(0,a4),cG.slice(0,a4)];if(cE==="visit"||cE===2){b6();ax[cD]=cF}else{if(cE==="page"||cE===3){bs[cD]=cF}else{if(cE==="event"){bR[cD]=cF}}}}};this.getCustomVariable=function(cD,cE){var cC;if(!B(cE)){cE="visit"}if(cE==="page"||cE===3){cC=bs[cD]}else{if(cE==="event"){cC=bR[cD]}else{if(cE==="visit"||cE===2){b6();cC=ax[cD]}}}if(!B(cC)||(cC&&cC[0]==="")){return false}return cC};this.deleteCustomVariable=function(cC,cD){if(this.getCustomVariable(cC,cD)){this.setCustomVariable(cC,"","",cD)}};this.storeCustomVariablesInCookie=function(){bn=true};this.setLinkTrackingTimer=function(cC){bg=cC +};this.setDownloadExtensions=function(cC){if(q(cC)){cC=cC.split("|")}cs=cC};this.addDownloadExtensions=function(cD){var cC;if(q(cD)){cD=cD.split("|")}for(cC=0;cC<cD.length;cC++){cs.push(cD[cC])}};this.removeDownloadExtensions=function(cE){var cD,cC=[];if(q(cE)){cE=cE.split("|")}for(cD=0;cD<cs.length;cD++){if(E(cE,cs[cD])===-1){cC.push(cs[cD])}}cs=cC};this.setDomains=function(cC){ai=q(cC)?[cC]:cC;var cG=false,cE=0,cD;for(cE;cE<ai.length;cE++){cD=String(ai[cE]);if(b8(cj,D(cD))){cG=true;break}var cF=bQ(cD);if(cF&&cF!=="/"&&cF!=="/*"){cG=true;break}}if(!cG){ai.push(cj)}};this.setIgnoreClasses=function(cC){a9=q(cC)?[cC]:cC};this.setRequestMethod=function(cC){cv=cC||bO};this.setRequestContentType=function(cC){b0=cC||aq};this.setReferrerUrl=function(cC){aZ=cC};this.setCustomUrl=function(cC){aK=br(bk,cC)};this.setDocumentTitle=function(cC){aV=cC};this.setAPIUrl=function(cC){bf=cC};this.setDownloadClasses=function(cC){bi=q(cC)?[cC]:cC};this.setLinkClasses=function(cC){aN=q(cC)?[cC]:cC};this.setCampaignNameKey=function(cC){bW=q(cC)?[cC]:cC +};this.setCampaignKeywordKey=function(cC){be=q(cC)?[cC]:cC};this.discardHashTag=function(cC){bm=cC};this.setCookieNamePrefix=function(cC){aW=cC;ax=bt()};this.setCookieDomain=function(cC){var cD=D(cC);if(a6(cD)){cl=cD;aU()}};this.setCookiePath=function(cC){a1=cC;aU()};this.setVisitorCookieTimeout=function(cC){cb=cC*1000};this.setSessionCookieTimeout=function(cC){bU=cC*1000};this.setReferralCookieTimeout=function(cC){cr=cC*1000};this.setConversionAttributionFirstReferrer=function(cC){a5=cC};this.disableCookies=function(){aX=true;ct.cookie="0";if(bC){an()}};this.deleteCookies=function(){an()};this.setDoNotTrack=function(cD){var cC=f.doNotTrack||f.msDoNotTrack;cf=cD&&(cC==="yes"||cC==="1");if(cf){this.disableCookies()}};this.addListener=function(cD,cC){ae(cD,cC)};this.enableLinkTracking=function(cC){cu=true;bN(function(){aR(function(){bb(cC)})})};this.enableJSErrorTracking=function(){if(ch){return}ch=true;var cC=L.onerror;L.onerror=function(cH,cF,cE,cG,cD){bN(function(){var cI="JavaScript Errors"; +var cJ=cF+":"+cE;if(cG){cJ+=":"+cG}ah(cI,cJ,cH)});if(cC){return cC(cH,cF,cE,cG,cD)}return false}};this.disablePerformanceTracking=function(){aI=false};this.setGenerationTimeMs=function(cC){bS=parseInt(cC,10)};this.enableHeartBeatTimer=function(cC){cC=Math.max(cC,1);aL=(cC||15)*1000;if(cm!==null){cA()}};this.killFrame=function(){if(L.location!==L.top.location){L.top.location=L.location}};this.redirectFile=function(cC){if(L.location.protocol==="file:"){L.location=cC}};this.setCountPreRendered=function(cC){aT=cC};this.trackGoal=function(cC,cE,cD){bN(function(){ci(cC,cE,cD)})};this.trackLink=function(cD,cC,cF,cE){bN(function(){cp(cD,cC,cF,cE)})};this.trackPageView=function(cC,cE,cD){bG=[];if(F(bC)){bN(function(){Q(am,bf,bC)})}else{bN(function(){bw(cC,cE,cD)})}};this.trackAllContentImpressions=function(){if(F(bC)){return}bN(function(){aR(function(){var cC=p.findContentNodes();var cD=b3(cC);cz(cD,bg)})})};this.trackVisibleContentImpressions=function(cC,cD){if(F(bC)){return}if(!B(cC)){cC=true}if(!B(cD)){cD=750 +}az(cC,cD,this);bN(function(){ar(function(){var cE=p.findContentNodes();var cF=aO(cE);cz(cF,bg)})})};this.trackContentImpression=function(cE,cC,cD){if(F(bC)){return}if(!cE){return}cC=cC||"Unknown";bN(function(){var cF=ao(cE,cC,cD);bd(cF,bg)})};this.trackContentImpressionsWithinNode=function(cC){if(F(bC)||!cC){return}bN(function(){if(bL){ar(function(){var cD=p.findContentNodesWithinNode(cC);var cE=aO(cD);cz(cE,bg)})}else{aR(function(){var cD=p.findContentNodesWithinNode(cC);var cE=b3(cD);cz(cE,bg)})}})};this.trackContentInteraction=function(cE,cF,cC,cD){if(F(bC)){return}if(!cE||!cF){return}cC=cC||"Unknown";bN(function(){var cG=aw(cE,cF,cC,cD);bd(cG,bg)})};this.trackContentInteractionNode=function(cD,cC){if(F(bC)||!cD){return}bN(function(){var cE=cx(cD,cC);bd(cE,bg)})};this.logAllContentBlocksOnPage=function(){var cD=p.findContentNodes();var cC=p.collectContent(cD);if(console!==undefined&&console&&console.log){console.log(cC)}};this.trackEvent=function(cD,cF,cC,cE,cG){bN(function(){ah(cD,cF,cC,cE,cG) +})};this.trackSiteSearch=function(cC,cE,cD,cF){bN(function(){bF(cC,cE,cD,cF)})};this.setEcommerceView=function(cF,cC,cE,cD){if(!B(cE)||!cE.length){cE=""}else{if(cE instanceof Array){cE=JSON2.stringify(cE)}}bs[5]=["_pkc",cE];if(B(cD)&&String(cD).length){bs[2]=["_pkp",cD]}if((!B(cF)||!cF.length)&&(!B(cC)||!cC.length)){return}if(B(cF)&&cF.length){bs[3]=["_pks",cF]}if(!B(cC)||!cC.length){cC=""}bs[4]=["_pkn",cC]};this.addEcommerceItem=function(cG,cC,cE,cD,cF){if(cG.length){co[cG]=[cG,cC,cE,cD,cF]}};this.trackEcommerceOrder=function(cC,cG,cF,cE,cD,cH){bv(cC,cG,cF,cE,cD,cH)};this.trackEcommerceCartUpdate=function(cC){a3(cC)};this.trackRequest=function(cC,cE,cD){bN(function(){var cF=bX(cC,cE);bd(cF,bg,cD)})};d.trigger("TrackerSetup",[this])}function z(){return{push:V}}function b(ah,ag){var ai={};var ae,af;for(ae=0;ae<ag.length;ae++){var ac=ag[ae];ai[ac]=1;for(af=0;af<ah.length;af++){if(ah[af]&&ah[af][0]){var ad=ah[af][0];if(ac===ad){V(ah[af]);delete ah[af];if(ai[ad]>1){Y("The method "+ad+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers') +}ai[ad]++}}}}return ah}var v=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","enableLinkTracking"];function T(ac,ae){var ad=new I(ac,ae);A.push(ad);_paq=b(_paq,v);for(x=0;x<_paq.length;x++){if(_paq[x]){V(_paq[x])}}_paq=new z();return ad}aa(L,"beforeunload",W,false);Date.prototype.getTimeAlias=Date.prototype.getTime;d={initialized:false,on:function(ad,ac){if(!r[ad]){r[ad]=[]}r[ad].push(ac)},off:function(ae,ad){if(!r[ae]){return}var ac=0;for(ac;ac<r[ae].length;ac++){if(r[ae][ac]===ad){delete r[ae][ac]}}},trigger:function(ae,af,ad){if(!r[ae]){return}var ac=0;for(ac;ac<r[ae].length;ac++){r[ae][ac].apply(ad||L,af)}},addPlugin:function(ac,ad){a[ac]=ad},getTracker:function(ac,ad){if(!B(ad)){ad=this.getAsyncTracker().getSiteId()}if(!B(ac)){ac=this.getAsyncTracker().getTrackerUrl()}return new I(ac,ad)},getAsyncTrackers:function(){return A},addTracker:function(ac,ad){if(!A.length){T(ac,ad)}else{A[0].addTracker(ac,ad) +}},getAsyncTracker:function(ad,ag){var af;if(A&&A[0]){af=A[0]}if(!ag&&!ad){return af}if((!B(ag)||null===ag)&&af){ag=af.getSiteId()}if((!B(ad)||null===ad)&&af){ad=af.getTrackerUrl()}var ae,ac=0;for(ac;ac<A.length;ac++){ae=A[ac];if(ae&&String(ae.getSiteId())===String(ag)&&ae.getTrackerUrl()===ad){return ae}}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}()); +/*!!! pluginTrackerHook */ +}(function(){if(window&&"object"===typeof window.piwikPluginAsyncInit&&window.piwikPluginAsyncInit.length){var a=0;for(a;a<window.piwikPluginAsyncInit.length;a++){if(typeof window.piwikPluginAsyncInit[a]==="function"){window.piwikPluginAsyncInit[a]()}}}window.Piwik.addTracker();window.Piwik.trigger("PiwikInitialized",[]);window.Piwik.initialized=true}());if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=window.Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h] +}}catch(i){}return}var c,e=window.Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; +/*!! @license-end */ +}; \ No newline at end of file diff --git a/piwik.js b/piwik.js index f89c3a539f..002e4cb757 100644 --- a/piwik.js +++ b/piwik.js @@ -18,51 +18,54 @@ var I="000000";var t=function(ac,ad){return(I+(ad||0)).slice(-ac)};var z="\\u00" }else{at=null}}else{if(typeof at.toJSON=="function"&&((ae!=N&&ae!=O&&ae!=E)||r.call(at,"toJSON"))){at=at.toJSON(ai)}}}if(ag){at=ag.call(aA,ai,at)}if(at===null){return"null"}ae=u.call(at);if(ae==A){return""+at}else{if(ae==N){return at>-1/0&&at<1/0?""+at:"null"}else{if(ae==O){return C(""+at)}}}if(typeof at=="object"){for(af=aj.length;af--;){if(aj[af]===at){throw aa()}}aj.push(at);ar=[];av=ac;ac+=ax;if(ae==E){for(ah=0,af=at.length;ah<af;ah++){ad=p(ah,at,ag,al,ax,ac,aj);ar.push(ad===L?"null":ad)}ao=ar.length?(ax?"[\n"+ac+ar.join(",\n"+ac)+"\n"+av+"]":("["+ar.join(",")+"]")):"[]"}else{m(al||at,function(aC){var aB=p(aC,at,ag,al,ax,ac,aj);if(aB!==L){ar.push(C(aC)+":"+(ax?" ":"")+aB)}});ao=ar.length?(ax?"{\n"+ac+ar.join(",\n"+ac)+"\n"+av+"}":("{"+ar.join(",")+"}")):"{}"}aj.pop();return ao}};V.stringify=function(ac,ae,af){var ad,al,aj,ai;if(e[typeof ae]&&ae){if((ai=u.call(ae))==U){al=ae}else{if(ai==E){aj={};for(var ah=0,ag=ae.length,ak;ah<ag;ak=ae[ah++],((ai=u.call(ak)),ai==O||ai==N)&&(aj[ak]=1)){}}}}if(af){if((ai=u.call(af))==N){if((af-=af%1)>0){for(ad="",af>10&&(af=10); ad.length<af;ad+=" "){}}}else{if(ai==O){ad=af.length<=10?af:af.slice(0,10)}}}return p("",(ak={},ak[""]=ac,ak),al,aj,ad,"",[])}}if(!o("json-parse")){var M=R.fromCharCode;var l={92:"\\",34:'"',47:"/",98:"\b",116:"\t",110:"\n",102:"\f",114:"\r"};var G,X;var H=function(){G=X=null;throw T()};var y=function(){var ah=X,af=ah.length,ag,ae,ac,ai,ad;while(G<af){ad=ah.charCodeAt(G);switch(ad){case 9:case 10:case 13:case 32:G++;break;case 123:case 125:case 91:case 93:case 58:case 44:ag=F?ah.charAt(G):ah[G];G++;return ag;case 34:for(ag="@",G++;G<af;){ad=ah.charCodeAt(G);if(ad<32){H()}else{if(ad==92){ad=ah.charCodeAt(++G);switch(ad){case 92:case 34:case 47:case 98:case 116:case 110:case 102:case 114:ag+=l[ad];G++;break;case 117:ae=++G;for(ac=G+4;G<ac;G++){ad=ah.charCodeAt(G);if(!(ad>=48&&ad<=57||ad>=97&&ad<=102||ad>=65&&ad<=70)){H()}}ag+=M("0x"+ah.slice(ae,G));break;default:H()}}else{if(ad==34){break}ad=ah.charCodeAt(G);ae=G;while(ad>=32&&ad!=92&&ad!=34){ad=ah.charCodeAt(++G)}ag+=ah.slice(ae,G)}}}if(ah.charCodeAt(G)==34){G++; return ag}H();default:ae=G;if(ad==45){ai=true;ad=ah.charCodeAt(++G)}if(ad>=48&&ad<=57){if(ad==48&&((ad=ah.charCodeAt(G+1)),ad>=48&&ad<=57)){H()}ai=false;for(;G<af&&((ad=ah.charCodeAt(G)),ad>=48&&ad<=57);G++){}if(ah.charCodeAt(G)==46){ac=++G;for(;ac<af&&((ad=ah.charCodeAt(ac)),ad>=48&&ad<=57);ac++){}if(ac==G){H()}G=ac}ad=ah.charCodeAt(G);if(ad==101||ad==69){ad=ah.charCodeAt(++G);if(ad==43||ad==45){G++}for(ac=G;ac<af&&((ad=ah.charCodeAt(ac)),ad>=48&&ad<=57);ac++){}if(ac==G){H()}G=ac}return +ah.slice(ae,G)}if(ai){H()}if(ah.slice(G,G+4)=="true"){G+=4;return true}else{if(ah.slice(G,G+5)=="false"){G+=5;return false}else{if(ah.slice(G,G+4)=="null"){G+=4;return null}}}H()}}return"$"};var W=function(ad){var ac,ae;if(ad=="$"){H()}if(typeof ad=="string"){if((F?ad.charAt(0):ad[0])=="@"){return ad.slice(1)}if(ad=="["){ac=[];for(;;ae||(ae=true)){ad=y();if(ad=="]"){break}if(ae){if(ad==","){ad=y();if(ad=="]"){H()}}else{H()}}if(ad==","){H()}ac.push(W(ad))}return ac}else{if(ad=="{"){ac={};for(;;ae||(ae=true)){ad=y(); -if(ad=="}"){break}if(ae){if(ad==","){ad=y();if(ad=="}"){H()}}else{H()}}if(ad==","||typeof ad!="string"||(F?ad.charAt(0):ad[0])!="@"||y()!=":"){H()}ac[ad.slice(1)]=W(y())}return ac}}H()}return ad};var P=function(ae,ad,af){var ac=w(ae,ad,af);if(ac===L){delete ae[ad]}else{ae[ad]=ac}};var w=function(af,ae,ag){var ad=af[ae],ac;if(typeof ad=="object"&&ad){if(u.call(ad)==E){for(ac=ad.length;ac--;){P(ad,ac,ag)}}else{m(ad,function(ah){P(ad,ah,ag)})}}return ag.call(af,ae,ad)};V.parse=function(ae,af){var ac,ad;G=0;X=""+ae;ac=W(y());if(y()!="$"){H()}G=X=null;return af&&u.call(af)==U?w((ad={},ad[""]=ac,ad),"",af):ac}}}V.runInContext=j;return V}if(h&&!c){j(i,h)}else{var f=i.JSON,k=i.JSON3,d=false;var g=j(i,(i.JSON3={noConflict:function(){if(!d){d=true;i.JSON=f;i.JSON3=k;f=k=null}return g}}));i.JSON={parse:g.parse,stringify:g.stringify}}if(c){define(function(){return g})}}).call(this);JSON2=a})()}if(typeof _paq!=="object"){_paq=[]}if(typeof window.Piwik!=="object"){window.Piwik=(function(){var l,a={},x=document,f=navigator,O=screen,K=window,g=K.performance||K.mozPerformance||K.msPerformance||K.webkitPerformance,n=K.encodeURIComponent,J=K.decodeURIComponent,i=unescape,z=[],w,d; -function k(aa){try{return J(aa)}catch(ab){return unescape(aa)}}function A(ab){var aa=typeof ab;return aa!=="undefined"}function s(aa){return typeof aa==="function"}function N(aa){return typeof aa==="object"}function q(aa){return typeof aa==="string"||aa instanceof String}function t(ab){if(!ab){return true}var aa;var ac=true;for(aa in ab){if(Object.prototype.hasOwnProperty.call(ab,aa)){ac=false}}return ac}function W(aa){if(console!==undefined&&console&&console.error){console.error(aa)}}function T(){var ab,aa,ae,ad;for(ab=0;ab<arguments.length;ab+=1){ad=arguments[ab];ae=ad.shift();for(aa=0;aa<z.length;aa++){if(q(ae)){if(z[aa][ae]){z[aa][ae].apply(z[aa],ad)}else{var ac="The method '"+ae+'\' was not found in "_paq" variable. Please have a look at the Piwik tracker documentation: http://developer.piwik.org/api-reference/tracking-javascript';W(ac);throw new TypeError(ac)}if(ae==="addTracker"){break}if(ae==="setTrackerUrl"||ae==="setSiteId"){break}}else{ae.apply(z[aa],ad)}}}}function Y(ad,ac,ab,aa){if(ad.addEventListener){ad.addEventListener(ac,ab,aa); -return true}if(ad.attachEvent){return ad.attachEvent("on"+ac,ab)}ad["on"+ac]=ab}function R(ab,af){var aa="",ad,ac,ae;for(ad in a){if(Object.prototype.hasOwnProperty.call(a,ad)){ac=a[ad][ab];if(s(ac)){ae=ac(af);if(ae){aa+=ae}}}}return aa}function U(){var aa;R("unload");if(l){do{aa=new Date()}while(aa.getTimeAlias()<l)}}function j(ac,ab){var aa=x.createElement("script");aa.type="text/javascript";aa.src=ac;if(aa.readyState){aa.onreadystatechange=function(){var ad=this.readyState;if(ad==="loaded"||ad==="complete"){aa.onreadystatechange=null;ab()}}}else{aa.onload=ab}x.getElementsByTagName("head")[0].appendChild(aa)}function B(){var aa="";try{aa=K.top.document.referrer}catch(ac){if(K.parent){try{aa=K.parent.document.referrer}catch(ab){aa=""}}}if(aa===""){aa=x.referrer}return aa}function m(aa){var ac=new RegExp("^([a-z]+):"),ab=ac.exec(aa);return ab?ab[1]:null}function c(aa){var ac=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),ab=ac.exec(aa);return ab?ab[1]:aa}function M(ac,ab){var aa="[\\?&#]"+ab+"=([^&#]*)"; -var ae=new RegExp(aa);var ad=ae.exec(ac);return ad?J(ad[1]):""}function v(aa){return unescape(n(aa))}function X(ap){var ac=function(aw,av){return(aw<<av)|(aw>>>(32-av))},aq=function(ay){var aw="",ax,av;for(ax=7;ax>=0;ax--){av=(ay>>>(ax*4))&15;aw+=av.toString(16)}return aw},af,at,ar,ab=[],aj=1732584193,ah=4023233417,ag=2562383102,ae=271733878,ad=3285377520,ao,an,am,al,ak,au,aa,ai=[];ap=v(ap);aa=ap.length;for(at=0;at<aa-3;at+=4){ar=ap.charCodeAt(at)<<24|ap.charCodeAt(at+1)<<16|ap.charCodeAt(at+2)<<8|ap.charCodeAt(at+3);ai.push(ar)}switch(aa&3){case 0:at=2147483648;break;case 1:at=ap.charCodeAt(aa-1)<<24|8388608;break;case 2:at=ap.charCodeAt(aa-2)<<24|ap.charCodeAt(aa-1)<<16|32768;break;case 3:at=ap.charCodeAt(aa-3)<<24|ap.charCodeAt(aa-2)<<16|ap.charCodeAt(aa-1)<<8|128;break}ai.push(at);while((ai.length&15)!==14){ai.push(0)}ai.push(aa>>>29);ai.push((aa<<3)&4294967295);for(af=0;af<ai.length;af+=16){for(at=0;at<16;at++){ab[at]=ai[af+at]}for(at=16;at<=79;at++){ab[at]=ac(ab[at-3]^ab[at-8]^ab[at-14]^ab[at-16],1) -}ao=aj;an=ah;am=ag;al=ae;ak=ad;for(at=0;at<=19;at++){au=(ac(ao,5)+((an&am)|(~an&al))+ak+ab[at]+1518500249)&4294967295;ak=al;al=am;am=ac(an,30);an=ao;ao=au}for(at=20;at<=39;at++){au=(ac(ao,5)+(an^am^al)+ak+ab[at]+1859775393)&4294967295;ak=al;al=am;am=ac(an,30);an=ao;ao=au}for(at=40;at<=59;at++){au=(ac(ao,5)+((an&am)|(an&al)|(am&al))+ak+ab[at]+2400959708)&4294967295;ak=al;al=am;am=ac(an,30);an=ao;ao=au}for(at=60;at<=79;at++){au=(ac(ao,5)+(an^am^al)+ak+ab[at]+3395469782)&4294967295;ak=al;al=am;am=ac(an,30);an=ao;ao=au}aj=(aj+ao)&4294967295;ah=(ah+an)&4294967295;ag=(ag+am)&4294967295;ae=(ae+al)&4294967295;ad=(ad+ak)&4294967295}au=aq(aj)+aq(ah)+aq(ag)+aq(ae)+aq(ad);return au.toLowerCase()}function Q(ac,aa,ab){if(!ac){ac=""}if(!aa){aa=""}if(ac==="translate.googleusercontent.com"){if(ab===""){ab=aa}aa=M(aa,"u");ac=c(aa)}else{if(ac==="cc.bingj.com"||ac==="webcache.googleusercontent.com"||ac.slice(0,5)==="74.6."){aa=x.links[0].href;ac=c(aa)}}return[ac,aa,ab]}function C(ab){var aa=ab.length;if(ab.charAt(--aa)==="."){ab=ab.slice(0,aa) -}if(ab.slice(0,2)==="*."){ab=ab.slice(1)}if(ab.indexOf("/")!==-1){ab=ab.substr(0,ab.indexOf("/"))}return ab}function Z(ab){ab=ab&&ab.text?ab.text:ab;if(!q(ab)){var aa=x.getElementsByTagName("title");if(aa&&A(aa[0])){ab=aa[0].text}}return ab}function G(aa){if(!aa){return[]}if(!A(aa.children)&&A(aa.childNodes)){return aa.children}if(A(aa.children)){return aa.children}return[]}function L(ab,aa){if(!ab||!aa){return false}if(ab.contains){return ab.contains(aa)}if(ab===aa){return true}if(ab.compareDocumentPosition){return !!(ab.compareDocumentPosition(aa)&16)}return false}function D(ac,ad){if(ac&&ac.indexOf){return ac.indexOf(ad)}if(!A(ac)||ac===null){return -1}if(!ac.length){return -1}var aa=ac.length;if(aa===0){return -1}var ab=0;while(ab<aa){if(ac[ab]===ad){return ab}ab++}return -1}function V(ab,aa){ab=String(ab);return ab.lastIndexOf(aa,0)===0}function I(ab,aa){ab=String(ab);return ab.indexOf(aa,ab.length-aa.length)!==-1}function r(ab,aa){ab=String(ab);return ab.indexOf(aa)!==-1}function e(ab,aa){ab=String(ab); -return ab.substr(0,ab.length-aa)}function h(ac){if(!ac){return false}function aa(ae,af){if(K.getComputedStyle){return x.defaultView.getComputedStyle(ae,null)[af]}if(ae.currentStyle){return ae.currentStyle[af]}}function ad(ae){ae=ae.parentNode;while(ae){if(ae===x){return true}ae=ae.parentNode}return false}function ab(ag,am,ae,aj,ah,ak,ai){var af=ag.parentNode,al=1;if(!ad(ag)){return false}if(9===af.nodeType){return true}if("0"===aa(ag,"opacity")||"none"===aa(ag,"display")||"hidden"===aa(ag,"visibility")){return false}if(!A(am)||!A(ae)||!A(aj)||!A(ah)||!A(ak)||!A(ai)){am=ag.offsetTop;ah=ag.offsetLeft;aj=am+ag.offsetHeight;ae=ah+ag.offsetWidth;ak=ag.offsetWidth;ai=ag.offsetHeight}if(ac===ag&&(0===ai||0===ak)&&"hidden"===aa(ag,"overflow")){return false}if(af){if(("hidden"===aa(af,"overflow")||"scroll"===aa(af,"overflow"))){if(ah+al>af.offsetWidth+af.scrollLeft||ah+ak-al<af.scrollLeft||am+al>af.offsetHeight+af.scrollTop||am+ai-al<af.scrollTop){return false}}if(ag.offsetParent===af){ah+=af.offsetLeft; -am+=af.offsetTop}return ab(af,am,ae,aj,ah,ak,ai)}return true}return ab(ac)}var S={htmlCollectionToArray:function(ac){var aa=[],ab;if(!ac||!ac.length){return aa}for(ab=0;ab<ac.length;ab++){aa.push(ac[ab])}return aa},find:function(aa){if(!document.querySelectorAll||!aa){return[]}var ab=document.querySelectorAll(aa);return this.htmlCollectionToArray(ab)},findMultiple:function(ac){if(!ac||!ac.length){return[]}var ab,ad;var aa=[];for(ab=0;ab<ac.length;ab++){ad=this.find(ac[ab]);aa=aa.concat(ad)}aa=this.makeNodesUnique(aa);return aa},findNodesByTagName:function(ab,aa){if(!ab||!aa||!ab.getElementsByTagName){return[]}var ac=ab.getElementsByTagName(aa);return this.htmlCollectionToArray(ac)},makeNodesUnique:function(aa){var af=[].concat(aa);aa.sort(function(ah,ag){if(ah===ag){return 0}var aj=D(af,ah);var ai=D(af,ag);if(aj===ai){return 0}return aj>ai?-1:1});if(aa.length<=1){return aa}var ab=0;var ad=0;var ae=[];var ac;ac=aa[ab++];while(ac){if(ac===aa[ab]){ad=ae.push(ab)}ac=aa[ab++]||null}while(ad--){aa.splice(ae[ad],1) -}return aa},getAttributeValueFromNode:function(ae,ac){if(!this.hasNodeAttribute(ae,ac)){return}if(ae&&ae.getAttribute){return ae.getAttribute(ac)}if(!ae||!ae.attributes){return}var ad=(typeof ae.attributes[ac]);if("undefined"===ad){return}if(ae.attributes[ac].value){return ae.attributes[ac].value}if(ae.attributes[ac].nodeValue){return ae.attributes[ac].nodeValue}var ab;var aa=ae.attributes;if(!aa){return}for(ab=0;ab<aa.length;ab++){if(aa[ab].nodeName===ac){return aa[ab].nodeValue}}return null},hasNodeAttributeWithValue:function(ab,aa){var ac=this.getAttributeValueFromNode(ab,aa);return !!ac},hasNodeAttribute:function(ac,aa){if(ac&&ac.hasAttribute){return ac.hasAttribute(aa)}if(ac&&ac.attributes){var ab=(typeof ac.attributes[aa]);return"undefined"!==ab}return false},hasNodeCssClass:function(ac,aa){if(ac&&aa&&ac.className){var ab=typeof ac.className==="string"?ac.className.split(" "):[];if(-1!==D(ab,aa)){return true}}return false},findNodesHavingAttribute:function(ae,ac,aa){if(!aa){aa=[]}if(!ae||!ac){return aa -}var ad=G(ae);if(!ad||!ad.length){return aa}var ab,af;for(ab=0;ab<ad.length;ab++){af=ad[ab];if(this.hasNodeAttribute(af,ac)){aa.push(af)}aa=this.findNodesHavingAttribute(af,ac,aa)}return aa},findFirstNodeHavingAttribute:function(ac,ab){if(!ac||!ab){return}if(this.hasNodeAttribute(ac,ab)){return ac}var aa=this.findNodesHavingAttribute(ac,ab);if(aa&&aa.length){return aa[0]}},findFirstNodeHavingAttributeWithValue:function(ad,ac){if(!ad||!ac){return}if(this.hasNodeAttributeWithValue(ad,ac)){return ad}var aa=this.findNodesHavingAttribute(ad,ac);if(!aa||!aa.length){return}var ab;for(ab=0;ab<aa.length;ab++){if(this.getAttributeValueFromNode(aa[ab],ac)){return aa[ab]}}},findNodesHavingCssClass:function(ae,ad,aa){if(!aa){aa=[]}if(!ae||!ad){return aa}if(ae.getElementsByClassName){var af=ae.getElementsByClassName(ad);return this.htmlCollectionToArray(af)}var ac=G(ae);if(!ac||!ac.length){return[]}var ab,ag;for(ab=0;ab<ac.length;ab++){ag=ac[ab];if(this.hasNodeCssClass(ag,ad)){aa.push(ag)}aa=this.findNodesHavingCssClass(ag,ad,aa) -}return aa},findFirstNodeHavingClass:function(ac,ab){if(!ac||!ab){return}if(this.hasNodeCssClass(ac,ab)){return ac}var aa=this.findNodesHavingCssClass(ac,ab);if(aa&&aa.length){return aa[0]}},isLinkElement:function(ab){if(!ab){return false}var aa=String(ab.nodeName).toLowerCase();var ad=["a","area"];var ac=D(ad,aa);return ac!==-1},setAnyAttribute:function(ab,aa,ac){if(!ab||!aa){return}if(ab.setAttribute){ab.setAttribute(aa,ac)}else{ab[aa]=ac}}};var p={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 ab="."+this.CONTENT_CLASS;var aa="["+this.CONTENT_ATTR+"]";var ac=S.findMultiple([ab,aa]);return ac -},findContentNodesWithinNode:function(ad){if(!ad){return[]}var ab=S.findNodesHavingCssClass(ad,this.CONTENT_CLASS);var aa=S.findNodesHavingAttribute(ad,this.CONTENT_ATTR);if(aa&&aa.length){var ac;for(ac=0;ac<aa.length;ac++){ab.push(aa[ac])}}if(S.hasNodeAttribute(ad,this.CONTENT_ATTR)){ab.push(ad)}else{if(S.hasNodeCssClass(ad,this.CONTENT_CLASS)){ab.push(ad)}}ab=S.makeNodesUnique(ab);return ab},findParentContentNode:function(ab){if(!ab){return}var ac=ab;var aa=0;while(ac&&ac!==x&&ac.parentNode){if(S.hasNodeAttribute(ac,this.CONTENT_ATTR)){return ac}if(S.hasNodeCssClass(ac,this.CONTENT_CLASS)){return ac}ac=ac.parentNode;if(aa>1000){break}aa++}},findPieceNode:function(ab){var aa;aa=S.findFirstNodeHavingAttribute(ab,this.CONTENT_PIECE_ATTR);if(!aa){aa=S.findFirstNodeHavingClass(ab,this.CONTENT_PIECE_CLASS)}if(aa){return aa}return ab},findTargetNodeNoDefault:function(aa){if(!aa){return}var ab=S.findFirstNodeHavingAttributeWithValue(aa,this.CONTENT_TARGET_ATTR);if(ab){return ab}ab=S.findFirstNodeHavingAttribute(aa,this.CONTENT_TARGET_ATTR); -if(ab){return ab}ab=S.findFirstNodeHavingClass(aa,this.CONTENT_TARGET_CLASS);if(ab){return ab}},findTargetNode:function(aa){var ab=this.findTargetNodeNoDefault(aa);if(ab){return ab}return aa},findContentName:function(ab){if(!ab){return}var ae=S.findFirstNodeHavingAttributeWithValue(ab,this.CONTENT_NAME_ATTR);if(ae){return S.getAttributeValueFromNode(ae,this.CONTENT_NAME_ATTR)}var aa=this.findContentPiece(ab);if(aa){return this.removeDomainIfIsInLink(aa)}if(S.hasNodeAttributeWithValue(ab,"title")){return S.getAttributeValueFromNode(ab,"title")}var ac=this.findPieceNode(ab);if(S.hasNodeAttributeWithValue(ac,"title")){return S.getAttributeValueFromNode(ac,"title")}var ad=this.findTargetNode(ab);if(S.hasNodeAttributeWithValue(ad,"title")){return S.getAttributeValueFromNode(ad,"title")}},findContentPiece:function(ab){if(!ab){return}var ad=S.findFirstNodeHavingAttributeWithValue(ab,this.CONTENT_PIECE_ATTR);if(ad){return S.getAttributeValueFromNode(ad,this.CONTENT_PIECE_ATTR)}var aa=this.findPieceNode(ab); -var ac=this.findMediaUrlInNode(aa);if(ac){return this.toAbsoluteUrl(ac)}},findContentTarget:function(ac){if(!ac){return}var ad=this.findTargetNode(ac);if(S.hasNodeAttributeWithValue(ad,this.CONTENT_TARGET_ATTR)){return S.getAttributeValueFromNode(ad,this.CONTENT_TARGET_ATTR)}var ab;if(S.hasNodeAttributeWithValue(ad,"href")){ab=S.getAttributeValueFromNode(ad,"href");return this.toAbsoluteUrl(ab)}var aa=this.findPieceNode(ac);if(S.hasNodeAttributeWithValue(aa,"href")){ab=S.getAttributeValueFromNode(aa,"href");return this.toAbsoluteUrl(ab)}},isSameDomain:function(aa){if(!aa||!aa.indexOf){return false}if(0===aa.indexOf(this.getLocation().origin)){return true}var ab=aa.indexOf(this.getLocation().host);if(8>=ab&&0<=ab){return true}return false},removeDomainIfIsInLink:function(ac){var ab="^https?://[^/]+";var aa="^.*//[^/]+";if(ac&&ac.search&&-1!==ac.search(new RegExp(ab))&&this.isSameDomain(ac)){ac=ac.replace(new RegExp(aa),"");if(!ac){ac="/"}}return ac},findMediaUrlInNode:function(ae){if(!ae){return -}var ac=["img","embed","video","audio"];var aa=ae.nodeName.toLowerCase();if(-1!==D(ac,aa)&&S.findFirstNodeHavingAttributeWithValue(ae,"src")){var ad=S.findFirstNodeHavingAttributeWithValue(ae,"src");return S.getAttributeValueFromNode(ad,"src")}if(aa==="object"&&S.hasNodeAttributeWithValue(ae,"data")){return S.getAttributeValueFromNode(ae,"data")}if(aa==="object"){var af=S.findNodesByTagName(ae,"param");if(af&&af.length){var ab;for(ab=0;ab<af.length;ab++){if("movie"===S.getAttributeValueFromNode(af[ab],"name")&&S.hasNodeAttributeWithValue(af[ab],"value")){return S.getAttributeValueFromNode(af[ab],"value")}}}var ag=S.findNodesByTagName(ae,"embed");if(ag&&ag.length){return this.findMediaUrlInNode(ag[0])}}},trim:function(aa){if(aa&&String(aa)===aa){return aa.replace(/^\s+|\s+$/g,"")}return aa},isOrWasNodeInViewport:function(af){if(!af||!af.getBoundingClientRect||af.nodeType!==1){return true}var ae=af.getBoundingClientRect();var ad=x.documentElement||{};var ac=ae.top<0;if(ac&&af.offsetTop){ac=(af.offsetTop+ae.height)>0 -}var ab=ad.clientWidth;if(K.innerWidth&&ab>K.innerWidth){ab=K.innerWidth}var aa=ad.clientHeight;if(K.innerHeight&&aa>K.innerHeight){aa=K.innerHeight}return((ae.bottom>0||ac)&&ae.right>0&&ae.left<ab&&((ae.top<aa)||ac))},isNodeVisible:function(ab){var aa=h(ab);var ac=this.isOrWasNodeInViewport(ab);return aa&&ac},buildInteractionRequestParams:function(aa,ab,ac,ad){var ae="";if(aa){ae+="c_i="+n(aa)}if(ab){if(ae){ae+="&"}ae+="c_n="+n(ab)}if(ac){if(ae){ae+="&"}ae+="c_p="+n(ac)}if(ad){if(ae){ae+="&"}ae+="c_t="+n(ad)}return ae},buildImpressionRequestParams:function(aa,ab,ac){var ad="c_n="+n(aa)+"&c_p="+n(ab);if(ac){ad+="&c_t="+n(ac)}return ad},buildContentBlock:function(ac){if(!ac){return}var aa=this.findContentName(ac);var ab=this.findContentPiece(ac);var ad=this.findContentTarget(ac);aa=this.trim(aa);ab=this.trim(ab);ad=this.trim(ad);return{name:aa||"Unknown",piece:ab||"Unknown",target:ad||""}},collectContent:function(ad){if(!ad||!ad.length){return[]}var ac=[];var aa,ab;for(aa=0;aa<ad.length; -aa++){ab=this.buildContentBlock(ad[aa]);if(A(ab)){ac.push(ab)}}return ac},setLocation:function(aa){this.location=aa},getLocation:function(){var aa=this.location||K.location;if(!aa.origin){aa.origin=aa.protocol+"//"+aa.hostname+(aa.port?":"+aa.port:"")}return aa},toAbsoluteUrl:function(ab){if((!ab||String(ab)!==ab)&&ab!==""){return ab}if(""===ab){return this.getLocation().href}if(ab.search(/^\/\//)!==-1){return this.getLocation().protocol+ab}if(ab.search(/:\/\//)!==-1){return ab}if(0===ab.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+ab}if(0===ab.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+ab}if(0===ab.search("^[a-zA-Z]{2,11}:")){return ab}if(ab.search(/^\//)!==-1){return this.getLocation().origin+ab}var aa="(.*/)";var ac=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(aa))[0];return ac+ab},isUrlToCurrentDomain:function(ab){var ac=this.toAbsoluteUrl(ab);if(!ac){return false}var aa=this.getLocation().origin; -if(aa===ac){return true}if(0===String(ac).indexOf(aa)){if(":"===String(ac).substr(aa.length,1)){return false}return true}return false},setHrefAttribute:function(ab,aa){if(!ab||!aa){return}S.setAnyAttribute(ab,"href",aa)},shouldIgnoreInteraction:function(ac){var ab=S.hasNodeAttribute(ac,this.CONTENT_IGNOREINTERACTION_ATTR);var aa=S.hasNodeCssClass(ac,this.CONTENT_IGNOREINTERACTION_CLASS);return ab||aa}};function F(ab,ae){if(ae){return ae}if(r(ab,"?")){var ad=ab.indexOf("?");ab=ab.slice(0,ad)}if(I(ab,"piwik.php")){ab=e(ab,"piwik.php".length)}else{if(I(ab,".php")){var aa=ab.lastIndexOf("/");var ac=1;ab=ab.slice(0,aa+ac)}}if(I(ab,"/js/")){ab=e(ab,"js/".length)}return ab}function E(ag){var ai="Piwik_Overlay";var ab=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$");var ac=ab.exec(x.referrer);if(ac){var ae=ac[1];if(ae!==String(ag)){return false}var af=ac[2],aa=ac[3],ad=ac[4];if(!ad){ad=""}else{if(ad.indexOf("&segment=")===0){ad=ad.substr("&segment=".length) -}}K.name=ai+"###"+af+"###"+aa+"###"+ad}var ah=K.name.split("###");return ah.length===4&&ah[0]===ai}function P(ab,ah,ad){var ag=K.name.split("###"),af=ag[1],aa=ag[2],ae=ag[3],ac=F(ab,ah);j(ac+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(ac,ad,af,aa,ae)})}function o(){var ac;try{ac=K.frameElement}catch(ab){return true}if(A(ac)){return(ac&&String(ac.nodeName).toLowerCase()==="iframe")?true:false}try{return K.self!==K.top}catch(aa){return true}}function H(bI,bC){var by=Q(x.domain,K.location.href,B()),ch=C(by[0]),bi=k(by[1]),aX=k(by[2]),cf=false,bM="GET",ct=bM,ao="application/x-www-form-urlencoded; charset=UTF-8",bY=ao,ak=bI||"",bd="",cl="",bA=bC||"",a6="",bj="",aI,aT="",cq=["7z","aac","apk","arc","arj","asf","asx","avi","azw3","bin","csv","deb","dmg","doc","docx","epub","exe","flv","gif","gz","gzip","hqx","ibooks","jar","jpg","jpeg","js","mobi","mp2","mp3","mp4","mpg","mpeg","mov","movie","msi","msp","odb","odf","odg","ods","odt","ogg","ogv","pdf","phps","png","ppt","pptx","qt","qtm","ra","ram","rar","rpm","sea","sit","tar","tbz","tbz2","bz","bz2","tgz","torrent","txt","wav","wma","wmv","wpd","xls","xlsx","xml","z","zip"],ag=[ch],a7=[],bg=[],aL=[],be=500,b8,aJ,bm,bk,aa,bU=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],bc=["pk_kwd","piwik_kwd","utm_term"],aU="_pk_",cj,aZ,aV=false,cd,aR,a3,b9=33955200000,bS=1800000,cp=15768000000,aG=true,bQ=0,bl=false,av=false,bF,bq={},bP={},aW={},a2=200,cm={},cr={},bE=[],bJ=false,b2=false,ab=false,cs=false,at=false,aQ=o(),ck=null,bG,aw,a8,bB=X,aY; -try{aT=x.title}catch(b0){aT=""}function cw(cG,cD,cC,cF,cB,cE){if(aV){return}var cA;if(cC){cA=new Date();cA.setTime(cA.getTime()+cC)}x.cookie=cG+"="+n(cD)+(cC?";expires="+cA.toGMTString():"")+";path="+(cF||"/")+(cB?";domain="+cB:"")+(cE?";secure":"")}function aj(cC){if(aV){return 0}var cA=new RegExp("(^|;)[ ]*"+cC+"=([^;]*)"),cB=cA.exec(x.cookie);return cB?J(cB[2]):0}function bw(cA){var cB;if(bk){cB=new RegExp("#.*");return cA.replace(cB,"")}return cA}function bp(cC,cA){var cD=m(cA),cB;if(cD){return cA}if(cA.slice(0,1)==="/"){return m(cC)+"://"+c(cC)+cA}cC=bw(cC);cB=cC.indexOf("?");if(cB>=0){cC=cC.slice(0,cB)}cB=cC.lastIndexOf("/");if(cB!==cC.length-1){cC=cC.slice(0,cB+1)}return cC+cA}function b6(cC,cA){var cB;cC=String(cC).toLowerCase();cA=String(cA).toLowerCase();if(cC===cA){return true}if(cA.slice(0,1)==="."){if(cC===cA.slice(1)){return true}cB=cC.length-cA.length;if((cB>0)&&(cC.slice(cB)===cA)){return true}}return false}function bO(cA){var cB=document.createElement("a");if(cA.indexOf("//")!==0&&cA.indexOf("http")!==0){if(cA.indexOf("*")===0){cA=cA.substr(1) -}if(cA.indexOf(".")===0){cA=cA.substr(1)}cA="http://"+cA}cB.href=p.toAbsoluteUrl(cA);if(cB.pathname){return cB.pathname}return""}function aH(cB,cA){if(!V(cA,"/")){cA="/"+cA}if(!V(cB,"/")){cB="/"+cB}var cC=(cA==="/"||cA==="/*");if(cC){return true}if(cB===cA){return true}cA=String(cA).toLowerCase();cB=String(cB).toLowerCase();if(I(cA,"*")){cA=cA.slice(0,-1);cC=(!cA||cA==="/");if(cC){return true}if(cB===cA){return true}return cB.indexOf(cA)===0}if(!I(cB,"/")){cB+="/"}if(!I(cA,"/")){cA+="/"}return cB.indexOf(cA)===0}function ad(cE,cG){var cB,cA,cC,cD,cF;for(cB=0;cB<ag.length;cB++){cD=C(ag[cB]);cF=bO(ag[cB]);if(b6(cE,cD)&&aH(cG,cF)){return true}}return false}function aA(cD){var cB,cA,cC;for(cB=0;cB<ag.length;cB++){cA=C(ag[cB].toLowerCase());if(cD===cA){return true}if(cA.slice(0,1)==="."){if(cD===cA.slice(1)){return true}cC=cD.length-cA.length;if((cC>0)&&(cD.slice(cC)===cA)){return true}}}return false}function bT(cA,cC){var cB=new Image(1,1);cB.onload=function(){w=0;if(typeof cC==="function"){cC() -}};cA=cA.replace("send_image=0","send_image=1");cB.src=ak+(ak.indexOf("?")<0?"?":"&")+cA}function co(cB,cE,cA){if(!A(cA)||null===cA){cA=true}try{var cD=K.XMLHttpRequest?new K.XMLHttpRequest():K.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cD.open("POST",ak,true);cD.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cA){bT(cB,cE)}else{if(this.readyState===4&&(typeof cE==="function")){cE()}}};cD.setRequestHeader("Content-Type",bY);cD.send(cB)}catch(cC){if(cA){bT(cB,cE)}}}function bK(cB){var cA=new Date();var cC=cA.getTime()+cB;if(!l||cC>l){l=cC}}function bR(cA){if(bG||!aJ){return}bG=setTimeout(function cB(){bG=null;if(!aQ){aQ=(!x.hasFocus||x.hasFocus())}if(!aQ){bR(aJ);return}if(bm()){return}var cC=new Date(),cD=aJ-(cC.getTime()-ck);cD=Math.min(aJ,cD);bR(cD)},cA||aJ)}function bf(){if(!bG){return}clearTimeout(bG);bG=null}function aN(){aQ=true;if(bm()){return}bR()}function ah(){bf()}function cy(){if(at||!aJ){return}at=true;Y(K,"focus",aN); -Y(K,"blur",ah);bR()}function b3(cE){var cB=new Date();var cA=cB.getTime();ck=cA;if(b2&&cA<b2){var cC=b2-cA;setTimeout(cE,cC);bK(cC+50);b2+=50;return}if(b2===false){var cD=800;b2=cA+cD}cE()}function bb(cB,cA,cC){if(!cd&&cB){b3(function(){if(ct==="POST"){co(cB,cC)}else{bT(cB,cC)}bK(cA)})}if(!at){cy()}else{bR()}}function bN(cA){if(cd){return false}return(cA&&cA.length)}function cx(cC,cA){if(!bN(cC)){return}var cB='{"requests":["?'+cC.join('","?')+'"]}';b3(function(){co(cB,null,false);bK(cA)})}function ay(cA){return aU+cA+"."+bA+"."+aY}function bz(){if(aV){return"0"}if(!A(f.cookieEnabled)){var cA=ay("testcookie");cw(cA,"1");return aj(cA)==="1"?"1":"0"}return f.cookieEnabled?"1":"0"}function aS(){aY=bB((cj||ch)+(aZ||"/")).slice(0,4)}function br(){var cB=ay("cvar"),cA=aj(cB);if(cA.length){cA=JSON2.parse(cA);if(N(cA)){return cA}}return{}}function b4(){if(av===false){av=br()}}function ce(){return bB((f.userAgent||"")+(f.platform||"")+JSON2.stringify(cr)+(new Date()).getTime()+Math.random()).slice(0,16) -}function cb(){var cC=new Date(),cA=Math.round(cC.getTime()/1000),cB=ay("id"),cF=aj(cB),cE,cD;if(cF){cE=cF.split(".");cE.unshift("0");if(bj.length){cE[1]=bj}return cE}if(bj.length){cD=bj}else{if("0"===bz()){cD=""}else{cD=ce()}}cE=["1",cD,cA,0,cA,"",""];return cE}function aC(){var cH=cb(),cD=cH[0],cE=cH[1],cB=cH[2],cA=cH[3],cF=cH[4],cC=cH[5];if(!A(cH[6])){cH[6]=""}var cG=cH[6];return{newVisitor:cD,uuid:cE,createTs:cB,visitCount:cA,currentVisitTs:cF,lastVisitTs:cC,lastEcommerceOrderTs:cG}}function an(){var cD=new Date(),cB=cD.getTime(),cE=aC().createTs;var cA=parseInt(cE,10);var cC=(cA*1000)+b9-cB;return cC}function aq(cA){if(!bA){return}var cC=new Date(),cB=Math.round(cC.getTime()/1000);if(!A(cA)){cA=aC()}var cD=cA.uuid+"."+cA.createTs+"."+cA.visitCount+"."+cB+"."+cA.lastVisitTs+"."+cA.lastEcommerceOrderTs;cw(ay("id"),cD,an(),aZ,cj)}function bh(){var cA=aj(ay("ref"));if(cA.length){try{cA=JSON2.parse(cA);if(N(cA)){return cA}}catch(cB){}}return["","",0,""]}function bs(cC,cB,cA){cw(cC,"",-86400,cB,cA) -}function a4(cB){var cA="testvalue";cw("test",cA,10000,null,cB);if(aj("test")===cA){bs("test",null,cB);return true}return false}function al(){var cC=aV;aV=false;var cA=["id","ses","cvar","ref"];var cB,cD;for(cB=0;cB<cA.length;cB++){cD=ay(cA[cB]);if(0!==aj(cD)){bs(cD,aZ,cj)}}aV=cC}function bx(cA){bA=cA;aq()}function cz(cE){if(!cE||!N(cE)){return}var cD=[];var cC;for(cC in cE){if(Object.prototype.hasOwnProperty.call(cE,cC)){cD.push(cC)}}var cF={};cD.sort();var cA=cD.length;var cB;for(cB=0;cB<cA;cB++){cF[cD[cB]]=cE[cD[cB]]}return cF}function bH(){cw(ay("ses"),"*",bS,aZ,cj)}function bV(cC,cX,cY,cD){var cW,cB=new Date(),cK=Math.round(cB.getTime()/1000),cH,cV,cE=1024,c3,cL,cT=av,cF=ay("ses"),cR=ay("ref"),cO=ay("cvar"),cP=aj(cF),cU=bh(),c0=aI||bi,cI,cA;if(aV){al()}if(cd){return""}var cQ=aC();if(!A(cD)){cD=""}var cN=x.characterSet||x.charset;if(!cN||cN.toLowerCase()==="utf-8"){cN=null}cI=cU[0];cA=cU[1];cH=cU[2];cV=cU[3];if(!cP){var cZ=bS/1000;if(!cQ.lastVisitTs||(cK-cQ.lastVisitTs)>cZ){cQ.visitCount++; -cQ.lastVisitTs=cQ.currentVisitTs}if(!a3||!cI.length){for(cW in bU){if(Object.prototype.hasOwnProperty.call(bU,cW)){cI=M(c0,bU[cW]);if(cI.length){break}}}for(cW in bc){if(Object.prototype.hasOwnProperty.call(bc,cW)){cA=M(c0,bc[cW]);if(cA.length){break}}}}c3=c(aX);cL=cV.length?c(cV):"";if(c3.length&&!aA(c3)&&(!a3||!cL.length||aA(cL))){cV=aX}if(cV.length||cI.length){cH=cK;cU=[cI,cA,cH,bw(cV.slice(0,cE))];cw(cR,JSON2.stringify(cU),cp,aZ,cj)}}cC+="&idsite="+bA+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cB.getHours()+"&m="+cB.getMinutes()+"&s="+cB.getSeconds()+"&url="+n(bw(c0))+(aX.length?"&urlref="+n(bw(aX)):"")+((a6&&a6.length)?"&uid="+n(a6):"")+"&_id="+cQ.uuid+"&_idts="+cQ.createTs+"&_idvc="+cQ.visitCount+"&_idn="+cQ.newVisitor+(cI.length?"&_rcn="+n(cI):"")+(cA.length?"&_rck="+n(cA):"")+"&_refts="+cH+"&_viewts="+cQ.lastVisitTs+(String(cQ.lastEcommerceOrderTs).length?"&_ects="+cQ.lastEcommerceOrderTs:"")+(String(cV).length?"&_ref="+n(bw(cV.slice(0,cE))):"")+(cN?"&cs="+n(cN):"")+"&send_image=0"; -for(cW in cr){if(Object.prototype.hasOwnProperty.call(cr,cW)){cC+="&"+cW+"="+cr[cW]}}var c2=[];if(cX){for(cW in cX){if(Object.prototype.hasOwnProperty.call(cX,cW)&&/^dimension\d+$/.test(cW)){var cG=cW.replace("dimension","");c2.push(parseInt(cG,10));c2.push(String(cG));cC+="&"+cW+"="+cX[cW];delete cX[cW]}}}if(cX&&t(cX)){cX=null}for(cW in aW){if(Object.prototype.hasOwnProperty.call(aW,cW)){var cM=(-1===D(c2,cW));if(cM){cC+="&dimension"+cW+"="+aW[cW]}}}if(cX){cC+="&data="+n(JSON2.stringify(cX))}else{if(aa){cC+="&data="+n(JSON2.stringify(aa))}}function cJ(c4,c5){var c6=JSON2.stringify(c4);if(c6.length>2){return"&"+c5+"="+n(c6)}return""}var c1=cz(bq);var cS=cz(bP);cC+=cJ(c1,"cvar");cC+=cJ(cS,"e_cvar");if(av){cC+=cJ(av,"_cvar");for(cW in cT){if(Object.prototype.hasOwnProperty.call(cT,cW)){if(av[cW][0]===""||av[cW][1]===""){delete av[cW]}}}if(bl){cw(cO,JSON2.stringify(av),bS,aZ,cj)}}if(aG){if(bQ){cC+=">_ms="+bQ}else{if(g&&g.timing&&g.timing.requestStart&&g.timing.responseEnd){cC+=">_ms="+(g.timing.responseEnd-g.timing.requestStart) -}}}cQ.lastEcommerceOrderTs=A(cD)&&String(cD).length?cD:cQ.lastEcommerceOrderTs;aq(cQ);bH();cC+=R(cY);if(cl.length){cC+="&"+cl}if(s(bF)){cC=bF(cC)}return cC}bm=function aK(){var cA=new Date();if(ck+aJ<=cA.getTime()){var cB=bV("ping=1",null,"ping");bb(cB,be);return true}return false};function a0(cD,cC,cI,cE,cA,cL){var cG="idgoal=0",cH,cB=new Date(),cJ=[],cK,cF=String(cD).length;if(cF){cG+="&ec_id="+n(cD);cH=Math.round(cB.getTime()/1000)}cG+="&revenue="+cC;if(String(cI).length){cG+="&ec_st="+cI}if(String(cE).length){cG+="&ec_tx="+cE}if(String(cA).length){cG+="&ec_sh="+cA}if(String(cL).length){cG+="&ec_dt="+cL}if(cm){for(cK in cm){if(Object.prototype.hasOwnProperty.call(cm,cK)){if(!A(cm[cK][1])){cm[cK][1]=""}if(!A(cm[cK][2])){cm[cK][2]=""}if(!A(cm[cK][3])||String(cm[cK][3]).length===0){cm[cK][3]=0}if(!A(cm[cK][4])||String(cm[cK][4]).length===0){cm[cK][4]=1}cJ.push(cm[cK])}}cG+="&ec_items="+n(JSON2.stringify(cJ))}cG=bV(cG,aa,"ecommerce",cH);bb(cG,be);if(cF){cm={}}}function bt(cA,cE,cD,cC,cB,cF){if(String(cA).length&&A(cE)){a0(cA,cE,cD,cC,cB,cF) -}}function a1(cA){if(A(cA)){a0("",cA,"","","","")}}function bu(cB,cD,cC){var cA=bV("action_name="+n(Z(cB||aT)),cD,"log");bb(cA,be,cC)}function aE(cC,cB){var cD,cA="(^| )(piwik[_-]"+cB;if(cC){for(cD=0;cD<cC.length;cD++){cA+="|"+cC[cD]}}cA+=")( |$)";return new RegExp(cA)}function az(cA){return(ak&&cA&&0===String(cA).indexOf(ak))}function bW(cE,cA,cF,cB){if(az(cA)){return 0}var cD=aE(bg,"download"),cC=aE(aL,"link"),cG=new RegExp("\\.("+cq.join("|")+")([?&#]|$)","i");if(cC.test(cE)){return"link"}if(cB||cD.test(cE)||cG.test(cA)){return"download"}if(cF){return 0}return"link"}function ae(cB){var cA;cA=cB.parentNode;while(cA!==null&&A(cA)){if(S.isLinkElement(cB)){break}cB=cA;cA=cB.parentNode}return cB}function cu(cF){cF=ae(cF);if(!S.hasNodeAttribute(cF,"href")){return}if(!A(cF.href)){return}var cE=S.getAttributeValueFromNode(cF,"href");if(az(cE)){return}var cB=cF.pathname||bO(cF.href);var cG=cF.hostname||c(cF.href);var cH=cG.toLowerCase();var cC=cF.href.replace(cG,cH);var cD=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto|tel):","i"); -if(!cD.test(cC)){var cA=bW(cF.className,cC,ad(cH,cB),S.hasNodeAttribute(cF,"download"));if(cA){return{type:cA,href:cC}}}}function au(cA,cB,cC,cD){var cE=p.buildInteractionRequestParams(cA,cB,cC,cD);if(!cE){return}return bV(cE,null,"contentInteraction")}function ca(cC,cD,cH,cA,cB){if(!A(cC)){return}if(az(cC)){return cC}var cF=p.toAbsoluteUrl(cC);var cE="redirecturl="+n(cF)+"&";cE+=au(cD,cH,cA,(cB||cC));var cG="&";if(ak.indexOf("?")<0){cG="?"}return ak+cG+cE}function aO(cA,cB){if(!cA||!cB){return false}var cC=p.findTargetNode(cA);if(p.shouldIgnoreInteraction(cC)){return false}cC=p.findTargetNodeNoDefault(cA);if(cC&&!L(cC,cB)){return false}return true}function bX(cC,cB,cE){if(!cC){return}var cA=p.findParentContentNode(cC);if(!cA){return}if(!aO(cA,cC)){return}var cD=p.buildContentBlock(cA);if(!cD){return}if(!cD.target&&cE){cD.target=cE}return p.buildInteractionRequestParams(cB,cD.name,cD.piece,cD.target)}function aB(cB){if(!bE||!bE.length){return false}var cA,cC;for(cA=0;cA<bE.length;cA++){cC=bE[cA]; -if(cC&&cC.name===cB.name&&cC.piece===cB.piece&&cC.target===cB.target){return true}}return false}function ba(cD){if(!cD){return false}var cG=p.findTargetNode(cD);if(!cG||p.shouldIgnoreInteraction(cG)){return false}var cH=cu(cG);if(cs&&cH&&cH.type){return false}if(S.isLinkElement(cG)&&S.hasNodeAttributeWithValue(cG,"href")){var cA=String(S.getAttributeValueFromNode(cG,"href"));if(0===cA.indexOf("#")){return false}if(az(cA)){return true}if(!p.isUrlToCurrentDomain(cA)){return false}var cE=p.buildContentBlock(cD);if(!cE){return}var cC=cE.name;var cI=cE.piece;var cF=cE.target;if(!S.hasNodeAttributeWithValue(cG,p.CONTENT_TARGET_ATTR)||cG.wasContentTargetAttrReplaced){cG.wasContentTargetAttrReplaced=true;cF=p.toAbsoluteUrl(cA);S.setAnyAttribute(cG,p.CONTENT_TARGET_ATTR,cF)}var cB=ca(cA,"click",cC,cI,cF);p.setHrefAttribute(cG,cB);return true}return false}function ar(cB){if(!cB||!cB.length){return}var cA;for(cA=0;cA<cB.length;cA++){ba(cB[cA])}}function aD(cA){return function(cB){if(!cA){return}var cE=p.findParentContentNode(cA); -var cF;if(cB){cF=cB.target||cB.srcElement}if(!cF){cF=cA}if(!aO(cE,cF)){return}bK(be);if(S.isLinkElement(cA)&&S.hasNodeAttributeWithValue(cA,"href")&&S.hasNodeAttributeWithValue(cA,p.CONTENT_TARGET_ATTR)){var cC=S.getAttributeValueFromNode(cA,"href");if(!az(cC)&&cA.wasContentTargetAttrReplaced){S.setAnyAttribute(cA,p.CONTENT_TARGET_ATTR,"")}}var cJ=cu(cA);if(ab&&cJ&&cJ.type){return cJ.type}if(ba(cE)){return"href"}var cG=p.buildContentBlock(cE);if(!cG){return}var cD=cG.name;var cK=cG.piece;var cI=cG.target;var cH=au("click",cD,cK,cI);bb(cH,be);return cH}}function bv(cC){if(!cC||!cC.length){return}var cA,cB;for(cA=0;cA<cC.length;cA++){cB=p.findTargetNode(cC[cA]);if(cB&&!cB.contentInteractionTrackingSetupDone){cB.contentInteractionTrackingSetupDone=true;Y(cB,"click",aD(cB))}}}function a5(cC,cD){if(!cC||!cC.length){return[]}var cA,cB;for(cA=0;cA<cC.length;cA++){if(aB(cC[cA])){cC.splice(cA,1);cA--}else{bE.push(cC[cA])}}if(!cC||!cC.length){return[]}ar(cD);bv(cD);var cE=[];for(cA=0;cA<cC.length; -cA++){cB=bV(p.buildImpressionRequestParams(cC[cA].name,cC[cA].piece,cC[cA].target),undefined,"contentImpressions");if(cB){cE.push(cB)}}return cE}function b1(cB){var cA=p.collectContent(cB);return a5(cA,cB)}function aM(cB){if(!cB||!cB.length){return[]}var cA;for(cA=0;cA<cB.length;cA++){if(!p.isNodeVisible(cB[cA])){cB.splice(cA,1);cA--}}if(!cB||!cB.length){return[]}return b1(cB)}function am(cC,cA,cB){var cD=p.buildImpressionRequestParams(cC,cA,cB);return bV(cD,null,"contentImpression")}function cv(cD,cB){if(!cD){return}var cA=p.findParentContentNode(cD);var cC=p.buildContentBlock(cA);if(!cC){return}if(!cB){cB="Unknown"}return au(cB,cC.name,cC.piece,cC.target)}function cc(cB,cD,cA,cC){return"e_c="+n(cB)+"&e_a="+n(cD)+(A(cA)?"&e_n="+n(cA):"")+(A(cC)?"&e_v="+n(cC):"")}function af(cC,cE,cA,cD,cF){if(String(cC).length===0||String(cE).length===0){return false}var cB=bV(cc(cC,cE,cA,cD),cF,"event");bb(cB,be)}function bD(cA,cD,cB,cE){var cC=bV("search="+n(cA)+(cD?"&search_cat="+n(cD):"")+(A(cB)?"&search_count="+cB:""),cE,"sitesearch"); -bb(cC,be)}function cg(cA,cD,cC){var cB=bV("idgoal="+cA+(cD?"&revenue="+cD:""),cC,"goal");bb(cB,be)}function cn(cD,cA,cH,cG,cC){var cF=cA+"="+n(bw(cD));var cB=bX(cC,"click",cD);if(cB){cF+="&"+cB}var cE=bV(cF,cH,"link");bb(cE,(cG?0:be),cG)}function bn(cB,cA){if(cB!==""){return cB+cA.charAt(0).toUpperCase()+cA.slice(1)}return cA}function bL(cF){var cE,cA,cD=["","webkit","ms","moz"],cC;if(!aR){for(cA=0;cA<cD.length;cA++){cC=cD[cA];if(Object.prototype.hasOwnProperty.call(x,bn(cC,"hidden"))){if(x[bn(cC,"visibilityState")]==="prerender"){cE=true}break}}}if(cE){Y(x,cC+"visibilitychange",function cB(){x.removeEventListener(cC+"visibilitychange",cB,false);cF()});return}cF()}function ap(cA){if(x.readyState==="complete"){cA()}else{if(K.addEventListener){K.addEventListener("load",cA)}else{if(K.attachEvent){K.attachEvent("onload",cA)}}}}function aP(cD){var cA=false;if(x.attachEvent){cA=x.readyState==="complete"}else{cA=x.readyState!=="loading"}if(cA){cD();return}var cC;if(x.addEventListener){Y(x,"DOMContentLoaded",function cB(){x.removeEventListener("DOMContentLoaded",cB,false); -if(!cA){cA=true;cD()}})}else{if(x.attachEvent){x.attachEvent("onreadystatechange",function cB(){if(x.readyState==="complete"){x.detachEvent("onreadystatechange",cB);if(!cA){cA=true;cD()}}});if(x.documentElement.doScroll&&K===K.top){(function cB(){if(!cA){try{x.documentElement.doScroll("left")}catch(cE){setTimeout(cB,0);return}cA=true;cD()}}())}}}Y(K,"load",function(){if(!cA){cA=true;cD()}},false)}function b7(cA){var cB=cu(cA);if(cB&&cB.type){cB.href=k(cB.href);cn(cB.href,cB.type,undefined,null,cA)}}function bZ(){return x.all&&!x.addEventListener}function ci(cA){var cC=cA.which;var cB=(typeof cA.button);if(!cC&&cB!=="undefined"){if(bZ()){if(cA.button&1){cC=1}else{if(cA.button&2){cC=3}else{if(cA.button&4){cC=2}}}}else{if(cA.button===0||cA.button==="0"){cC=1}else{if(cA.button&1){cC=2}else{if(cA.button&2){cC=3}}}}}return cC}function bo(cA){switch(ci(cA)){case 1:return"left";case 2:return"middle";case 3:return"right"}}function aF(cA){return cA.target||cA.srcElement}function ai(cA){return function(cD){cD=cD||K.event; -var cC=bo(cD);var cE=aF(cD);if(cD.type==="click"){var cB=false;if(cA&&cC==="middle"){cB=true}if(cE&&!cB){b7(cE)}}else{if(cD.type==="mousedown"){if(cC==="middle"&&cE){aw=cC;a8=cE}else{aw=a8=null}}else{if(cD.type==="mouseup"){if(cC===aw&&cE===a8){b7(cE)}aw=a8=null}else{if(cD.type==="contextmenu"){b7(cE)}}}}}}function ac(cB,cA){Y(cB,"click",ai(cA),false);if(cA){Y(cB,"mouseup",ai(cA),false);Y(cB,"mousedown",ai(cA),false);Y(cB,"contextmenu",ai(cA),false)}}function a9(cB){if(!ab){ab=true;var cC,cA=aE(a7,"ignore"),cD=x.links;if(cD){for(cC=0;cC<cD.length;cC++){if(!cA.test(cD[cC].className)){ac(cD[cC],cB)}}}}}function ax(cC,cE,cF){if(bJ){return true}bJ=true;var cG=false;var cD,cB;function cA(){cG=true}ap(function(){function cH(cJ){setTimeout(function(){if(!bJ){return}cG=false;cF.trackVisibleContentImpressions();cH(cJ)},cJ)}function cI(cJ){setTimeout(function(){if(!bJ){return}if(cG){cG=false;cF.trackVisibleContentImpressions()}cI(cJ)},cJ)}if(cC){cD=["scroll","resize"];for(cB=0;cB<cD.length;cB++){if(x.addEventListener){x.addEventListener(cD[cB],cA) -}else{K.attachEvent("on"+cD[cB],cA)}}cI(100)}if(cE&&cE>0){cE=parseInt(cE,10);cH(cE)}})}function b5(){var cB,cD,cE={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"};if(!((new RegExp("MSIE")).test(f.userAgent))){if(f.mimeTypes&&f.mimeTypes.length){for(cB in cE){if(Object.prototype.hasOwnProperty.call(cE,cB)){cD=f.mimeTypes[cE[cB]];cr[cB]=(cD&&cD.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&A(f.javaEnabled)&&f.javaEnabled()){cr.java="1"}if(s(K.GearsFactory)){cr.gears="1"}cr.cookie=bz()}var cC=parseInt(O.width,10);var cA=parseInt(O.height,10);cr.res=parseInt(cC,10)+"x"+parseInt(cA,10)}b5();aS();aq();return{getVisitorId:function(){return aC().uuid},getVisitorInfo:function(){return cb()},getAttributionInfo:function(){return bh()},getAttributionCampaignName:function(){return bh()[0] -},getAttributionCampaignKeyword:function(){return bh()[1]},getAttributionReferrerTimestamp:function(){return bh()[2]},getAttributionReferrerUrl:function(){return bh()[3]},setTrackerUrl:function(cA){ak=cA},getTrackerUrl:function(){return ak},addTracker:function(cA,cC){if(!cC){throw new Error("A siteId must be given to add a new tracker")}if(!A(cA)||null===cA){cA=this.getTrackerUrl()}var cB=new H(cA,cC);z.push(cB);return cB},getSiteId:function(){return bA},setSiteId:function(cA){bx(cA)},setUserId:function(cA){if(!A(cA)||!cA.length){return}a6=cA;bj=bB(a6).substr(0,16)},getUserId:function(){return a6},setCustomData:function(cA,cB){if(N(cA)){aa=cA}else{if(!aa){aa={}}aa[cA]=cB}},getCustomData:function(){return aa},setCustomRequestProcessing:function(cA){bF=cA},appendToTrackingUrl:function(cA){cl=cA},getRequest:function(cA){return bV(cA)},addPlugin:function(cA,cB){a[cA]=cB},setCustomDimension:function(cA,cB){cA=parseInt(cA,10);if(cA>0){if(!A(cB)){cB=""}if(!q(cB)){cB=String(cB)}aW[cA]=cB}},getCustomDimension:function(cA){cA=parseInt(cA,10); -if(cA>0&&Object.prototype.hasOwnProperty.call(aW,cA)){return aW[cA]}},deleteCustomDimension:function(cA){cA=parseInt(cA,10);if(cA>0){delete aW[cA]}},setCustomVariable:function(cB,cA,cE,cC){var cD;if(!A(cC)){cC="visit"}if(!A(cA)){return}if(!A(cE)){cE=""}if(cB>0){cA=!q(cA)?String(cA):cA;cE=!q(cE)?String(cE):cE;cD=[cA.slice(0,a2),cE.slice(0,a2)];if(cC==="visit"||cC===2){b4();av[cB]=cD}else{if(cC==="page"||cC===3){bq[cB]=cD}else{if(cC==="event"){bP[cB]=cD}}}}},getCustomVariable:function(cB,cC){var cA;if(!A(cC)){cC="visit"}if(cC==="page"||cC===3){cA=bq[cB]}else{if(cC==="event"){cA=bP[cB]}else{if(cC==="visit"||cC===2){b4();cA=av[cB]}}}if(!A(cA)||(cA&&cA[0]==="")){return false}return cA},deleteCustomVariable:function(cA,cB){if(this.getCustomVariable(cA,cB)){this.setCustomVariable(cA,"","",cB)}},storeCustomVariablesInCookie:function(){bl=true},setLinkTrackingTimer:function(cA){be=cA},setDownloadExtensions:function(cA){if(q(cA)){cA=cA.split("|")}cq=cA},addDownloadExtensions:function(cB){var cA;if(q(cB)){cB=cB.split("|") -}for(cA=0;cA<cB.length;cA++){cq.push(cB[cA])}},removeDownloadExtensions:function(cC){var cB,cA=[];if(q(cC)){cC=cC.split("|")}for(cB=0;cB<cq.length;cB++){if(D(cC,cq[cB])===-1){cA.push(cq[cB])}}cq=cA},setDomains:function(cA){ag=q(cA)?[cA]:cA;var cE=false,cC=0,cB;for(cC;cC<ag.length;cC++){cB=String(ag[cC]);if(b6(ch,C(cB))){cE=true;break}var cD=bO(cB);if(cD&&cD!=="/"&&cD!=="/*"){cE=true;break}}if(!cE){ag.push(ch)}},setIgnoreClasses:function(cA){a7=q(cA)?[cA]:cA},setRequestMethod:function(cA){ct=cA||bM},setRequestContentType:function(cA){bY=cA||ao},setReferrerUrl:function(cA){aX=cA},setCustomUrl:function(cA){aI=bp(bi,cA)},setDocumentTitle:function(cA){aT=cA},setAPIUrl:function(cA){bd=cA},setDownloadClasses:function(cA){bg=q(cA)?[cA]:cA},setLinkClasses:function(cA){aL=q(cA)?[cA]:cA},setCampaignNameKey:function(cA){bU=q(cA)?[cA]:cA},setCampaignKeywordKey:function(cA){bc=q(cA)?[cA]:cA},discardHashTag:function(cA){bk=cA},setCookieNamePrefix:function(cA){aU=cA;av=br()},setCookieDomain:function(cA){var cB=C(cA); -if(a4(cB)){cj=cB;aS()}},setCookiePath:function(cA){aZ=cA;aS()},setVisitorCookieTimeout:function(cA){b9=cA*1000},setSessionCookieTimeout:function(cA){bS=cA*1000},setReferralCookieTimeout:function(cA){cp=cA*1000},setConversionAttributionFirstReferrer:function(cA){a3=cA},disableCookies:function(){aV=true;cr.cookie="0";if(bA){al()}},deleteCookies:function(){al()},setDoNotTrack:function(cB){var cA=f.doNotTrack||f.msDoNotTrack;cd=cB&&(cA==="yes"||cA==="1");if(cd){this.disableCookies()}},addListener:function(cB,cA){ac(cB,cA)},enableLinkTracking:function(cA){cs=true;bL(function(){aP(function(){a9(cA)})})},enableJSErrorTracking:function(){if(cf){return}cf=true;var cA=K.onerror;K.onerror=function(cF,cD,cC,cE,cB){bL(function(){var cG="JavaScript Errors";var cH=cD+":"+cC;if(cE){cH+=":"+cE}af(cG,cH,cF)});if(cA){return cA(cF,cD,cC,cE,cB)}return false}},disablePerformanceTracking:function(){aG=false},setGenerationTimeMs:function(cA){bQ=parseInt(cA,10)},enableHeartBeatTimer:function(cA){cA=Math.max(cA,1); -aJ=(cA||15)*1000;if(ck!==null){cy()}},killFrame:function(){if(K.location!==K.top.location){K.top.location=K.location}},redirectFile:function(cA){if(K.location.protocol==="file:"){K.location=cA}},setCountPreRendered:function(cA){aR=cA},trackGoal:function(cA,cC,cB){bL(function(){cg(cA,cC,cB)})},trackLink:function(cB,cA,cD,cC){bL(function(){cn(cB,cA,cD,cC)})},trackPageView:function(cA,cC,cB){bE=[];if(E(bA)){bL(function(){P(ak,bd,bA)})}else{bL(function(){bu(cA,cC,cB)})}},trackAllContentImpressions:function(){if(E(bA)){return}bL(function(){aP(function(){var cA=p.findContentNodes();var cB=b1(cA);cx(cB,be)})})},trackVisibleContentImpressions:function(cA,cB){if(E(bA)){return}if(!A(cA)){cA=true}if(!A(cB)){cB=750}ax(cA,cB,this);bL(function(){ap(function(){var cC=p.findContentNodes();var cD=aM(cC);cx(cD,be)})})},trackContentImpression:function(cC,cA,cB){if(E(bA)){return}if(!cC){return}cA=cA||"Unknown";bL(function(){var cD=am(cC,cA,cB);bb(cD,be)})},trackContentImpressionsWithinNode:function(cA){if(E(bA)||!cA){return -}bL(function(){if(bJ){ap(function(){var cB=p.findContentNodesWithinNode(cA);var cC=aM(cB);cx(cC,be)})}else{aP(function(){var cB=p.findContentNodesWithinNode(cA);var cC=b1(cB);cx(cC,be)})}})},trackContentInteraction:function(cC,cD,cA,cB){if(E(bA)){return}if(!cC||!cD){return}cA=cA||"Unknown";bL(function(){var cE=au(cC,cD,cA,cB);bb(cE,be)})},trackContentInteractionNode:function(cB,cA){if(E(bA)||!cB){return}bL(function(){var cC=cv(cB,cA);bb(cC,be)})},logAllContentBlocksOnPage:function(){var cB=p.findContentNodes();var cA=p.collectContent(cB);if(console!==undefined&&console&&console.log){console.log(cA)}},trackEvent:function(cB,cD,cA,cC,cE){bL(function(){af(cB,cD,cA,cC,cE)})},trackSiteSearch:function(cA,cC,cB,cD){bL(function(){bD(cA,cC,cB,cD)})},setEcommerceView:function(cD,cA,cC,cB){if(!A(cC)||!cC.length){cC=""}else{if(cC instanceof Array){cC=JSON2.stringify(cC)}}bq[5]=["_pkc",cC];if(A(cB)&&String(cB).length){bq[2]=["_pkp",cB]}if((!A(cD)||!cD.length)&&(!A(cA)||!cA.length)){return}if(A(cD)&&cD.length){bq[3]=["_pks",cD] -}if(!A(cA)||!cA.length){cA=""}bq[4]=["_pkn",cA]},addEcommerceItem:function(cE,cA,cC,cB,cD){if(cE.length){cm[cE]=[cE,cA,cC,cB,cD]}},trackEcommerceOrder:function(cA,cE,cD,cC,cB,cF){bt(cA,cE,cD,cC,cB,cF)},trackEcommerceCartUpdate:function(cA){a1(cA)}}}function y(){return{push:T}}function b(af,ae){var ag={};var ac,ad;for(ac=0;ac<ae.length;ac++){var aa=ae[ac];ag[aa]=1;for(ad=0;ad<af.length;ad++){if(af[ad]&&af[ad][0]){var ab=af[ad][0];if(aa===ab){T(af[ad]);delete af[ad];if(ag[ab]>1){W("The method "+ab+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}ag[ab]++}}}}return af}Y(K,"beforeunload",U,false);Date.prototype.getTimeAlias=Date.prototype.getTime;z.push(new H());var u=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","enableLinkTracking"]; -_paq=b(_paq,u);for(w=0;w<_paq.length;w++){if(_paq[w]){T(_paq[w])}}_paq=new y();d={addPlugin:function(aa,ab){a[aa]=ab},getTracker:function(aa,ab){if(!A(ab)){ab=this.getAsyncTracker().getSiteId()}if(!A(aa)){aa=this.getAsyncTracker().getTrackerUrl()}return new H(aa,ab)},getAsyncTracker:function(ab,ae){var ad;if(z&&z[0]){ad=z[0]}if(!ae&&!ab){return ad}if((!A(ae)||null===ae)&&ad){ae=ad.getSiteId()}if((!A(ab)||null===ab)&&ad){ab=ad.getTrackerUrl()}var ac,aa=0;for(aa;aa<z.length;aa++){ac=z[aa];if(ac&&String(ac.getSiteId())===String(ae)&&ac.getTrackerUrl()===ab){return ac}}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=window.Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h]}}catch(i){}return}var c,e=window.Piwik.getTracker(d,f); -e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; +if(ad=="}"){break}if(ae){if(ad==","){ad=y();if(ad=="}"){H()}}else{H()}}if(ad==","||typeof ad!="string"||(F?ad.charAt(0):ad[0])!="@"||y()!=":"){H()}ac[ad.slice(1)]=W(y())}return ac}}H()}return ad};var P=function(ae,ad,af){var ac=w(ae,ad,af);if(ac===L){delete ae[ad]}else{ae[ad]=ac}};var w=function(af,ae,ag){var ad=af[ae],ac;if(typeof ad=="object"&&ad){if(u.call(ad)==E){for(ac=ad.length;ac--;){P(ad,ac,ag)}}else{m(ad,function(ah){P(ad,ah,ag)})}}return ag.call(af,ae,ad)};V.parse=function(ae,af){var ac,ad;G=0;X=""+ae;ac=W(y());if(y()!="$"){H()}G=X=null;return af&&u.call(af)==U?w((ad={},ad[""]=ac,ad),"",af):ac}}}V.runInContext=j;return V}if(h&&!c){j(i,h)}else{var f=i.JSON,k=i.JSON3,d=false;var g=j(i,(i.JSON3={noConflict:function(){if(!d){d=true;i.JSON=f;i.JSON3=k;f=k=null}return g}}));i.JSON={parse:g.parse,stringify:g.stringify}}if(c){define(function(){return g})}}).call(this);JSON2=a})()}if(typeof _paq!=="object"){_paq=[]}if(typeof window.Piwik!=="object"){window.Piwik=(function(){var l,a={},r={},y=document,f=navigator,P=screen,L=window,g=L.performance||L.mozPerformance||L.msPerformance||L.webkitPerformance,n=L.encodeURIComponent,K=L.decodeURIComponent,i=unescape,A=[],x,d; +function k(ac){try{return K(ac)}catch(ad){return unescape(ac)}}function B(ad){var ac=typeof ad;return ac!=="undefined"}function t(ac){return typeof ac==="function"}function O(ac){return typeof ac==="object"}function q(ac){return typeof ac==="string"||ac instanceof String}function u(ad){if(!ad){return true}var ac;var ae=true;for(ac in ad){if(Object.prototype.hasOwnProperty.call(ad,ac)){ae=false}}return ae}function Y(ac){if(console!==undefined&&console&&console.error){console.error(ac)}}function V(){var ag,af,ai,ac;for(ag=0;ag<arguments.length;ag+=1){ac=arguments[ag];ai=ac.shift();for(af=0;af<A.length;af++){if(q(ai)){var ad=A[af];var ah;var ae=ai.indexOf("::")>0;if(ae){ah=ai.split("::");ad=ah[0];ai=ah[1];if("object"===typeof d[ad]&&"function"===typeof d[ad][ai]){d[ad][ai].apply(d[ad],ac)}return}var aj=ai.indexOf(".")>0;if(aj){ah=ai.split(".");ad=ad[ah[0]];ai=ah[1]}if(ad[ai]){ad[ai].apply(ad,ac)}else{var ak="The method '"+ai+'\' was not found in "_paq" variable. Please have a look at the Piwik tracker documentation: http://developer.piwik.org/api-reference/tracking-javascript'; +Y(ak);if(!aj){throw new TypeError(ak)}}if(ai==="addTracker"){break}if(ai==="setTrackerUrl"||ai==="setSiteId"){break}}else{ai.apply(A[af],ac)}}}}function aa(af,ae,ad,ac){if(af.addEventListener){af.addEventListener(ae,ad,ac);return true}if(af.attachEvent){return af.attachEvent("on"+ae,ad)}af["on"+ae]=ad}function S(ad,ah){var ac="",af,ae,ag;for(af in a){if(Object.prototype.hasOwnProperty.call(a,af)){ae=a[af][ad];if(t(ae)){ag=ae(ah);if(ag){ac+=ag}}}}return ac}function W(){var ac;S("unload");if(l){do{ac=new Date()}while(ac.getTimeAlias()<l)}}function j(ae,ad){var ac=y.createElement("script");ac.type="text/javascript";ac.src=ae;if(ac.readyState){ac.onreadystatechange=function(){var af=this.readyState;if(af==="loaded"||af==="complete"){ac.onreadystatechange=null;ad()}}}else{ac.onload=ad}y.getElementsByTagName("head")[0].appendChild(ac)}function C(){var ac="";try{ac=L.top.document.referrer}catch(ae){if(L.parent){try{ac=L.parent.document.referrer}catch(ad){ac=""}}}if(ac===""){ac=y.referrer}return ac +}function m(ac){var ae=new RegExp("^([a-z]+):"),ad=ae.exec(ac);return ad?ad[1]:null}function c(ac){var ae=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),ad=ae.exec(ac);return ad?ad[1]:ac}function N(ae,ad){var ac="[\\?&#]"+ad+"=([^&#]*)";var ag=new RegExp(ac);var af=ag.exec(ae);return af?K(af[1]):""}function w(ac){return unescape(n(ac))}function Z(ar){var ae=function(ay,ax){return(ay<<ax)|(ay>>>(32-ax))},at=function(aA){var ay="",az,ax;for(az=7;az>=0;az--){ax=(aA>>>(az*4))&15;ay+=ax.toString(16)}return ay},ah,av,au,ad=[],al=1732584193,aj=4023233417,ai=2562383102,ag=271733878,af=3285377520,aq,ap,ao,an,am,aw,ac,ak=[];ar=w(ar);ac=ar.length;for(av=0;av<ac-3;av+=4){au=ar.charCodeAt(av)<<24|ar.charCodeAt(av+1)<<16|ar.charCodeAt(av+2)<<8|ar.charCodeAt(av+3);ak.push(au)}switch(ac&3){case 0:av=2147483648;break;case 1:av=ar.charCodeAt(ac-1)<<24|8388608;break;case 2:av=ar.charCodeAt(ac-2)<<24|ar.charCodeAt(ac-1)<<16|32768;break;case 3:av=ar.charCodeAt(ac-3)<<24|ar.charCodeAt(ac-2)<<16|ar.charCodeAt(ac-1)<<8|128; +break}ak.push(av);while((ak.length&15)!==14){ak.push(0)}ak.push(ac>>>29);ak.push((ac<<3)&4294967295);for(ah=0;ah<ak.length;ah+=16){for(av=0;av<16;av++){ad[av]=ak[ah+av]}for(av=16;av<=79;av++){ad[av]=ae(ad[av-3]^ad[av-8]^ad[av-14]^ad[av-16],1)}aq=al;ap=aj;ao=ai;an=ag;am=af;for(av=0;av<=19;av++){aw=(ae(aq,5)+((ap&ao)|(~ap&an))+am+ad[av]+1518500249)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}for(av=20;av<=39;av++){aw=(ae(aq,5)+(ap^ao^an)+am+ad[av]+1859775393)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}for(av=40;av<=59;av++){aw=(ae(aq,5)+((ap&ao)|(ap&an)|(ao&an))+am+ad[av]+2400959708)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}for(av=60;av<=79;av++){aw=(ae(aq,5)+(ap^ao^an)+am+ad[av]+3395469782)&4294967295;am=an;an=ao;ao=ae(ap,30);ap=aq;aq=aw}al=(al+aq)&4294967295;aj=(aj+ap)&4294967295;ai=(ai+ao)&4294967295;ag=(ag+an)&4294967295;af=(af+am)&4294967295}aw=at(al)+at(aj)+at(ai)+at(ag)+at(af);return aw.toLowerCase()}function R(ae,ac,ad){if(!ae){ae=""}if(!ac){ac=""}if(ae==="translate.googleusercontent.com"){if(ad===""){ad=ac +}ac=N(ac,"u");ae=c(ac)}else{if(ae==="cc.bingj.com"||ae==="webcache.googleusercontent.com"||ae.slice(0,5)==="74.6."){ac=y.links[0].href;ae=c(ac)}}return[ae,ac,ad]}function D(ad){var ac=ad.length;if(ad.charAt(--ac)==="."){ad=ad.slice(0,ac)}if(ad.slice(0,2)==="*."){ad=ad.slice(1)}if(ad.indexOf("/")!==-1){ad=ad.substr(0,ad.indexOf("/"))}return ad}function ab(ad){ad=ad&&ad.text?ad.text:ad;if(!q(ad)){var ac=y.getElementsByTagName("title");if(ac&&B(ac[0])){ad=ac[0].text}}return ad}function H(ac){if(!ac){return[]}if(!B(ac.children)&&B(ac.childNodes)){return ac.children}if(B(ac.children)){return ac.children}return[]}function M(ad,ac){if(!ad||!ac){return false}if(ad.contains){return ad.contains(ac)}if(ad===ac){return true}if(ad.compareDocumentPosition){return !!(ad.compareDocumentPosition(ac)&16)}return false}function E(ae,af){if(ae&&ae.indexOf){return ae.indexOf(af)}if(!B(ae)||ae===null){return -1}if(!ae.length){return -1}var ac=ae.length;if(ac===0){return -1}var ad=0;while(ad<ac){if(ae[ad]===af){return ad +}ad++}return -1}function X(ad,ac){ad=String(ad);return ad.lastIndexOf(ac,0)===0}function J(ad,ac){ad=String(ad);return ad.indexOf(ac,ad.length-ac.length)!==-1}function s(ad,ac){ad=String(ad);return ad.indexOf(ac)!==-1}function e(ad,ac){ad=String(ad);return ad.substr(0,ad.length-ac)}function h(ae){if(!ae){return false}function ac(ag,ah){if(L.getComputedStyle){return y.defaultView.getComputedStyle(ag,null)[ah]}if(ag.currentStyle){return ag.currentStyle[ah]}}function af(ag){ag=ag.parentNode;while(ag){if(ag===y){return true}ag=ag.parentNode}return false}function ad(ai,ao,ag,al,aj,am,ak){var ah=ai.parentNode,an=1;if(!af(ai)){return false}if(9===ah.nodeType){return true}if("0"===ac(ai,"opacity")||"none"===ac(ai,"display")||"hidden"===ac(ai,"visibility")){return false}if(!B(ao)||!B(ag)||!B(al)||!B(aj)||!B(am)||!B(ak)){ao=ai.offsetTop;aj=ai.offsetLeft;al=ao+ai.offsetHeight;ag=aj+ai.offsetWidth;am=ai.offsetWidth;ak=ai.offsetHeight}if(ae===ai&&(0===ak||0===am)&&"hidden"===ac(ai,"overflow")){return false +}if(ah){if(("hidden"===ac(ah,"overflow")||"scroll"===ac(ah,"overflow"))){if(aj+an>ah.offsetWidth+ah.scrollLeft||aj+am-an<ah.scrollLeft||ao+an>ah.offsetHeight+ah.scrollTop||ao+ak-an<ah.scrollTop){return false}}if(ai.offsetParent===ah){aj+=ah.offsetLeft;ao+=ah.offsetTop}return ad(ah,ao,ag,al,aj,am,ak)}return true}return ad(ae)}var U={htmlCollectionToArray:function(ae){var ac=[],ad;if(!ae||!ae.length){return ac}for(ad=0;ad<ae.length;ad++){ac.push(ae[ad])}return ac},find:function(ac){if(!document.querySelectorAll||!ac){return[]}var ad=document.querySelectorAll(ac);return this.htmlCollectionToArray(ad)},findMultiple:function(ae){if(!ae||!ae.length){return[]}var ad,af;var ac=[];for(ad=0;ad<ae.length;ad++){af=this.find(ae[ad]);ac=ac.concat(af)}ac=this.makeNodesUnique(ac);return ac},findNodesByTagName:function(ad,ac){if(!ad||!ac||!ad.getElementsByTagName){return[]}var ae=ad.getElementsByTagName(ac);return this.htmlCollectionToArray(ae)},makeNodesUnique:function(ac){var ah=[].concat(ac);ac.sort(function(aj,ai){if(aj===ai){return 0 +}var al=E(ah,aj);var ak=E(ah,ai);if(al===ak){return 0}return al>ak?-1:1});if(ac.length<=1){return ac}var ad=0;var af=0;var ag=[];var ae;ae=ac[ad++];while(ae){if(ae===ac[ad]){af=ag.push(ad)}ae=ac[ad++]||null}while(af--){ac.splice(ag[af],1)}return ac},getAttributeValueFromNode:function(ag,ae){if(!this.hasNodeAttribute(ag,ae)){return}if(ag&&ag.getAttribute){return ag.getAttribute(ae)}if(!ag||!ag.attributes){return}var af=(typeof ag.attributes[ae]);if("undefined"===af){return}if(ag.attributes[ae].value){return ag.attributes[ae].value}if(ag.attributes[ae].nodeValue){return ag.attributes[ae].nodeValue}var ad;var ac=ag.attributes;if(!ac){return}for(ad=0;ad<ac.length;ad++){if(ac[ad].nodeName===ae){return ac[ad].nodeValue}}return null},hasNodeAttributeWithValue:function(ad,ac){var ae=this.getAttributeValueFromNode(ad,ac);return !!ae},hasNodeAttribute:function(ae,ac){if(ae&&ae.hasAttribute){return ae.hasAttribute(ac)}if(ae&&ae.attributes){var ad=(typeof ae.attributes[ac]);return"undefined"!==ad}return false +},hasNodeCssClass:function(ae,ac){if(ae&&ac&&ae.className){var ad=typeof ae.className==="string"?ae.className.split(" "):[];if(-1!==E(ad,ac)){return true}}return false},findNodesHavingAttribute:function(ag,ae,ac){if(!ac){ac=[]}if(!ag||!ae){return ac}var af=H(ag);if(!af||!af.length){return ac}var ad,ah;for(ad=0;ad<af.length;ad++){ah=af[ad];if(this.hasNodeAttribute(ah,ae)){ac.push(ah)}ac=this.findNodesHavingAttribute(ah,ae,ac)}return ac},findFirstNodeHavingAttribute:function(ae,ad){if(!ae||!ad){return}if(this.hasNodeAttribute(ae,ad)){return ae}var ac=this.findNodesHavingAttribute(ae,ad);if(ac&&ac.length){return ac[0]}},findFirstNodeHavingAttributeWithValue:function(af,ae){if(!af||!ae){return}if(this.hasNodeAttributeWithValue(af,ae)){return af}var ac=this.findNodesHavingAttribute(af,ae);if(!ac||!ac.length){return}var ad;for(ad=0;ad<ac.length;ad++){if(this.getAttributeValueFromNode(ac[ad],ae)){return ac[ad]}}},findNodesHavingCssClass:function(ag,af,ac){if(!ac){ac=[]}if(!ag||!af){return ac}if(ag.getElementsByClassName){var ah=ag.getElementsByClassName(af); +return this.htmlCollectionToArray(ah)}var ae=H(ag);if(!ae||!ae.length){return[]}var ad,ai;for(ad=0;ad<ae.length;ad++){ai=ae[ad];if(this.hasNodeCssClass(ai,af)){ac.push(ai)}ac=this.findNodesHavingCssClass(ai,af,ac)}return ac},findFirstNodeHavingClass:function(ae,ad){if(!ae||!ad){return}if(this.hasNodeCssClass(ae,ad)){return ae}var ac=this.findNodesHavingCssClass(ae,ad);if(ac&&ac.length){return ac[0]}},isLinkElement:function(ad){if(!ad){return false}var ac=String(ad.nodeName).toLowerCase();var af=["a","area"];var ae=E(af,ac);return ae!==-1},setAnyAttribute:function(ad,ac,ae){if(!ad||!ac){return}if(ad.setAttribute){ad.setAttribute(ac,ae)}else{ad[ac]=ae}}};var p={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 ad="."+this.CONTENT_CLASS; +var ac="["+this.CONTENT_ATTR+"]";var ae=U.findMultiple([ad,ac]);return ae},findContentNodesWithinNode:function(af){if(!af){return[]}var ad=U.findNodesHavingCssClass(af,this.CONTENT_CLASS);var ac=U.findNodesHavingAttribute(af,this.CONTENT_ATTR);if(ac&&ac.length){var ae;for(ae=0;ae<ac.length;ae++){ad.push(ac[ae])}}if(U.hasNodeAttribute(af,this.CONTENT_ATTR)){ad.push(af)}else{if(U.hasNodeCssClass(af,this.CONTENT_CLASS)){ad.push(af)}}ad=U.makeNodesUnique(ad);return ad},findParentContentNode:function(ad){if(!ad){return}var ae=ad;var ac=0;while(ae&&ae!==y&&ae.parentNode){if(U.hasNodeAttribute(ae,this.CONTENT_ATTR)){return ae}if(U.hasNodeCssClass(ae,this.CONTENT_CLASS)){return ae}ae=ae.parentNode;if(ac>1000){break}ac++}},findPieceNode:function(ad){var ac;ac=U.findFirstNodeHavingAttribute(ad,this.CONTENT_PIECE_ATTR);if(!ac){ac=U.findFirstNodeHavingClass(ad,this.CONTENT_PIECE_CLASS)}if(ac){return ac}return ad},findTargetNodeNoDefault:function(ac){if(!ac){return}var ad=U.findFirstNodeHavingAttributeWithValue(ac,this.CONTENT_TARGET_ATTR); +if(ad){return ad}ad=U.findFirstNodeHavingAttribute(ac,this.CONTENT_TARGET_ATTR);if(ad){return ad}ad=U.findFirstNodeHavingClass(ac,this.CONTENT_TARGET_CLASS);if(ad){return ad}},findTargetNode:function(ac){var ad=this.findTargetNodeNoDefault(ac);if(ad){return ad}return ac},findContentName:function(ad){if(!ad){return}var ag=U.findFirstNodeHavingAttributeWithValue(ad,this.CONTENT_NAME_ATTR);if(ag){return U.getAttributeValueFromNode(ag,this.CONTENT_NAME_ATTR)}var ac=this.findContentPiece(ad);if(ac){return this.removeDomainIfIsInLink(ac)}if(U.hasNodeAttributeWithValue(ad,"title")){return U.getAttributeValueFromNode(ad,"title")}var ae=this.findPieceNode(ad);if(U.hasNodeAttributeWithValue(ae,"title")){return U.getAttributeValueFromNode(ae,"title")}var af=this.findTargetNode(ad);if(U.hasNodeAttributeWithValue(af,"title")){return U.getAttributeValueFromNode(af,"title")}},findContentPiece:function(ad){if(!ad){return}var af=U.findFirstNodeHavingAttributeWithValue(ad,this.CONTENT_PIECE_ATTR);if(af){return U.getAttributeValueFromNode(af,this.CONTENT_PIECE_ATTR) +}var ac=this.findPieceNode(ad);var ae=this.findMediaUrlInNode(ac);if(ae){return this.toAbsoluteUrl(ae)}},findContentTarget:function(ae){if(!ae){return}var af=this.findTargetNode(ae);if(U.hasNodeAttributeWithValue(af,this.CONTENT_TARGET_ATTR)){return U.getAttributeValueFromNode(af,this.CONTENT_TARGET_ATTR)}var ad;if(U.hasNodeAttributeWithValue(af,"href")){ad=U.getAttributeValueFromNode(af,"href");return this.toAbsoluteUrl(ad)}var ac=this.findPieceNode(ae);if(U.hasNodeAttributeWithValue(ac,"href")){ad=U.getAttributeValueFromNode(ac,"href");return this.toAbsoluteUrl(ad)}},isSameDomain:function(ac){if(!ac||!ac.indexOf){return false}if(0===ac.indexOf(this.getLocation().origin)){return true}var ad=ac.indexOf(this.getLocation().host);if(8>=ad&&0<=ad){return true}return false},removeDomainIfIsInLink:function(ae){var ad="^https?://[^/]+";var ac="^.*//[^/]+";if(ae&&ae.search&&-1!==ae.search(new RegExp(ad))&&this.isSameDomain(ae)){ae=ae.replace(new RegExp(ac),"");if(!ae){ae="/"}}return ae},findMediaUrlInNode:function(ag){if(!ag){return +}var ae=["img","embed","video","audio"];var ac=ag.nodeName.toLowerCase();if(-1!==E(ae,ac)&&U.findFirstNodeHavingAttributeWithValue(ag,"src")){var af=U.findFirstNodeHavingAttributeWithValue(ag,"src");return U.getAttributeValueFromNode(af,"src")}if(ac==="object"&&U.hasNodeAttributeWithValue(ag,"data")){return U.getAttributeValueFromNode(ag,"data")}if(ac==="object"){var ah=U.findNodesByTagName(ag,"param");if(ah&&ah.length){var ad;for(ad=0;ad<ah.length;ad++){if("movie"===U.getAttributeValueFromNode(ah[ad],"name")&&U.hasNodeAttributeWithValue(ah[ad],"value")){return U.getAttributeValueFromNode(ah[ad],"value")}}}var ai=U.findNodesByTagName(ag,"embed");if(ai&&ai.length){return this.findMediaUrlInNode(ai[0])}}},trim:function(ac){if(ac&&String(ac)===ac){return ac.replace(/^\s+|\s+$/g,"")}return ac},isOrWasNodeInViewport:function(ah){if(!ah||!ah.getBoundingClientRect||ah.nodeType!==1){return true}var ag=ah.getBoundingClientRect();var af=y.documentElement||{};var ae=ag.top<0;if(ae&&ah.offsetTop){ae=(ah.offsetTop+ag.height)>0 +}var ad=af.clientWidth;if(L.innerWidth&&ad>L.innerWidth){ad=L.innerWidth}var ac=af.clientHeight;if(L.innerHeight&&ac>L.innerHeight){ac=L.innerHeight}return((ag.bottom>0||ae)&&ag.right>0&&ag.left<ad&&((ag.top<ac)||ae))},isNodeVisible:function(ad){var ac=h(ad);var ae=this.isOrWasNodeInViewport(ad);return ac&&ae},buildInteractionRequestParams:function(ac,ad,ae,af){var ag="";if(ac){ag+="c_i="+n(ac)}if(ad){if(ag){ag+="&"}ag+="c_n="+n(ad)}if(ae){if(ag){ag+="&"}ag+="c_p="+n(ae)}if(af){if(ag){ag+="&"}ag+="c_t="+n(af)}return ag},buildImpressionRequestParams:function(ac,ad,ae){var af="c_n="+n(ac)+"&c_p="+n(ad);if(ae){af+="&c_t="+n(ae)}return af},buildContentBlock:function(ae){if(!ae){return}var ac=this.findContentName(ae);var ad=this.findContentPiece(ae);var af=this.findContentTarget(ae);ac=this.trim(ac);ad=this.trim(ad);af=this.trim(af);return{name:ac||"Unknown",piece:ad||"Unknown",target:af||""}},collectContent:function(af){if(!af||!af.length){return[]}var ae=[];var ac,ad;for(ac=0;ac<af.length; +ac++){ad=this.buildContentBlock(af[ac]);if(B(ad)){ae.push(ad)}}return ae},setLocation:function(ac){this.location=ac},getLocation:function(){var ac=this.location||L.location;if(!ac.origin){ac.origin=ac.protocol+"//"+ac.hostname+(ac.port?":"+ac.port:"")}return ac},toAbsoluteUrl:function(ad){if((!ad||String(ad)!==ad)&&ad!==""){return ad}if(""===ad){return this.getLocation().href}if(ad.search(/^\/\//)!==-1){return this.getLocation().protocol+ad}if(ad.search(/:\/\//)!==-1){return ad}if(0===ad.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+ad}if(0===ad.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+ad}if(0===ad.search("^[a-zA-Z]{2,11}:")){return ad}if(ad.search(/^\//)!==-1){return this.getLocation().origin+ad}var ac="(.*/)";var ae=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(ac))[0];return ae+ad},isUrlToCurrentDomain:function(ad){var ae=this.toAbsoluteUrl(ad);if(!ae){return false}var ac=this.getLocation().origin; +if(ac===ae){return true}if(0===String(ae).indexOf(ac)){if(":"===String(ae).substr(ac.length,1)){return false}return true}return false},setHrefAttribute:function(ad,ac){if(!ad||!ac){return}U.setAnyAttribute(ad,"href",ac)},shouldIgnoreInteraction:function(ae){var ad=U.hasNodeAttribute(ae,this.CONTENT_IGNOREINTERACTION_ATTR);var ac=U.hasNodeCssClass(ae,this.CONTENT_IGNOREINTERACTION_CLASS);return ad||ac}};function G(ad,ag){if(ag){return ag}if(s(ad,"?")){var af=ad.indexOf("?");ad=ad.slice(0,af)}if(J(ad,"piwik.php")){ad=e(ad,"piwik.php".length)}else{if(J(ad,".php")){var ac=ad.lastIndexOf("/");var ae=1;ad=ad.slice(0,ac+ae)}}if(J(ad,"/js/")){ad=e(ad,"js/".length)}return ad}function F(ai){var ak="Piwik_Overlay";var ad=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$");var ae=ad.exec(y.referrer);if(ae){var ag=ae[1];if(ag!==String(ai)){return false}var ah=ae[2],ac=ae[3],af=ae[4];if(!af){af=""}else{if(af.indexOf("&segment=")===0){af=af.substr("&segment=".length) +}}L.name=ak+"###"+ah+"###"+ac+"###"+af}var aj=L.name.split("###");return aj.length===4&&aj[0]===ak}function Q(ad,aj,af){var ai=L.name.split("###"),ah=ai[1],ac=ai[2],ag=ai[3],ae=G(ad,aj);j(ae+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(ae,af,ah,ac,ag)})}function o(){var ae;try{ae=L.frameElement}catch(ad){return true}if(B(ae)){return(ae&&String(ae.nodeName).toLowerCase()==="iframe")?true:false}try{return L.self!==L.top}catch(ac){return true}}function I(bK,bE){var bA=R(y.domain,L.location.href,C()),cj=D(bA[0]),bk=k(bA[1]),aZ=k(bA[2]),ch=false,bO="GET",cv=bO,aq="application/x-www-form-urlencoded; charset=UTF-8",b0=aq,am=bK||"",bf="",cn="",bC=bE||"",a8="",bl="",aK,aV="",cs=["7z","aac","apk","arc","arj","asf","asx","avi","azw3","bin","csv","deb","dmg","doc","docx","epub","exe","flv","gif","gz","gzip","hqx","ibooks","jar","jpg","jpeg","js","mobi","mp2","mp3","mp4","mpg","mpeg","mov","movie","msi","msp","odb","odf","odg","ods","odt","ogg","ogv","pdf","phps","png","ppt","pptx","qt","qtm","ra","ram","rar","rpm","sea","sit","tar","tbz","tbz2","bz","bz2","tgz","torrent","txt","wav","wma","wmv","wpd","xls","xlsx","xml","z","zip"],ai=[cj],a9=[],bi=[],aN=[],bg=500,ca,aL,bo,bm,ac,bW=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],be=["pk_kwd","piwik_kwd","utm_term"],aW="_pk_",cl,a1,aX=false,cf,aT,a5,cb=33955200000,bU=1800000,cr=15768000000,aI=true,bS=0,bn=false,ax=false,bH,bs={},bR={},aY={},a4=200,co={},ct={},bG=[],bL=false,b4=false,ad=false,cu=false,av=false,aS=o(),cm=null,bI,ay,ba,bD=Z,a0; +try{aV=y.title}catch(b2){aV=""}function cy(cI,cF,cE,cH,cD,cG){if(aX){return}var cC;if(cE){cC=new Date();cC.setTime(cC.getTime()+cE)}y.cookie=cI+"="+n(cF)+(cE?";expires="+cC.toGMTString():"")+";path="+(cH||"/")+(cD?";domain="+cD:"")+(cG?";secure":"")}function al(cE){if(aX){return 0}var cC=new RegExp("(^|;)[ ]*"+cE+"=([^;]*)"),cD=cC.exec(y.cookie);return cD?K(cD[2]):0}function by(cC){var cD;if(bm){cD=new RegExp("#.*");return cC.replace(cD,"")}return cC}function br(cE,cC){var cF=m(cC),cD;if(cF){return cC}if(cC.slice(0,1)==="/"){return m(cE)+"://"+c(cE)+cC}cE=by(cE);cD=cE.indexOf("?");if(cD>=0){cE=cE.slice(0,cD)}cD=cE.lastIndexOf("/");if(cD!==cE.length-1){cE=cE.slice(0,cD+1)}return cE+cC}function b8(cE,cC){var cD;cE=String(cE).toLowerCase();cC=String(cC).toLowerCase();if(cE===cC){return true}if(cC.slice(0,1)==="."){if(cE===cC.slice(1)){return true}cD=cE.length-cC.length;if((cD>0)&&(cE.slice(cD)===cC)){return true}}return false}function bQ(cC){var cD=document.createElement("a");if(cC.indexOf("//")!==0&&cC.indexOf("http")!==0){if(cC.indexOf("*")===0){cC=cC.substr(1) +}if(cC.indexOf(".")===0){cC=cC.substr(1)}cC="http://"+cC}cD.href=p.toAbsoluteUrl(cC);if(cD.pathname){return cD.pathname}return""}function aJ(cD,cC){if(!X(cC,"/")){cC="/"+cC}if(!X(cD,"/")){cD="/"+cD}var cE=(cC==="/"||cC==="/*");if(cE){return true}if(cD===cC){return true}cC=String(cC).toLowerCase();cD=String(cD).toLowerCase();if(J(cC,"*")){cC=cC.slice(0,-1);cE=(!cC||cC==="/");if(cE){return true}if(cD===cC){return true}return cD.indexOf(cC)===0}if(!J(cD,"/")){cD+="/"}if(!J(cC,"/")){cC+="/"}return cD.indexOf(cC)===0}function af(cG,cI){var cD,cC,cE,cF,cH;for(cD=0;cD<ai.length;cD++){cF=D(ai[cD]);cH=bQ(ai[cD]);if(b8(cG,cF)&&aJ(cI,cH)){return true}}return false}function aC(cF){var cD,cC,cE;for(cD=0;cD<ai.length;cD++){cC=D(ai[cD].toLowerCase());if(cF===cC){return true}if(cC.slice(0,1)==="."){if(cF===cC.slice(1)){return true}cE=cF.length-cC.length;if((cE>0)&&(cF.slice(cE)===cC)){return true}}}return false}function bV(cC,cE){var cD=new Image(1,1);cD.onload=function(){x=0;if(typeof cE==="function"){cE() +}};cC=cC.replace("send_image=0","send_image=1");cD.src=am+(am.indexOf("?")<0?"?":"&")+cC}function cq(cD,cG,cC){if(!B(cC)||null===cC){cC=true}try{var cF=L.XMLHttpRequest?new L.XMLHttpRequest():L.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cF.open("POST",am,true);cF.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cC){bV(cD,cG)}else{if(this.readyState===4&&(typeof cG==="function")){cG()}}};cF.setRequestHeader("Content-Type",b0);cF.send(cD)}catch(cE){if(cC){bV(cD,cG)}}}function bM(cD){var cC=new Date();var cE=cC.getTime()+cD;if(!l||cE>l){l=cE}}function bT(cC){if(bI||!aL){return}bI=setTimeout(function cD(){bI=null;if(!aS){aS=(!y.hasFocus||y.hasFocus())}if(!aS){bT(aL);return}if(bo()){return}var cE=new Date(),cF=aL-(cE.getTime()-cm);cF=Math.min(aL,cF);bT(cF)},cC||aL)}function bh(){if(!bI){return}clearTimeout(bI);bI=null}function aP(){aS=true;if(bo()){return}bT()}function aj(){bh()}function cA(){if(av||!aL){return}av=true;aa(L,"focus",aP); +aa(L,"blur",aj);bT()}function b5(cG){var cD=new Date();var cC=cD.getTime();cm=cC;if(b4&&cC<b4){var cE=b4-cC;setTimeout(cG,cE);bM(cE+50);b4+=50;return}if(b4===false){var cF=800;b4=cC+cF}cG()}function bd(cD,cC,cE){if(!cf&&cD){b5(function(){if(cv==="POST"){cq(cD,cE)}else{bV(cD,cE)}bM(cC)})}if(!av){cA()}else{bT()}}function bP(cC){if(cf){return false}return(cC&&cC.length)}function cz(cE,cC){if(!bP(cE)){return}var cD='{"requests":["?'+cE.join('","?')+'"]}';b5(function(){cq(cD,null,false);bM(cC)})}function aA(cC){return aW+cC+"."+bC+"."+a0}function bB(){if(aX){return"0"}if(!B(f.cookieEnabled)){var cC=aA("testcookie");cy(cC,"1");return al(cC)==="1"?"1":"0"}return f.cookieEnabled?"1":"0"}function aU(){a0=bD((cl||cj)+(a1||"/")).slice(0,4)}function bt(){var cD=aA("cvar"),cC=al(cD);if(cC.length){cC=JSON2.parse(cC);if(O(cC)){return cC}}return{}}function b6(){if(ax===false){ax=bt()}}function cg(){return bD((f.userAgent||"")+(f.platform||"")+JSON2.stringify(ct)+(new Date()).getTime()+Math.random()).slice(0,16) +}function cd(){var cE=new Date(),cC=Math.round(cE.getTime()/1000),cD=aA("id"),cH=al(cD),cG,cF;if(cH){cG=cH.split(".");cG.unshift("0");if(bl.length){cG[1]=bl}return cG}if(bl.length){cF=bl}else{if("0"===bB()){cF=""}else{cF=cg()}}cG=["1",cF,cC,0,cC,"",""];return cG}function aE(){var cJ=cd(),cF=cJ[0],cG=cJ[1],cD=cJ[2],cC=cJ[3],cH=cJ[4],cE=cJ[5];if(!B(cJ[6])){cJ[6]=""}var cI=cJ[6];return{newVisitor:cF,uuid:cG,createTs:cD,visitCount:cC,currentVisitTs:cH,lastVisitTs:cE,lastEcommerceOrderTs:cI}}function ap(){var cF=new Date(),cD=cF.getTime(),cG=aE().createTs;var cC=parseInt(cG,10);var cE=(cC*1000)+cb-cD;return cE}function at(cC){if(!bC){return}var cE=new Date(),cD=Math.round(cE.getTime()/1000);if(!B(cC)){cC=aE()}var cF=cC.uuid+"."+cC.createTs+"."+cC.visitCount+"."+cD+"."+cC.lastVisitTs+"."+cC.lastEcommerceOrderTs;cy(aA("id"),cF,ap(),a1,cl)}function bj(){var cC=al(aA("ref"));if(cC.length){try{cC=JSON2.parse(cC);if(O(cC)){return cC}}catch(cD){}}return["","",0,""]}function bu(cE,cD,cC){cy(cE,"",-86400,cD,cC) +}function a6(cD){var cC="testvalue";cy("test",cC,10000,null,cD);if(al("test")===cC){bu("test",null,cD);return true}return false}function an(){var cE=aX;aX=false;var cC=["id","ses","cvar","ref"];var cD,cF;for(cD=0;cD<cC.length;cD++){cF=aA(cC[cD]);if(0!==al(cF)){bu(cF,a1,cl)}}aX=cE}function bz(cC){bC=cC;at()}function cB(cG){if(!cG||!O(cG)){return}var cF=[];var cE;for(cE in cG){if(Object.prototype.hasOwnProperty.call(cG,cE)){cF.push(cE)}}var cH={};cF.sort();var cC=cF.length;var cD;for(cD=0;cD<cC;cD++){cH[cF[cD]]=cG[cF[cD]]}return cH}function bJ(){cy(aA("ses"),"*",bU,a1,cl)}function bX(cE,cZ,c0,cF){var cY,cD=new Date(),cM=Math.round(cD.getTime()/1000),cJ,cX,cG=1024,c5,cN,cV=ax,cH=aA("ses"),cT=aA("ref"),cQ=aA("cvar"),cR=al(cH),cW=bj(),c2=aK||bk,cK,cC;if(aX){an()}if(cf){return""}var cS=aE();if(!B(cF)){cF=""}var cP=y.characterSet||y.charset;if(!cP||cP.toLowerCase()==="utf-8"){cP=null}cK=cW[0];cC=cW[1];cJ=cW[2];cX=cW[3];if(!cR){var c1=bU/1000;if(!cS.lastVisitTs||(cM-cS.lastVisitTs)>c1){cS.visitCount++; +cS.lastVisitTs=cS.currentVisitTs}if(!a5||!cK.length){for(cY in bW){if(Object.prototype.hasOwnProperty.call(bW,cY)){cK=N(c2,bW[cY]);if(cK.length){break}}}for(cY in be){if(Object.prototype.hasOwnProperty.call(be,cY)){cC=N(c2,be[cY]);if(cC.length){break}}}}c5=c(aZ);cN=cX.length?c(cX):"";if(c5.length&&!aC(c5)&&(!a5||!cN.length||aC(cN))){cX=aZ}if(cX.length||cK.length){cJ=cM;cW=[cK,cC,cJ,by(cX.slice(0,cG))];cy(cT,JSON2.stringify(cW),cr,a1,cl)}}cE+="&idsite="+bC+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cD.getHours()+"&m="+cD.getMinutes()+"&s="+cD.getSeconds()+"&url="+n(by(c2))+(aZ.length?"&urlref="+n(by(aZ)):"")+((a8&&a8.length)?"&uid="+n(a8):"")+"&_id="+cS.uuid+"&_idts="+cS.createTs+"&_idvc="+cS.visitCount+"&_idn="+cS.newVisitor+(cK.length?"&_rcn="+n(cK):"")+(cC.length?"&_rck="+n(cC):"")+"&_refts="+cJ+"&_viewts="+cS.lastVisitTs+(String(cS.lastEcommerceOrderTs).length?"&_ects="+cS.lastEcommerceOrderTs:"")+(String(cX).length?"&_ref="+n(by(cX.slice(0,cG))):"")+(cP?"&cs="+n(cP):"")+"&send_image=0"; +for(cY in ct){if(Object.prototype.hasOwnProperty.call(ct,cY)){cE+="&"+cY+"="+ct[cY]}}var c4=[];if(cZ){for(cY in cZ){if(Object.prototype.hasOwnProperty.call(cZ,cY)&&/^dimension\d+$/.test(cY)){var cI=cY.replace("dimension","");c4.push(parseInt(cI,10));c4.push(String(cI));cE+="&"+cY+"="+cZ[cY];delete cZ[cY]}}}if(cZ&&u(cZ)){cZ=null}for(cY in aY){if(Object.prototype.hasOwnProperty.call(aY,cY)){var cO=(-1===E(c4,cY));if(cO){cE+="&dimension"+cY+"="+aY[cY]}}}if(cZ){cE+="&data="+n(JSON2.stringify(cZ))}else{if(ac){cE+="&data="+n(JSON2.stringify(ac))}}function cL(c6,c7){var c8=JSON2.stringify(c6);if(c8.length>2){return"&"+c7+"="+n(c8)}return""}var c3=cB(bs);var cU=cB(bR);cE+=cL(c3,"cvar");cE+=cL(cU,"e_cvar");if(ax){cE+=cL(ax,"_cvar");for(cY in cV){if(Object.prototype.hasOwnProperty.call(cV,cY)){if(ax[cY][0]===""||ax[cY][1]===""){delete ax[cY]}}}if(bn){cy(cQ,JSON2.stringify(ax),bU,a1,cl)}}if(aI){if(bS){cE+=">_ms="+bS}else{if(g&&g.timing&&g.timing.requestStart&&g.timing.responseEnd){cE+=">_ms="+(g.timing.responseEnd-g.timing.requestStart) +}}}cS.lastEcommerceOrderTs=B(cF)&&String(cF).length?cF:cS.lastEcommerceOrderTs;at(cS);bJ();cE+=S(c0);if(cn.length){cE+="&"+cn}if(t(bH)){cE=bH(cE)}return cE}bo=function aM(){var cC=new Date();if(cm+aL<=cC.getTime()){var cD=bX("ping=1",null,"ping");bd(cD,bg);return true}return false};function a2(cF,cE,cK,cG,cC,cN){var cI="idgoal=0",cJ,cD=new Date(),cL=[],cM,cH=String(cF).length;if(cH){cI+="&ec_id="+n(cF);cJ=Math.round(cD.getTime()/1000)}cI+="&revenue="+cE;if(String(cK).length){cI+="&ec_st="+cK}if(String(cG).length){cI+="&ec_tx="+cG}if(String(cC).length){cI+="&ec_sh="+cC}if(String(cN).length){cI+="&ec_dt="+cN}if(co){for(cM in co){if(Object.prototype.hasOwnProperty.call(co,cM)){if(!B(co[cM][1])){co[cM][1]=""}if(!B(co[cM][2])){co[cM][2]=""}if(!B(co[cM][3])||String(co[cM][3]).length===0){co[cM][3]=0}if(!B(co[cM][4])||String(co[cM][4]).length===0){co[cM][4]=1}cL.push(co[cM])}}cI+="&ec_items="+n(JSON2.stringify(cL))}cI=bX(cI,ac,"ecommerce",cJ);bd(cI,bg);if(cH){co={}}}function bv(cC,cG,cF,cE,cD,cH){if(String(cC).length&&B(cG)){a2(cC,cG,cF,cE,cD,cH) +}}function a3(cC){if(B(cC)){a2("",cC,"","","","")}}function bw(cD,cF,cE){var cC=bX("action_name="+n(ab(cD||aV)),cF,"log");bd(cC,bg,cE)}function aG(cE,cD){var cF,cC="(^| )(piwik[_-]"+cD;if(cE){for(cF=0;cF<cE.length;cF++){cC+="|"+cE[cF]}}cC+=")( |$)";return new RegExp(cC)}function aB(cC){return(am&&cC&&0===String(cC).indexOf(am))}function bY(cG,cC,cH,cD){if(aB(cC)){return 0}var cF=aG(bi,"download"),cE=aG(aN,"link"),cI=new RegExp("\\.("+cs.join("|")+")([?&#]|$)","i");if(cE.test(cG)){return"link"}if(cD||cF.test(cG)||cI.test(cC)){return"download"}if(cH){return 0}return"link"}function ag(cD){var cC;cC=cD.parentNode;while(cC!==null&&B(cC)){if(U.isLinkElement(cD)){break}cD=cC;cC=cD.parentNode}return cD}function cw(cH){cH=ag(cH);if(!U.hasNodeAttribute(cH,"href")){return}if(!B(cH.href)){return}var cG=U.getAttributeValueFromNode(cH,"href");if(aB(cG)){return}var cD=cH.pathname||bQ(cH.href);var cI=cH.hostname||c(cH.href);var cJ=cI.toLowerCase();var cE=cH.href.replace(cI,cJ);var cF=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto|tel):","i"); +if(!cF.test(cE)){var cC=bY(cH.className,cE,af(cJ,cD),U.hasNodeAttribute(cH,"download"));if(cC){return{type:cC,href:cE}}}}function aw(cC,cD,cE,cF){var cG=p.buildInteractionRequestParams(cC,cD,cE,cF);if(!cG){return}return bX(cG,null,"contentInteraction")}function cc(cE,cF,cJ,cC,cD){if(!B(cE)){return}if(aB(cE)){return cE}var cH=p.toAbsoluteUrl(cE);var cG="redirecturl="+n(cH)+"&";cG+=aw(cF,cJ,cC,(cD||cE));var cI="&";if(am.indexOf("?")<0){cI="?"}return am+cI+cG}function aQ(cC,cD){if(!cC||!cD){return false}var cE=p.findTargetNode(cC);if(p.shouldIgnoreInteraction(cE)){return false}cE=p.findTargetNodeNoDefault(cC);if(cE&&!M(cE,cD)){return false}return true}function bZ(cE,cD,cG){if(!cE){return}var cC=p.findParentContentNode(cE);if(!cC){return}if(!aQ(cC,cE)){return}var cF=p.buildContentBlock(cC);if(!cF){return}if(!cF.target&&cG){cF.target=cG}return p.buildInteractionRequestParams(cD,cF.name,cF.piece,cF.target)}function aD(cD){if(!bG||!bG.length){return false}var cC,cE;for(cC=0;cC<bG.length;cC++){cE=bG[cC]; +if(cE&&cE.name===cD.name&&cE.piece===cD.piece&&cE.target===cD.target){return true}}return false}function bc(cF){if(!cF){return false}var cI=p.findTargetNode(cF);if(!cI||p.shouldIgnoreInteraction(cI)){return false}var cJ=cw(cI);if(cu&&cJ&&cJ.type){return false}if(U.isLinkElement(cI)&&U.hasNodeAttributeWithValue(cI,"href")){var cC=String(U.getAttributeValueFromNode(cI,"href"));if(0===cC.indexOf("#")){return false}if(aB(cC)){return true}if(!p.isUrlToCurrentDomain(cC)){return false}var cG=p.buildContentBlock(cF);if(!cG){return}var cE=cG.name;var cK=cG.piece;var cH=cG.target;if(!U.hasNodeAttributeWithValue(cI,p.CONTENT_TARGET_ATTR)||cI.wasContentTargetAttrReplaced){cI.wasContentTargetAttrReplaced=true;cH=p.toAbsoluteUrl(cC);U.setAnyAttribute(cI,p.CONTENT_TARGET_ATTR,cH)}var cD=cc(cC,"click",cE,cK,cH);p.setHrefAttribute(cI,cD);return true}return false}function au(cD){if(!cD||!cD.length){return}var cC;for(cC=0;cC<cD.length;cC++){bc(cD[cC])}}function aF(cC){return function(cD){if(!cC){return}var cG=p.findParentContentNode(cC); +var cH;if(cD){cH=cD.target||cD.srcElement}if(!cH){cH=cC}if(!aQ(cG,cH)){return}bM(bg);if(U.isLinkElement(cC)&&U.hasNodeAttributeWithValue(cC,"href")&&U.hasNodeAttributeWithValue(cC,p.CONTENT_TARGET_ATTR)){var cE=U.getAttributeValueFromNode(cC,"href");if(!aB(cE)&&cC.wasContentTargetAttrReplaced){U.setAnyAttribute(cC,p.CONTENT_TARGET_ATTR,"")}}var cL=cw(cC);if(ad&&cL&&cL.type){return cL.type}if(bc(cG)){return"href"}var cI=p.buildContentBlock(cG);if(!cI){return}var cF=cI.name;var cM=cI.piece;var cK=cI.target;var cJ=aw("click",cF,cM,cK);bd(cJ,bg);return cJ}}function bx(cE){if(!cE||!cE.length){return}var cC,cD;for(cC=0;cC<cE.length;cC++){cD=p.findTargetNode(cE[cC]);if(cD&&!cD.contentInteractionTrackingSetupDone){cD.contentInteractionTrackingSetupDone=true;aa(cD,"click",aF(cD))}}}function a7(cE,cF){if(!cE||!cE.length){return[]}var cC,cD;for(cC=0;cC<cE.length;cC++){if(aD(cE[cC])){cE.splice(cC,1);cC--}else{bG.push(cE[cC])}}if(!cE||!cE.length){return[]}au(cF);bx(cF);var cG=[];for(cC=0;cC<cE.length; +cC++){cD=bX(p.buildImpressionRequestParams(cE[cC].name,cE[cC].piece,cE[cC].target),undefined,"contentImpressions");if(cD){cG.push(cD)}}return cG}function b3(cD){var cC=p.collectContent(cD);return a7(cC,cD)}function aO(cD){if(!cD||!cD.length){return[]}var cC;for(cC=0;cC<cD.length;cC++){if(!p.isNodeVisible(cD[cC])){cD.splice(cC,1);cC--}}if(!cD||!cD.length){return[]}return b3(cD)}function ao(cE,cC,cD){var cF=p.buildImpressionRequestParams(cE,cC,cD);return bX(cF,null,"contentImpression")}function cx(cF,cD){if(!cF){return}var cC=p.findParentContentNode(cF);var cE=p.buildContentBlock(cC);if(!cE){return}if(!cD){cD="Unknown"}return aw(cD,cE.name,cE.piece,cE.target)}function ce(cD,cF,cC,cE){return"e_c="+n(cD)+"&e_a="+n(cF)+(B(cC)?"&e_n="+n(cC):"")+(B(cE)?"&e_v="+n(cE):"")}function ah(cE,cG,cC,cF,cH){if(String(cE).length===0||String(cG).length===0){return false}var cD=bX(ce(cE,cG,cC,cF),cH,"event");bd(cD,bg)}function bF(cC,cF,cD,cG){var cE=bX("search="+n(cC)+(cF?"&search_cat="+n(cF):"")+(B(cD)?"&search_count="+cD:""),cG,"sitesearch"); +bd(cE,bg)}function ci(cC,cF,cE){var cD=bX("idgoal="+cC+(cF?"&revenue="+cF:""),cE,"goal");bd(cD,bg)}function cp(cF,cC,cJ,cI,cE){var cH=cC+"="+n(by(cF));var cD=bZ(cE,"click",cF);if(cD){cH+="&"+cD}var cG=bX(cH,cJ,"link");bd(cG,(cI?0:bg),cI)}function bp(cD,cC){if(cD!==""){return cD+cC.charAt(0).toUpperCase()+cC.slice(1)}return cC}function bN(cH){var cG,cC,cF=["","webkit","ms","moz"],cE;if(!aT){for(cC=0;cC<cF.length;cC++){cE=cF[cC];if(Object.prototype.hasOwnProperty.call(y,bp(cE,"hidden"))){if(y[bp(cE,"visibilityState")]==="prerender"){cG=true}break}}}if(cG){aa(y,cE+"visibilitychange",function cD(){y.removeEventListener(cE+"visibilitychange",cD,false);cH()});return}cH()}function ar(cC){if(y.readyState==="complete"){cC()}else{if(L.addEventListener){L.addEventListener("load",cC)}else{if(L.attachEvent){L.attachEvent("onload",cC)}}}}function aR(cF){var cC=false;if(y.attachEvent){cC=y.readyState==="complete"}else{cC=y.readyState!=="loading"}if(cC){cF();return}var cE;if(y.addEventListener){aa(y,"DOMContentLoaded",function cD(){y.removeEventListener("DOMContentLoaded",cD,false); +if(!cC){cC=true;cF()}})}else{if(y.attachEvent){y.attachEvent("onreadystatechange",function cD(){if(y.readyState==="complete"){y.detachEvent("onreadystatechange",cD);if(!cC){cC=true;cF()}}});if(y.documentElement.doScroll&&L===L.top){(function cD(){if(!cC){try{y.documentElement.doScroll("left")}catch(cG){setTimeout(cD,0);return}cC=true;cF()}}())}}}aa(L,"load",function(){if(!cC){cC=true;cF()}},false)}function b9(cC){var cD=cw(cC);if(cD&&cD.type){cD.href=k(cD.href);cp(cD.href,cD.type,undefined,null,cC)}}function b1(){return y.all&&!y.addEventListener}function ck(cC){var cE=cC.which;var cD=(typeof cC.button);if(!cE&&cD!=="undefined"){if(b1()){if(cC.button&1){cE=1}else{if(cC.button&2){cE=3}else{if(cC.button&4){cE=2}}}}else{if(cC.button===0||cC.button==="0"){cE=1}else{if(cC.button&1){cE=2}else{if(cC.button&2){cE=3}}}}}return cE}function bq(cC){switch(ck(cC)){case 1:return"left";case 2:return"middle";case 3:return"right"}}function aH(cC){return cC.target||cC.srcElement}function ak(cC){return function(cF){cF=cF||L.event; +var cE=bq(cF);var cG=aH(cF);if(cF.type==="click"){var cD=false;if(cC&&cE==="middle"){cD=true}if(cG&&!cD){b9(cG)}}else{if(cF.type==="mousedown"){if(cE==="middle"&&cG){ay=cE;ba=cG}else{ay=ba=null}}else{if(cF.type==="mouseup"){if(cE===ay&&cG===ba){b9(cG)}ay=ba=null}else{if(cF.type==="contextmenu"){b9(cG)}}}}}}function ae(cD,cC){aa(cD,"click",ak(cC),false);if(cC){aa(cD,"mouseup",ak(cC),false);aa(cD,"mousedown",ak(cC),false);aa(cD,"contextmenu",ak(cC),false)}}function bb(cD){if(!ad){ad=true;var cE,cC=aG(a9,"ignore"),cF=y.links;if(cF){for(cE=0;cE<cF.length;cE++){if(!cC.test(cF[cE].className)){ae(cF[cE],cD)}}}}}function az(cE,cG,cH){if(bL){return true}bL=true;var cI=false;var cF,cD;function cC(){cI=true}ar(function(){function cJ(cL){setTimeout(function(){if(!bL){return}cI=false;cH.trackVisibleContentImpressions();cJ(cL)},cL)}function cK(cL){setTimeout(function(){if(!bL){return}if(cI){cI=false;cH.trackVisibleContentImpressions()}cK(cL)},cL)}if(cE){cF=["scroll","resize"];for(cD=0;cD<cF.length;cD++){if(y.addEventListener){y.addEventListener(cF[cD],cC) +}else{L.attachEvent("on"+cF[cD],cC)}}cK(100)}if(cG&&cG>0){cG=parseInt(cG,10);cJ(cG)}})}function b7(){var cD,cF,cG={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"};if(!((new RegExp("MSIE")).test(f.userAgent))){if(f.mimeTypes&&f.mimeTypes.length){for(cD in cG){if(Object.prototype.hasOwnProperty.call(cG,cD)){cF=f.mimeTypes[cG[cD]];ct[cD]=(cF&&cF.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&B(f.javaEnabled)&&f.javaEnabled()){ct.java="1"}if(t(L.GearsFactory)){ct.gears="1"}ct.cookie=bB()}var cE=parseInt(P.width,10);var cC=parseInt(P.height,10);ct.res=parseInt(cE,10)+"x"+parseInt(cC,10)}b7();aU();at();this.getVisitorId=function(){return aE().uuid};this.getVisitorInfo=function(){return cd()};this.getAttributionInfo=function(){return bj()};this.getAttributionCampaignName=function(){return bj()[0] +};this.getAttributionCampaignKeyword=function(){return bj()[1]};this.getAttributionReferrerTimestamp=function(){return bj()[2]};this.getAttributionReferrerUrl=function(){return bj()[3]};this.setTrackerUrl=function(cC){am=cC};this.getTrackerUrl=function(){return am};this.addTracker=function(cC,cE){if(!cE){throw new Error("A siteId must be given to add a new tracker")}if(!B(cC)||null===cC){cC=this.getTrackerUrl()}var cD=new I(cC,cE);A.push(cD);return cD};this.getSiteId=function(){return bC};this.setSiteId=function(cC){bz(cC)};this.setUserId=function(cC){if(!B(cC)||!cC.length){return}a8=cC;bl=bD(a8).substr(0,16)};this.getUserId=function(){return a8};this.setCustomData=function(cC,cD){if(O(cC)){ac=cC}else{if(!ac){ac={}}ac[cC]=cD}};this.getCustomData=function(){return ac};this.setCustomRequestProcessing=function(cC){bH=cC};this.appendToTrackingUrl=function(cC){cn=cC};this.getRequest=function(cC){return bX(cC)};this.addPlugin=function(cC,cD){a[cC]=cD};this.setCustomDimension=function(cC,cD){cC=parseInt(cC,10); +if(cC>0){if(!B(cD)){cD=""}if(!q(cD)){cD=String(cD)}aY[cC]=cD}};this.getCustomDimension=function(cC){cC=parseInt(cC,10);if(cC>0&&Object.prototype.hasOwnProperty.call(aY,cC)){return aY[cC]}};this.deleteCustomDimension=function(cC){cC=parseInt(cC,10);if(cC>0){delete aY[cC]}};this.setCustomVariable=function(cD,cC,cG,cE){var cF;if(!B(cE)){cE="visit"}if(!B(cC)){return}if(!B(cG)){cG=""}if(cD>0){cC=!q(cC)?String(cC):cC;cG=!q(cG)?String(cG):cG;cF=[cC.slice(0,a4),cG.slice(0,a4)];if(cE==="visit"||cE===2){b6();ax[cD]=cF}else{if(cE==="page"||cE===3){bs[cD]=cF}else{if(cE==="event"){bR[cD]=cF}}}}};this.getCustomVariable=function(cD,cE){var cC;if(!B(cE)){cE="visit"}if(cE==="page"||cE===3){cC=bs[cD]}else{if(cE==="event"){cC=bR[cD]}else{if(cE==="visit"||cE===2){b6();cC=ax[cD]}}}if(!B(cC)||(cC&&cC[0]==="")){return false}return cC};this.deleteCustomVariable=function(cC,cD){if(this.getCustomVariable(cC,cD)){this.setCustomVariable(cC,"","",cD)}};this.storeCustomVariablesInCookie=function(){bn=true};this.setLinkTrackingTimer=function(cC){bg=cC +};this.setDownloadExtensions=function(cC){if(q(cC)){cC=cC.split("|")}cs=cC};this.addDownloadExtensions=function(cD){var cC;if(q(cD)){cD=cD.split("|")}for(cC=0;cC<cD.length;cC++){cs.push(cD[cC])}};this.removeDownloadExtensions=function(cE){var cD,cC=[];if(q(cE)){cE=cE.split("|")}for(cD=0;cD<cs.length;cD++){if(E(cE,cs[cD])===-1){cC.push(cs[cD])}}cs=cC};this.setDomains=function(cC){ai=q(cC)?[cC]:cC;var cG=false,cE=0,cD;for(cE;cE<ai.length;cE++){cD=String(ai[cE]);if(b8(cj,D(cD))){cG=true;break}var cF=bQ(cD);if(cF&&cF!=="/"&&cF!=="/*"){cG=true;break}}if(!cG){ai.push(cj)}};this.setIgnoreClasses=function(cC){a9=q(cC)?[cC]:cC};this.setRequestMethod=function(cC){cv=cC||bO};this.setRequestContentType=function(cC){b0=cC||aq};this.setReferrerUrl=function(cC){aZ=cC};this.setCustomUrl=function(cC){aK=br(bk,cC)};this.setDocumentTitle=function(cC){aV=cC};this.setAPIUrl=function(cC){bf=cC};this.setDownloadClasses=function(cC){bi=q(cC)?[cC]:cC};this.setLinkClasses=function(cC){aN=q(cC)?[cC]:cC};this.setCampaignNameKey=function(cC){bW=q(cC)?[cC]:cC +};this.setCampaignKeywordKey=function(cC){be=q(cC)?[cC]:cC};this.discardHashTag=function(cC){bm=cC};this.setCookieNamePrefix=function(cC){aW=cC;ax=bt()};this.setCookieDomain=function(cC){var cD=D(cC);if(a6(cD)){cl=cD;aU()}};this.setCookiePath=function(cC){a1=cC;aU()};this.setVisitorCookieTimeout=function(cC){cb=cC*1000};this.setSessionCookieTimeout=function(cC){bU=cC*1000};this.setReferralCookieTimeout=function(cC){cr=cC*1000};this.setConversionAttributionFirstReferrer=function(cC){a5=cC};this.disableCookies=function(){aX=true;ct.cookie="0";if(bC){an()}};this.deleteCookies=function(){an()};this.setDoNotTrack=function(cD){var cC=f.doNotTrack||f.msDoNotTrack;cf=cD&&(cC==="yes"||cC==="1");if(cf){this.disableCookies()}};this.addListener=function(cD,cC){ae(cD,cC)};this.enableLinkTracking=function(cC){cu=true;bN(function(){aR(function(){bb(cC)})})};this.enableJSErrorTracking=function(){if(ch){return}ch=true;var cC=L.onerror;L.onerror=function(cH,cF,cE,cG,cD){bN(function(){var cI="JavaScript Errors"; +var cJ=cF+":"+cE;if(cG){cJ+=":"+cG}ah(cI,cJ,cH)});if(cC){return cC(cH,cF,cE,cG,cD)}return false}};this.disablePerformanceTracking=function(){aI=false};this.setGenerationTimeMs=function(cC){bS=parseInt(cC,10)};this.enableHeartBeatTimer=function(cC){cC=Math.max(cC,1);aL=(cC||15)*1000;if(cm!==null){cA()}};this.killFrame=function(){if(L.location!==L.top.location){L.top.location=L.location}};this.redirectFile=function(cC){if(L.location.protocol==="file:"){L.location=cC}};this.setCountPreRendered=function(cC){aT=cC};this.trackGoal=function(cC,cE,cD){bN(function(){ci(cC,cE,cD)})};this.trackLink=function(cD,cC,cF,cE){bN(function(){cp(cD,cC,cF,cE)})};this.trackPageView=function(cC,cE,cD){bG=[];if(F(bC)){bN(function(){Q(am,bf,bC)})}else{bN(function(){bw(cC,cE,cD)})}};this.trackAllContentImpressions=function(){if(F(bC)){return}bN(function(){aR(function(){var cC=p.findContentNodes();var cD=b3(cC);cz(cD,bg)})})};this.trackVisibleContentImpressions=function(cC,cD){if(F(bC)){return}if(!B(cC)){cC=true}if(!B(cD)){cD=750 +}az(cC,cD,this);bN(function(){ar(function(){var cE=p.findContentNodes();var cF=aO(cE);cz(cF,bg)})})};this.trackContentImpression=function(cE,cC,cD){if(F(bC)){return}if(!cE){return}cC=cC||"Unknown";bN(function(){var cF=ao(cE,cC,cD);bd(cF,bg)})};this.trackContentImpressionsWithinNode=function(cC){if(F(bC)||!cC){return}bN(function(){if(bL){ar(function(){var cD=p.findContentNodesWithinNode(cC);var cE=aO(cD);cz(cE,bg)})}else{aR(function(){var cD=p.findContentNodesWithinNode(cC);var cE=b3(cD);cz(cE,bg)})}})};this.trackContentInteraction=function(cE,cF,cC,cD){if(F(bC)){return}if(!cE||!cF){return}cC=cC||"Unknown";bN(function(){var cG=aw(cE,cF,cC,cD);bd(cG,bg)})};this.trackContentInteractionNode=function(cD,cC){if(F(bC)||!cD){return}bN(function(){var cE=cx(cD,cC);bd(cE,bg)})};this.logAllContentBlocksOnPage=function(){var cD=p.findContentNodes();var cC=p.collectContent(cD);if(console!==undefined&&console&&console.log){console.log(cC)}};this.trackEvent=function(cD,cF,cC,cE,cG){bN(function(){ah(cD,cF,cC,cE,cG) +})};this.trackSiteSearch=function(cC,cE,cD,cF){bN(function(){bF(cC,cE,cD,cF)})};this.setEcommerceView=function(cF,cC,cE,cD){if(!B(cE)||!cE.length){cE=""}else{if(cE instanceof Array){cE=JSON2.stringify(cE)}}bs[5]=["_pkc",cE];if(B(cD)&&String(cD).length){bs[2]=["_pkp",cD]}if((!B(cF)||!cF.length)&&(!B(cC)||!cC.length)){return}if(B(cF)&&cF.length){bs[3]=["_pks",cF]}if(!B(cC)||!cC.length){cC=""}bs[4]=["_pkn",cC]};this.addEcommerceItem=function(cG,cC,cE,cD,cF){if(cG.length){co[cG]=[cG,cC,cE,cD,cF]}};this.trackEcommerceOrder=function(cC,cG,cF,cE,cD,cH){bv(cC,cG,cF,cE,cD,cH)};this.trackEcommerceCartUpdate=function(cC){a3(cC)};this.trackRequest=function(cC,cE,cD){bN(function(){var cF=bX(cC,cE);bd(cF,bg,cD)})};d.trigger("TrackerSetup",[this])}function z(){return{push:V}}function b(ah,ag){var ai={};var ae,af;for(ae=0;ae<ag.length;ae++){var ac=ag[ae];ai[ac]=1;for(af=0;af<ah.length;af++){if(ah[af]&&ah[af][0]){var ad=ah[af][0];if(ac===ad){V(ah[af]);delete ah[af];if(ai[ad]>1){Y("The method "+ad+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers') +}ai[ad]++}}}}return ah}var v=["addTracker","disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setDomains","setUserId","setSiteId","enableLinkTracking"];function T(ac,ae){var ad=new I(ac,ae);A.push(ad);_paq=b(_paq,v);for(x=0;x<_paq.length;x++){if(_paq[x]){V(_paq[x])}}_paq=new z();return ad}aa(L,"beforeunload",W,false);Date.prototype.getTimeAlias=Date.prototype.getTime;d={initialized:false,on:function(ad,ac){if(!r[ad]){r[ad]=[]}r[ad].push(ac)},off:function(ae,ad){if(!r[ae]){return}var ac=0;for(ac;ac<r[ae].length;ac++){if(r[ae][ac]===ad){delete r[ae][ac]}}},trigger:function(ae,af,ad){if(!r[ae]){return}var ac=0;for(ac;ac<r[ae].length;ac++){r[ae][ac].apply(ad||L,af)}},addPlugin:function(ac,ad){a[ac]=ad},getTracker:function(ac,ad){if(!B(ad)){ad=this.getAsyncTracker().getSiteId()}if(!B(ac)){ac=this.getAsyncTracker().getTrackerUrl()}return new I(ac,ad)},getAsyncTrackers:function(){return A},addTracker:function(ac,ad){if(!A.length){T(ac,ad)}else{A[0].addTracker(ac,ad) +}},getAsyncTracker:function(ad,ag){var af;if(A&&A[0]){af=A[0]}if(!ag&&!ad){return af}if((!B(ag)||null===ag)&&af){ag=af.getSiteId()}if((!B(ad)||null===ad)&&af){ad=af.getTrackerUrl()}var ae,ac=0;for(ac;ac<A.length;ac++){ae=A[ac];if(ae&&String(ae.getSiteId())===String(ag)&&ae.getTrackerUrl()===ad){return ae}}}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return d})}return d}()); +/*!!! pluginTrackerHook */ +}(function(){if(window&&"object"===typeof window.piwikPluginAsyncInit&&window.piwikPluginAsyncInit.length){var a=0;for(a;a<window.piwikPluginAsyncInit.length;a++){if(typeof window.piwikPluginAsyncInit[a]==="function"){window.piwikPluginAsyncInit[a]()}}}window.Piwik.addTracker();window.Piwik.trigger("PiwikInitialized",[]);window.Piwik.initialized=true}());if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=window.Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{if(window["piwik_"+h]){return window["piwik_"+h] +}}catch(i){}return}var c,e=window.Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}}; /*!! @license-end */ }; \ No newline at end of file diff --git a/plugins/Actions/javascripts/actionsDataTable.js b/plugins/Actions/javascripts/actionsDataTable.js index 5d96ff2891..b15958db3d 100644 --- a/plugins/Actions/javascripts/actionsDataTable.js +++ b/plugins/Actions/javascripts/actionsDataTable.js @@ -54,6 +54,7 @@ var self = this; self.cleanParams(); + self.preBindEventsAndApplyStyleHook(domElem, rows); if (!rows) { rows = $('tr', domElem); @@ -92,6 +93,7 @@ self.setFixWidthToMakeEllipsisWork(domElem); self.handleSummaryRow(domElem); self.openSubtableFromLevel0IfOnlyOneSubtableGiven(domElem); + self.postBindEventsAndApplyStyleHook(domElem, rows); }, openSubtableFromLevel0IfOnlyOneSubtableGiven: function (domElem) { diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js index e143af91dc..bb892a636c 100644 --- a/plugins/CoreHome/javascripts/dataTable.js +++ b/plugins/CoreHome/javascripts/dataTable.js @@ -302,6 +302,7 @@ $.extend(DataTable.prototype, UIControl.prototype, { bindEventsAndApplyStyle: function (domElem) { var self = this; self.cleanParams(); + self.preBindEventsAndApplyStyleHook(domElem); self.handleSort(domElem); self.handleLimit(domElem); self.handleSearchBox(domElem); @@ -321,6 +322,14 @@ $.extend(DataTable.prototype, UIControl.prototype, { self.handleExpandFooter(domElem); self.setFixWidthToMakeEllipsisWork(domElem); self.handleSummaryRow(domElem); + self.postBindEventsAndApplyStyleHook(domElem); + }, + + preBindEventsAndApplyStyleHook: function (domElem) { + + }, + postBindEventsAndApplyStyleHook: function (domElem) { + }, setFixWidthToMakeEllipsisWork: function (domElem) { diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php index 90066a187a..ca699274c0 100644 --- a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php +++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php @@ -26,7 +26,6 @@ class AllColumns extends HtmlTable public function beforeRender() { $this->config->show_extra_columns = true; - $this->config->datatable_css_class = 'dataTableVizAllColumns'; $this->config->show_exclude_low_population = true; parent::beforeRender(); @@ -34,6 +33,8 @@ class AllColumns extends HtmlTable public function beforeGenericFiltersAreAppliedToLoadedDataTable() { + $this->config->datatable_css_class = 'dataTableVizAllColumns'; + $this->dataTable->filter('AddColumnsProcessedMetrics'); $properties = $this->config; diff --git a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php index 121cfdc315..84b53bae88 100644 --- a/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php +++ b/plugins/CoreVisualizations/Visualizations/JqplotGraph/Bar.php @@ -20,10 +20,10 @@ class Bar extends JqplotGraph const ID = 'graphVerticalBar'; const FOOTER_ICON = 'plugins/Morpheus/images/chart_bar.png'; const FOOTER_ICON_TITLE = 'General_VBarGraph'; - - public function beforeRender() + + public function beforeLoadDataTable() { - parent::beforeRender(); + parent::beforeLoadDataTable(); $this->config->datatable_js_type = 'JqplotBarGraphDataTable'; } diff --git a/plugins/CustomPiwikJs/.gitignore b/plugins/CustomPiwikJs/.gitignore new file mode 100644 index 0000000000..c8c9480010 --- /dev/null +++ b/plugins/CustomPiwikJs/.gitignore @@ -0,0 +1 @@ +tests/System/processed/*xml \ No newline at end of file diff --git a/plugins/CustomPiwikJs/Commands/UpdateTracker.php b/plugins/CustomPiwikJs/Commands/UpdateTracker.php new file mode 100644 index 0000000000..70754bc20f --- /dev/null +++ b/plugins/CustomPiwikJs/Commands/UpdateTracker.php @@ -0,0 +1,62 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\Commands; + +use Piwik\Plugin\ConsoleCommand; +use Piwik\Plugins\CustomPiwikJs\TrackerUpdater; +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PluginTrackerFiles; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +class UpdateTracker extends ConsoleCommand +{ + protected function configure() + { + $this->setName('custom-piwik-js:update'); + $this->addOption('source-file', null, InputOption::VALUE_REQUIRED, 'Absolute path to source PiwikJS file.', $this->getPathOriginalPiwikJs()); + $this->addOption('target-file', null, InputOption::VALUE_REQUIRED, 'Absolute path to target file. Useful if your /piwik.js is not writable and you want to replace the file manually', PIWIK_DOCUMENT_ROOT . TrackerUpdater::TARGET_PIWIK_JS); + $this->addOption('ignore-minified', null, InputOption::VALUE_NONE, 'Ignore minified tracker files, useful during development so the original source file can be debugged'); + $this->setDescription('Update the Javascript Tracker with plugin tracker additions'); + } + + private function getPathOriginalPiwikJs() + { + return PIWIK_DOCUMENT_ROOT . TrackerUpdater::ORIGINAL_PIWIK_JS; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $sourceFile = $input->getOption('source-file'); + $targetFile = $input->getOption('target-file'); + $ignoreMinified = $input->hasOption('ignore-minified'); + + $this->updateTracker($sourceFile, $targetFile, $ignoreMinified); + + $output->writeln('<info>The Javascript Tracker has been updated</info>'); + } + + public function updateTracker($sourceFile, $targetFile, $ignoreMinified) + { + $pluginTrackerFiles = new PluginTrackerFiles(); + + if ($ignoreMinified) { + if (empty($sourceFile) || $sourceFile === $this->getPathOriginalPiwikJs()) { + // no custom source file was requested + $sourceFile = PIWIK_DOCUMENT_ROOT . TrackerUpdater::DEVELOPMENT_PIWIK_JS; + } + $pluginTrackerFiles->ignoreMinified(); + } + + $updater = new TrackerUpdater($sourceFile, $targetFile); + $updater->setTrackerFiles($pluginTrackerFiles); + $updater->checkWillSucceed(); + $updater->update(); + } +} diff --git a/plugins/CustomPiwikJs/CustomPiwikJs.php b/plugins/CustomPiwikJs/CustomPiwikJs.php new file mode 100644 index 0000000000..4089f8cba8 --- /dev/null +++ b/plugins/CustomPiwikJs/CustomPiwikJs.php @@ -0,0 +1,35 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs; + +use Piwik\Log; +use Piwik\Plugin; + +class CustomPiwikJs extends Plugin +{ + public function getListHooksRegistered() + { + return array( + 'CoreUpdater.update.end' => 'updateTracker', + 'PluginManager.pluginDeactivated' => 'updateTracker', + 'PluginManager.pluginActivated' => 'updateTracker', + 'CronArchive.end' => 'updateTracker', + ); + } + + public function updateTracker() + { + try { + $trackerUpdater = new TrackerUpdater(); + $trackerUpdater->update(); + } catch (\Exception $e) { + Log::error('There was an error while updating the javascript tracker: ' . $e->getMessage()); + } + } +} diff --git a/plugins/CustomPiwikJs/Diagnostic/PiwikJsCheck.php b/plugins/CustomPiwikJs/Diagnostic/PiwikJsCheck.php new file mode 100644 index 0000000000..3766dc650b --- /dev/null +++ b/plugins/CustomPiwikJs/Diagnostic/PiwikJsCheck.php @@ -0,0 +1,54 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Plugins\CustomPiwikJs\Diagnostic; + +use Piwik\Filechecks; +use Piwik\Filesystem; +use Piwik\Plugins\CustomPiwikJs\File; +use Piwik\Plugins\Diagnostics\Diagnostic\Diagnostic; +use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult; +use Piwik\SettingsServer; +use Piwik\Translation\Translator; + +/** + * Check Piwik JS is writable + */ +class PiwikJsCheck implements Diagnostic +{ + /** + * @var Translator + */ + private $translator; + + public function __construct(Translator $translator) + { + $this->translator = $translator; + } + + public function execute() + { + $label = $this->translator->translate('CustomPiwikJs_DiagnosticPiwikJsWritable'); + + $file = new File(PIWIK_DOCUMENT_ROOT . '/piwik.js'); + + if ($file->hasWriteAccess()) { + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_OK, '')); + } + + $comment = $this->translator->translate('CustomPiwikJs_DiagnosticPiwikJsNotWritable'); + + if(!SettingsServer::isWindows()) { + $realpath = Filesystem::realpath(PIWIK_INCLUDE_PATH . '/piwik.js'); + $command = "<br/><code> chmod +w $realpath<br/> chown ". Filechecks::getUserAndGroup() ." " . $realpath . "</code><br />"; + $comment .= $this->translator->translate('CustomPiwikJs_DiagnosticPiwikJsMakeWritable', $command); + } + + return array(DiagnosticResult::singleResult($label, DiagnosticResult::STATUS_WARNING, $comment)); + } + +} diff --git a/plugins/CustomPiwikJs/Exception/AccessDeniedException.php b/plugins/CustomPiwikJs/Exception/AccessDeniedException.php new file mode 100644 index 0000000000..356e63e7be --- /dev/null +++ b/plugins/CustomPiwikJs/Exception/AccessDeniedException.php @@ -0,0 +1,15 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\Exception; + +use Exception; + +class AccessDeniedException extends Exception +{ +} diff --git a/plugins/CustomPiwikJs/File.php b/plugins/CustomPiwikJs/File.php new file mode 100644 index 0000000000..79a93e7437 --- /dev/null +++ b/plugins/CustomPiwikJs/File.php @@ -0,0 +1,75 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs; + +use Piwik\Plugins\CustomPiwikJs\Exception\AccessDeniedException; + +class File +{ + /** + * @var string + */ + private $file; + + public function __construct($file) + { + $this->file = $file; + } + + public function checkReadable() + { + if (!$this->hasReadAccess()) { + throw new AccessDeniedException(sprintf('The file %s is not readable', $this->file)); + } + } + + public function checkWritable() + { + if (!$this->hasWriteAccess()) { + throw new AccessDeniedException(sprintf('The file %s is not writable', $this->file)); + } + } + + public function save($content) + { + file_put_contents($this->file, $content); + } + + public function getContent() + { + if (!$this->hasReadAccess()) { + return null; + } + + return file_get_contents($this->file); + } + + public function getName() + { + return basename($this->file); + } + + /** + * @return bool + */ + public function hasWriteAccess() + { + return $this->hasReadAccess() && is_writable($this->file); + } + + /** + * @return bool + */ + public function hasReadAccess() + { + return file_exists($this->file) && is_readable($this->file); + } + + +} diff --git a/plugins/CustomPiwikJs/Tasks.php b/plugins/CustomPiwikJs/Tasks.php new file mode 100644 index 0000000000..1bb1bae384 --- /dev/null +++ b/plugins/CustomPiwikJs/Tasks.php @@ -0,0 +1,23 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs; + +class Tasks extends \Piwik\Plugin\Tasks +{ + public function schedule() + { + $this->hourly('updateTracker'); + } + + public function updateTracker() + { + $updater = new TrackerUpdater(); + $updater->update(); + } +} diff --git a/plugins/CustomPiwikJs/TrackerUpdater.php b/plugins/CustomPiwikJs/TrackerUpdater.php new file mode 100644 index 0000000000..6370287882 --- /dev/null +++ b/plugins/CustomPiwikJs/TrackerUpdater.php @@ -0,0 +1,78 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs; + +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PiwikJsManipulator; +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PluginTrackerFiles; + +/** + * Updates the Javascript file containing the Tracker. + */ +class TrackerUpdater +{ + const DEVELOPMENT_PIWIK_JS = '/js/piwik.js'; + const ORIGINAL_PIWIK_JS = '/js/piwik.min.js'; + const TARGET_PIWIK_JS = '/piwik.js'; + + /** + * @var File + */ + private $fromFile; + + /** + * @var File + */ + private $toFile; + + private $trackerFiles = array(); + + /** + * @param string|null $fromFile If null then the minified JS tracker file in /js fill be used + * @param string|null $toFile If null then the minified JS tracker will be updated. + */ + public function __construct($fromFile = null, $toFile = null) + { + if (!isset($fromFile)) { + $fromFile = PIWIK_DOCUMENT_ROOT . self::ORIGINAL_PIWIK_JS; + } + + if (!isset($toFile)) { + $toFile = PIWIK_DOCUMENT_ROOT . self::TARGET_PIWIK_JS; + } + + $this->fromFile = new File($fromFile); + $this->toFile = new File($toFile); + $this->trackerFiles = new PluginTrackerFiles(); + } + + public function setTrackerFiles(PluginTrackerFiles $trackerFiles) + { + $this->trackerFiles = $trackerFiles; + } + + public function checkWillSucceed() + { + $this->fromFile->checkReadable(); + $this->toFile->checkWritable(); + } + + public function update() + { + if (!$this->toFile->hasWriteAccess() || !$this->fromFile->hasReadAccess()) { + return; + } + + $trackingCode = new PiwikJsManipulator($this->fromFile->getContent(), $this->trackerFiles); + $newContent = $trackingCode->manipulateContent(); + + if ($newContent !== $this->toFile->getContent()) { + $this->toFile->save($newContent); + } + } +} diff --git a/plugins/CustomPiwikJs/TrackingCode/JsTestPluginTrackerFiles.php b/plugins/CustomPiwikJs/TrackingCode/JsTestPluginTrackerFiles.php new file mode 100644 index 0000000000..773ac03286 --- /dev/null +++ b/plugins/CustomPiwikJs/TrackingCode/JsTestPluginTrackerFiles.php @@ -0,0 +1,27 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Plugins\CustomPiwikJs\TrackingCode; + +/** + * Used for when running Piwik tracker tests. We simply include all custom tracker files there. + */ +class JsTestPluginTrackerFiles extends PluginTrackerFiles +{ + + public function __construct() + { + parent::__construct(); + $this->ignoreMinified = true; + } + + protected function isPluginActivated($pluginName) + { + return true; + } + +} diff --git a/plugins/CustomPiwikJs/TrackingCode/PiwikJsManipulator.php b/plugins/CustomPiwikJs/TrackingCode/PiwikJsManipulator.php new file mode 100644 index 0000000000..affac1fd46 --- /dev/null +++ b/plugins/CustomPiwikJs/TrackingCode/PiwikJsManipulator.php @@ -0,0 +1,57 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\TrackingCode; + +class PiwikJsManipulator +{ + const HOOK = '/*!!! pluginTrackerHook */'; + /** + * @var string + */ + private $content; + + /** @var PluginTrackerFiles */ + private $pluginTrackerFiles; + + public function __construct($content, PluginTrackerFiles $pluginTrackerFiles) + { + $this->content = $content; + $this->pluginTrackerFiles = $pluginTrackerFiles; + } + + public function manipulateContent() + { + $files = $this->pluginTrackerFiles->find(); + + foreach ($files as $file) { + $trackerExtension = $this->getSignatureWithContent($file->getName(), $file->getContent()); + + // for some reasons it is /*!!! in piwik.js minified and /*!! in js/piwik.js unminified + $this->content = str_replace(array(self::HOOK, '/*!! pluginTrackerHook */'), self::HOOK . $trackerExtension, $this->content); + } + + return $this->content; + } + + /** + * @param string $name + * @param string $content + * @return string + */ + private function getSignatureWithContent($name, $content) + { + return sprintf( + "\n\n/* GENERATED: %s */\n%s\n/* END GENERATED: %s */\n", + $name, + $content, + $name + ); + } + +} diff --git a/plugins/CustomPiwikJs/TrackingCode/PluginTrackerFiles.php b/plugins/CustomPiwikJs/TrackingCode/PluginTrackerFiles.php new file mode 100644 index 0000000000..f114565990 --- /dev/null +++ b/plugins/CustomPiwikJs/TrackingCode/PluginTrackerFiles.php @@ -0,0 +1,87 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Plugins\CustomPiwikJs\TrackingCode; + +use Piwik\Filesystem; +use Piwik\Plugin; +use Piwik\Plugins\CustomPiwikJs\File; + +class PluginTrackerFiles +{ + const TRACKER_FILE = 'tracker.js'; + const MIN_TRACKER_FILE = 'tracker.min.js'; + + /** + * @var string + */ + protected $dir; + + /** + * @var Plugin\Manager + */ + private $pluginManager; + + /** + * @var bool + */ + protected $ignoreMinified = false; + + public function __construct() + { + $this->dir = PIWIK_DOCUMENT_ROOT . '/plugins/'; + $this->pluginManager = Plugin\Manager::getInstance(); + } + + public function ignoreMinified() + { + $this->ignoreMinified = true; + } + + /** + * @return File[] + */ + public function find() + { + $jsFiles = array(); + + if (!$this->ignoreMinified) { + $trackerFiles = \_glob($this->dir . '*/' . self::MIN_TRACKER_FILE); + + foreach ($trackerFiles as $trackerFile) { + $plugin = $this->getPluginNameFromFile($trackerFile); + if ($this->isPluginActivated($plugin)) { + $jsFiles[$plugin] = new File($trackerFile); + } + } + } + + $trackerFiles = \_glob($this->dir . '*/' . self::TRACKER_FILE); + + foreach ($trackerFiles as $trackerFile) { + $plugin = $this->getPluginNameFromFile($trackerFile); + if (!isset($jsFiles[$plugin])) { + if ($this->isPluginActivated($plugin)) { + $jsFiles[$plugin] = new File($trackerFile); + } + } + } + + return $jsFiles; + } + + protected function isPluginActivated($pluginName) + { + return $this->pluginManager->isPluginActivated($pluginName); + } + + protected function getPluginNameFromFile($file) + { + $file = str_replace(array($this->dir, self::TRACKER_FILE, self::MIN_TRACKER_FILE), '', $file); + return trim($file, '/'); + } +} diff --git a/plugins/CustomPiwikJs/config/config.php b/plugins/CustomPiwikJs/config/config.php new file mode 100644 index 0000000000..cafd3336ac --- /dev/null +++ b/plugins/CustomPiwikJs/config/config.php @@ -0,0 +1,7 @@ +<?php + +return array( + 'diagnostics.optional' => DI\add(array( + DI\get('Piwik\Plugins\CustomPiwikJs\Diagnostic\PiwikJsCheck'), + )), +); diff --git a/plugins/CustomPiwikJs/lang/en.json b/plugins/CustomPiwikJs/lang/en.json new file mode 100644 index 0000000000..57dbea22a6 --- /dev/null +++ b/plugins/CustomPiwikJs/lang/en.json @@ -0,0 +1,7 @@ +{ + "CustomPiwikJs": { + "DiagnosticPiwikJsWritable": "Writable JavaScript Tracker (\"/piwik.js\")", + "DiagnosticPiwikJsNotWritable": "The Piwik JavaScript tracker file \"/piwik.js\" is not writable which means other plugins cannot extend the JavaScript tracker. In the future even some core features might not work as expected. ", + "DiagnosticPiwikJsMakeWritable": "We recommend to Piwik.js writable by running this command: %s" + } +} \ No newline at end of file diff --git a/plugins/CustomPiwikJs/tests/Framework/Mock/PluginTrackerFilesMock.php b/plugins/CustomPiwikJs/tests/Framework/Mock/PluginTrackerFilesMock.php new file mode 100644 index 0000000000..2503353ec0 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/Framework/Mock/PluginTrackerFilesMock.php @@ -0,0 +1,36 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\tests\Framework\Mock; + +use Piwik\Plugins\CustomPiwikJs\File; +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PluginTrackerFiles; + +class PluginTrackerFilesMock extends PluginTrackerFiles +{ + /** + * @var array + */ + private $files; + + public function __construct($files) + { + $this->files = $files; + } + + public function find() + { + $files = array(); + foreach ($this->files as $file) { + $files[] = new File(PIWIK_DOCUMENT_ROOT . $file); + } + return $files; + } + + +} diff --git a/plugins/CustomPiwikJs/tests/Integration/FileTest.php b/plugins/CustomPiwikJs/tests/Integration/FileTest.php new file mode 100644 index 0000000000..4d96bd6478 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/Integration/FileTest.php @@ -0,0 +1,125 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\tests\Integration; + +use Piwik\Plugins\CustomPiwikJs\File; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group CustomPiwikJs + * @group FileTest + * @group File + * @group Plugins + */ +class FileTest extends IntegrationTestCase +{ + const NOT_EXISTING_FILE = 'notExisTinGFile.js'; + + /** + * @var string + */ + private $dir = ''; + + public function setUp() + { + parent::setUp(); + $this->dir = PIWIK_DOCUMENT_ROOT . '/plugins/CustomPiwikJs/tests/resources/'; + } + + public function tearDown() + { + if (file_exists($this->dir . self::NOT_EXISTING_FILE)) { + unlink($this->dir . self::NOT_EXISTING_FILE); + } + + parent::tearDown(); + } + + private function makeFile($fileName = 'test.js') + { + return new File($this->dir . $fileName); + } + + private function makeNotReadableFile() + { + return $this->makeFile(self::NOT_EXISTING_FILE); + } + + public function test_getName() + { + $this->assertSame('test.js', $this->makeFile()->getName()); + $this->assertSame('notExisTinGFile.js', $this->makeNotReadableFile()->getName()); + } + + public function test_hasReadAccess() + { + $this->assertTrue($this->makeFile()->hasReadAccess()); + $this->assertFalse($this->makeNotReadableFile()->hasReadAccess()); + } + + public function test_hasWriteAccess() + { + $this->assertTrue($this->makeFile()->hasWriteAccess()); + $this->assertFalse($this->makeNotReadableFile()->hasWriteAccess()); + } + + public function test_checkReadable_shouldNotThrowException_IfIsReadable() + { + $this->makeFile()->checkReadable(); + $this->assertTrue(true); + } + + public function test_checkWritable_shouldNotThrowException_IfIsWritable() + { + $this->makeFile()->checkWritable(); + $this->assertTrue(true); + } + + /** + * @expectedException \Piwik\Plugins\CustomPiwikJs\Exception\AccessDeniedException + * @expectedExceptionMessage not readable + */ + public function test_checkReadable_shouldThrowException_IfNotIsReadable() + { + $this->makeNotReadableFile()->checkReadable(); + } + + /** + * @expectedException \Piwik\Plugins\CustomPiwikJs\Exception\AccessDeniedException + * @expectedExceptionMessage not writable + */ + public function test_checkWritable_shouldThrowException_IfNotIsWritable() + { + $this->makeNotReadableFile()->checkWritable(); + } + + public function test_getContent() + { + $this->assertSame("// Hello world\nvar fooBar = 'test';", $this->makeFile()->getContent()); + } + + public function test_getContent_returnsNull_IfFileIsNotReadableOrNotExists() + { + $this->assertNull($this->makeNotReadableFile()->getContent()); + } + + public function test_save() + { + $notExistingFile = $this->makeNotReadableFile(); + $this->assertFalse($notExistingFile->hasReadAccess()); + $this->assertFalse($notExistingFile->hasWriteAccess()); + + $notExistingFile->save('myTestContent'); + + $this->assertEquals('myTestContent', $notExistingFile->getContent()); + $this->assertTrue($notExistingFile->hasReadAccess()); + $this->assertTrue($notExistingFile->hasWriteAccess()); + } + +} diff --git a/plugins/CustomPiwikJs/tests/Integration/PiwikJsManipulatorTest.php b/plugins/CustomPiwikJs/tests/Integration/PiwikJsManipulatorTest.php new file mode 100644 index 0000000000..75c66b73f7 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/Integration/PiwikJsManipulatorTest.php @@ -0,0 +1,73 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\tests\Integration; + +use Piwik\Plugins\CustomPiwikJs\tests\Framework\Mock\PluginTrackerFilesMock; +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PiwikJsManipulator; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group CustomPiwikJs + * @group PiwikJsManipulatorTest + * @group PiwikJsManipulator + * @group Plugins + */ +class PiwikJsManipulatorTest extends IntegrationTestCase +{ + private $content = 'var Piwik.js = "mytest"; +/*!!! pluginTrackerHook */ + +var myArray = []; +'; + + public function test_manipulateContent_shouldAddCodeOfTrackerPlugins() + { + $manipulator = $this->makeManipulator(array( + '/plugins/CustomPiwikJs/tests/resources/tracker.js', + '/plugins/CustomPiwikJs/tests/resources/tracker.min.js', + )); + + $updatedContent = $manipulator->manipulateContent(); + + $this->assertSame('var Piwik.js = "mytest"; +/*!!! pluginTrackerHook */ + +/* GENERATED: tracker.min.js */ +/* my license header */ +var mySecondCustomTracker = \'test\'; +/* END GENERATED: tracker.min.js */ + + +/* GENERATED: tracker.js */ +/** my license header*/ +var myCustomTracker = \'test\'; + +var fooBar = \'baz\'; +/* END GENERATED: tracker.js */ + + +var myArray = []; +', $updatedContent); + } + + public function test_manipulateContent_shouldNotAddCodeOfTrackerPlugins_IfThereAreNoTrackerFiles() + { + $manipulator = $this->makeManipulator(array()); + + $updatedContent = $manipulator->manipulateContent(); + + $this->assertSame($this->content, $updatedContent); + } + + private function makeManipulator($files) + { + return new PiwikJsManipulator($this->content, new PluginTrackerFilesMock($files)); + } + +} diff --git a/plugins/CustomPiwikJs/tests/Integration/PluginTrackerFilesTest.php b/plugins/CustomPiwikJs/tests/Integration/PluginTrackerFilesTest.php new file mode 100644 index 0000000000..8c0cd2ee01 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/Integration/PluginTrackerFilesTest.php @@ -0,0 +1,124 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\tests\Integration; + +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PluginTrackerFiles; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +class CustomPluginTrackerFiles extends PluginTrackerFiles { + + private $pluginNamesForFile = array(); + + public function __construct($pluginNameForRegularTrackerFile = 'CustomPiwikJs', $pluginNameForMinifiedTracker = 'CustomPiwikJs') + { + parent::__construct(); + + $this->dir = PIWIK_DOCUMENT_ROOT . '/plugins/CustomPiwikJs/tests/'; + + $this->pluginNamesForFile = array( + 'tracker.js' => $pluginNameForRegularTrackerFile, + 'tracker.min.js' => $pluginNameForMinifiedTracker + ); + } + + protected function getPluginNameFromFile($file) + { + $fileName = basename($file); + return $this->pluginNamesForFile[$fileName]; + } +} + +class CustomPluginTrackerFiles2 extends PluginTrackerFiles { + + public function getPluginNameFromFile($file) + { + return parent::getPluginNameFromFile($file); + } +} + +/** + * @group CustomPiwikJs + * @group PluginTrackerFilesTest + * @group PluginTrackerFiles + * @group Plugins + */ +class PluginTrackerFilesTest extends IntegrationTestCase +{ + public function test_find_ifAPluginDefinesAMinifiedAndARegularTrackerItShouldPreferTheMinifiedVersion() + { + $trackerFiles = new CustomPluginTrackerFiles(); + $foundFiles = $trackerFiles->find(); + + $this->assertCount(1, $foundFiles); + $this->assertTrue(isset($foundFiles['CustomPiwikJs'])); + $this->assertEquals('tracker.min.js', $foundFiles['CustomPiwikJs']->getName()); + } + + public function test_find_shouldIgnoreMinifiedVersion_IfRequested() + { + $trackerFiles = new CustomPluginTrackerFiles(); + $trackerFiles->ignoreMinified(); + $foundFiles = $trackerFiles->find(); + + $this->assertCount(1, $foundFiles); + $this->assertTrue(isset($foundFiles['CustomPiwikJs'])); + $this->assertEquals('tracker.js', $foundFiles['CustomPiwikJs']->getName()); + } + + public function test_find_ifMultiplePluginsImplementATracker_ShouldReturnEachOfThem() + { + $trackerFiles = new CustomPluginTrackerFiles('CustomPiwikJs', 'Goals'); + $foundFiles = $trackerFiles->find(); + + $this->assertCount(2, $foundFiles); + $this->assertTrue(isset($foundFiles['CustomPiwikJs'])); + $this->assertTrue(isset($foundFiles['Goals'])); + $this->assertEquals('tracker.js', $foundFiles['CustomPiwikJs']->getName()); + $this->assertEquals('tracker.min.js', $foundFiles['Goals']->getName()); + } + + public function test_find_shouldNotReturnATrackerFile_IfPluginIsNotActivatedOrLoaded() + { + $trackerFiles = new CustomPluginTrackerFiles('MyNotExistingPlugin', 'Goals'); + $foundFiles = $trackerFiles->find(); + + $this->assertCount(1, $foundFiles); + $this->assertTrue(isset($foundFiles['Goals'])); + $this->assertEquals('tracker.min.js', $foundFiles['Goals']->getName()); + + $trackerFiles = new CustomPluginTrackerFiles('Goals', 'MyNotExistingPlugin'); + $foundFiles = $trackerFiles->find(); + + $this->assertCount(1, $foundFiles); + $this->assertTrue(isset($foundFiles['Goals'])); + $this->assertEquals('tracker.js', $foundFiles['Goals']->getName()); + } + + public function test_find_shouldNotReturnFileIfNoPluginActivated() + { + $trackerFiles = new CustomPluginTrackerFiles('MyNotExistingPlugin', 'MyNotExistingPlugin2'); + $foundFiles = $trackerFiles->find(); + + $this->assertSame(array(), $foundFiles); + } + + public function test_getPluginNameFromFile_shouldDetectPluginName() + { + $trackerFiles = new CustomPluginTrackerFiles2(); + $pluginName = $trackerFiles->getPluginNameFromFile(PIWIK_DOCUMENT_ROOT . '/plugins/MyFooBarPlugin/tracker.js'); + $this->assertSame('MyFooBarPlugin', $pluginName); + + $pluginName = $trackerFiles->getPluginNameFromFile(PIWIK_DOCUMENT_ROOT . '/plugins//MyFooBarPlugin//tracker.js'); + $this->assertSame('MyFooBarPlugin', $pluginName); + + $pluginName = $trackerFiles->getPluginNameFromFile(PIWIK_DOCUMENT_ROOT . '/plugins//MyFooBarPlugin//tracker.min.js'); + $this->assertSame('MyFooBarPlugin', $pluginName); + } + +} diff --git a/plugins/CustomPiwikJs/tests/Integration/TrackerUpdaterTest.php b/plugins/CustomPiwikJs/tests/Integration/TrackerUpdaterTest.php new file mode 100644 index 0000000000..240638c5e9 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/Integration/TrackerUpdaterTest.php @@ -0,0 +1,121 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\tests\Integration; + +use Piwik\Plugins\CustomPiwikJs\tests\Framework\Mock\PluginTrackerFilesMock; +use Piwik\Plugins\CustomPiwikJs\TrackerUpdater; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; + +/** + * @group CustomPiwikJs + * @group PiwikJsManipulatorTest + * @group PiwikJsManipulator + * @group Plugins + */ +class TrackerUpdaterTest extends IntegrationTestCase +{ + private $dir; + + public function setUp() + { + parent::setUp(); + $this->dir = PIWIK_DOCUMENT_ROOT . '/plugins/CustomPiwikJs/tests/resources/'; + + $this->cleanUp(); + } + + public function tearDown() + { + parent::tearDown(); + + $this->cleanUp(); + } + + private function cleanUp() + { + $target = $this->dir . 'MyTestTarget.js'; + if (file_exists($target)) { + unlink($target); + } + } + + private function makeUpdater($from = null, $to = null) + { + return new TrackerUpdater($from, $to); + } + + public function test_checkWillSucceed_shouldNotThrowExceptionIfPiwikJsTargetIsWritable() + { + $updater = $this->makeUpdater(); + $updater->checkWillSucceed(); + + $this->assertTrue(true); + } + + /** + * @expectedException \Piwik\Plugins\CustomPiwikJs\Exception\AccessDeniedException + * @expectedExceptionMessage not writable + */ + public function test_checkWillSucceed_shouldThrowExceptionIfTargetIsNotWritable() + { + $updater = $this->makeUpdater(null, $this->dir . 'MyNotExisIngFilessss.js'); + $updater->checkWillSucceed(); + } + + public function test_update_shouldNotThrowAnError_IfTargetFileIsNotWritable() + { + $updater = $this->makeUpdater(null, $this->dir . 'MyNotExisIngFilessss.js'); + $updater->update(); + $this->assertTrue(true); + } + + public function test_update_shouldNotWriteToFileIfThereIsNothingToChange() + { + $source = $this->dir . 'testpiwik.js'; + $target = $this->dir . 'MyTestTarget.js'; + file_put_contents($target, file_get_contents($source)); + $updater = $this->makeUpdater($this->dir . 'testpiwik.js', $target); + $updater->setTrackerFiles(new PluginTrackerFilesMock(array())); + // mock that does not find any files . therefore there is nothing to di + $updater->update(); + + $this->assertSame(file_get_contents($source), file_get_contents($target)); + } + + public function test_update_targetFileIfPluginsDefineDifferentFiles() + { + $target = $this->dir . 'MyTestTarget.js'; + file_put_contents($target, ''); // file has to exist in order to work + + $updater = $this->makeUpdater($this->dir . 'testpiwik.js', $target); + $updater->setTrackerFiles(new PluginTrackerFilesMock(array( + $this->dir . 'tracker.js', $this->dir . 'tracker.min.js' + ))); + $updater->update(); + + $this->assertSame('/** MyHeader*/ +var PiwikJs = "mytest"; + +/*!!! pluginTrackerHook */ + +/* GENERATED: tracker.min.js */ + +/* END GENERATED: tracker.min.js */ + + +/* GENERATED: tracker.js */ + +/* END GENERATED: tracker.js */ + + +var myArray = []; +', file_get_contents($target)); + } + +} diff --git a/plugins/CustomPiwikJs/tests/System/PiwikJsContentTest.php b/plugins/CustomPiwikJs/tests/System/PiwikJsContentTest.php new file mode 100644 index 0000000000..dd3f08ab3a --- /dev/null +++ b/plugins/CustomPiwikJs/tests/System/PiwikJsContentTest.php @@ -0,0 +1,39 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CustomPiwikJs\tests\System; + +use Piwik\Plugins\CustomPiwikJs\TrackerUpdater; +use Piwik\Plugins\CustomPiwikJs\TrackingCode\PiwikJsManipulator; +use Piwik\Tests\Framework\TestCase\SystemTestCase; + +/** + * @group CustomPiwikJs + * @group PiwikJsContentTest + * @group PiwikJsContent + * @group Plugins + */ +class PiwikJsContentTest extends SystemTestCase +{ + public function test_piwikJsAndPiwikMinJsMustHaveSameContent() + { + $piwikMin = PIWIK_DOCUMENT_ROOT . TrackerUpdater::ORIGINAL_PIWIK_JS; + $piwikJs = PIWIK_DOCUMENT_ROOT . TrackerUpdater::TARGET_PIWIK_JS; + + $this->assertSame(file_get_contents($piwikMin), file_get_contents($piwikJs)); + } + + public function test_piwikJsContainsHook() + { + $piwikMin = PIWIK_DOCUMENT_ROOT . '/js/piwik.min.js'; + $content = file_get_contents($piwikMin); + + $this->assertContains(PiwikJsManipulator::HOOK, $content); + } + +} \ No newline at end of file diff --git a/plugins/CustomPiwikJs/tests/resources/MyTestTarget2.js b/plugins/CustomPiwikJs/tests/resources/MyTestTarget2.js new file mode 100644 index 0000000000..258c2d3e11 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/resources/MyTestTarget2.js @@ -0,0 +1,16 @@ +/** MyHeader*/ +var PiwikJs = "mytest"; + +/*!!! pluginTrackerHook */ + +/* GENERATED: tracker.min.js */ + +/* END GENERATED: tracker.min.js */ + + +/* GENERATED: tracker.js */ + +/* END GENERATED: tracker.js */ + + +var myArray = []; diff --git a/plugins/CustomPiwikJs/tests/resources/test.js b/plugins/CustomPiwikJs/tests/resources/test.js new file mode 100644 index 0000000000..0ea6fcc24a --- /dev/null +++ b/plugins/CustomPiwikJs/tests/resources/test.js @@ -0,0 +1,2 @@ +// Hello world +var fooBar = 'test'; \ No newline at end of file diff --git a/plugins/CustomPiwikJs/tests/resources/testpiwik.js b/plugins/CustomPiwikJs/tests/resources/testpiwik.js new file mode 100644 index 0000000000..02b60f8bcd --- /dev/null +++ b/plugins/CustomPiwikJs/tests/resources/testpiwik.js @@ -0,0 +1,6 @@ +/** MyHeader*/ +var PiwikJs = "mytest"; + +/*!!! pluginTrackerHook */ + +var myArray = []; diff --git a/plugins/CustomPiwikJs/tests/resources/tracker.js b/plugins/CustomPiwikJs/tests/resources/tracker.js new file mode 100644 index 0000000000..ae4d228f39 --- /dev/null +++ b/plugins/CustomPiwikJs/tests/resources/tracker.js @@ -0,0 +1,4 @@ +/** my license header*/ +var myCustomTracker = 'test'; + +var fooBar = 'baz'; \ No newline at end of file diff --git a/plugins/CustomPiwikJs/tests/resources/tracker.min.js b/plugins/CustomPiwikJs/tests/resources/tracker.min.js new file mode 100644 index 0000000000..587ee0464d --- /dev/null +++ b/plugins/CustomPiwikJs/tests/resources/tracker.min.js @@ -0,0 +1,2 @@ +/* my license header */ +var mySecondCustomTracker = 'test'; \ No newline at end of file diff --git a/plugins/Live/javascripts/live.js b/plugins/Live/javascripts/live.js index 36b59326a8..42231280f1 100644 --- a/plugins/Live/javascripts/live.js +++ b/plugins/Live/javascripts/live.js @@ -65,7 +65,14 @@ ajaxRequest.addParams(this.options.dataUrlParams, 'GET'); ajaxRequest.setFormat('html'); ajaxRequest.setCallback(function (r) { - that._parseResponse(r); + if (that.options.replaceContent) { + $(that.element).html(r); + if (that.options.fadeInSpeed) { + $(that.element).effect("highlight", {}, that.options.fadeInSpeed); + } + } else { + that._parseResponse(r); + } // add default interval to last interval if not updated or reset to default if so if (!that.updated) { diff --git a/tests/PHPUnit/Integration/ReleaseCheckListTest.php b/tests/PHPUnit/Integration/ReleaseCheckListTest.php index d42a310626..bfc4ec13e3 100644 --- a/tests/PHPUnit/Integration/ReleaseCheckListTest.php +++ b/tests/PHPUnit/Integration/ReleaseCheckListTest.php @@ -438,8 +438,12 @@ class ReleaseCheckListTest extends \PHPUnit_Framework_TestCase shell_exec("sed '/<DEBUG>/,/<\/DEBUG>/d' < ". PIWIK_DOCUMENT_ROOT ."/js/piwik.js | sed 's/eval/replacedEvilString/' | java -jar yuicompressor-2.4.7/build/yuicompressor-2.4.7.jar --type js --line-break 1000 | sed 's/replacedEvilString/eval/' | sed 's/^[/][*]/\/*!/' > " . PIWIK_DOCUMENT_ROOT ."/piwik-minified.js"); $this->assertFileEquals(PIWIK_DOCUMENT_ROOT . '/piwik-minified.js', - PIWIK_DOCUMENT_ROOT . '/piwik.js', - 'minified /piwik.js is out of date, please re-generate the minified /piwik.js using instructions in /js/README' + PIWIK_DOCUMENT_ROOT . '/piwik.js', + 'minified /piwik.js is out of date, please re-generate the minified files using instructions in /js/README' + ); + $this->assertFileEquals(PIWIK_DOCUMENT_ROOT . '/piwik-minified.js', + PIWIK_DOCUMENT_ROOT . '/js/piwik.min.js', + 'minified /js/piwik.min.js is out of date, please re-generate the minified files using instructions in /js/README' ); } diff --git a/tests/UI/expected-ui-screenshots b/tests/UI/expected-ui-screenshots index 9291b72602..f1b6e9f1ae 160000 --- a/tests/UI/expected-ui-screenshots +++ b/tests/UI/expected-ui-screenshots @@ -1 +1 @@ -Subproject commit 9291b72602d31ef1de5285ffa76685bab322ecf9 +Subproject commit f1b6e9f1ae4eef30dbcd95e3995d55bb91f97e9a diff --git a/tests/angularjs/Gruntfile.js b/tests/angularjs/Gruntfile.js index 2e50318f95..3527b5973a 100644 --- a/tests/angularjs/Gruntfile.js +++ b/tests/angularjs/Gruntfile.js @@ -2,6 +2,12 @@ module.exports = function(grunt) { // Project configuration. grunt.initConfig({ + karma: { + unit: { + configFile: 'karma.conf.js', + autoWatch: true + } + }, pkg: grunt.file.readJSON('package.json'), watch: { scripts: { @@ -13,7 +19,14 @@ module.exports = function(grunt) { }, piwikjs: { files: ['js/piwik.js'], - tasks: ["shell:compilePiwikJs"], + tasks: ["shell:compilePiwikJs", "shell:updateTracker"], + options: { + spawn: false, + }, + }, + piwikjs2: { + files: ['plugins/*/tracker.js',], + tasks: ["shell:updateTracker"], options: { spawn: false, }, @@ -27,6 +40,14 @@ module.exports = function(grunt) { cwd: 'js' } } + }, + updateTracker: { + command: "php console custom-piwik-js:update --ignore-minified", + options: { + execOptions: { + cwd: '' + } + } } }, "clean-pattern": { diff --git a/tests/angularjs/karma.conf.js b/tests/angularjs/karma.conf.js index 9525c7e06b..780fdb6d8e 100755 --- a/tests/angularjs/karma.conf.js +++ b/tests/angularjs/karma.conf.js @@ -20,7 +20,7 @@ module.exports = function(config) { "libs/bower_components/angular-animate/angular-animate.js", 'libs/bower_components/angular-mocks/angular-mocks.js', 'libs/bower_components/jquery/dist/jquery.min.js', - "libs/bower_components/jquery-ui/jquery-ui.min.js", + "libs/bower_components/jquery-ui/ui/minified/jquery-ui.min.js", "plugins/CoreHome/javascripts/require.js", "plugins/Morpheus/javascripts/piwikHelper.js", "plugins/Morpheus/javascripts/ajaxHelper.js", @@ -68,7 +68,7 @@ module.exports = function(config) { // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher - browsers: ['PhantomJS'], + browsers: [], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits diff --git a/tests/angularjs/package.json b/tests/angularjs/package.json index 74b8e44936..0d8daedeed 100644 --- a/tests/angularjs/package.json +++ b/tests/angularjs/package.json @@ -24,5 +24,8 @@ "engines": { "node": ">= 0.10.0" }, - "version": "0.1.0" + "version": "0.1.0", + "devDependencies": { + "grunt-karma": "^2.0.0" + } } diff --git a/tests/javascript/index.php b/tests/javascript/index.php index 043a9ef8c1..c03d06ba22 100644 --- a/tests/javascript/index.php +++ b/tests/javascript/index.php @@ -21,6 +21,20 @@ try { $mysql = false; } +use \Piwik\Plugins\CustomPiwikJs\TrackerUpdater; +use \Piwik\Plugins\CustomPiwikJs\TrackingCode\JsTestPluginTrackerFiles; + +$targetFileName = '/tests/resources/piwik.test.js'; +$sourceFile = PIWIK_DOCUMENT_ROOT . TrackerUpdater::DEVELOPMENT_PIWIK_JS; +$targetFile = PIWIK_DOCUMENT_ROOT . $targetFileName; + +file_put_contents($targetFile, ''); + +$updater = new TrackerUpdater($sourceFile, $targetFile); +$updater->setTrackerFiles(new JsTestPluginTrackerFiles()); +$updater->checkWillSucceed(); +$updater->update(); + if(file_exists("stub.tpl")) { echo file_get_contents("stub.tpl"); } @@ -60,7 +74,7 @@ testTrackPageViewAsync(); ?> </script> <script src="../lib/q-1.4.1/q.js" type="text/javascript"></script> - <script src="../../js/piwik.js?rand=<?php echo $cacheBuster ?>" type="text/javascript"></script> + <script src="../..<?php echo $targetFileName ?>?rand=<?php echo $cacheBuster ?>" type="text/javascript"></script> <script src="../../plugins/Overlay/client/urlnormalizer.js" type="text/javascript"></script> <script src="piwiktest.js" type="text/javascript"></script> <link rel="stylesheet" href="assets/qunit.css" type="text/css" media="screen" /> @@ -1971,7 +1985,7 @@ function PiwikTest() { }); test("API methods", function() { - expect(70); + expect(71); equal( typeof Piwik.addPlugin, 'function', 'addPlugin' ); equal( typeof Piwik.addPlugin, 'function', 'addTracker' ); @@ -2039,6 +2053,7 @@ function PiwikTest() { equal( typeof tracker.trackGoal, 'function', 'trackGoal' ); equal( typeof tracker.trackLink, 'function', 'trackLink' ); equal( typeof tracker.trackPageView, 'function', 'trackPageView' ); + equal( typeof tracker.trackRequest, 'function', 'trackRequest' ); // content equal( typeof tracker.trackAllContentImpressions, 'function', 'trackAllContentImpressions' ); equal( typeof tracker.trackVisibleContentImpressions, 'function', 'trackVisibleContentImpressions' ); @@ -2997,7 +3012,7 @@ function PiwikTest() { // Calling undefined methods should generate an error function callNonExistingMethod() { - _paq.push(['NonExistingFunction should error and display the error in the console.']); + _paq.push(['NonExistingFunction should error and display the error in the console']); } function callNonExistingMethodWithParameter() { _paq.push(['NonExistingFunction should not error', 'this is a parameter']); @@ -3172,7 +3187,7 @@ if ($mysql) { }); test("tracking", function() { - expect(118); + expect(119); // Prevent Opera and HtmlUnit from performing the default action (i.e., load the href URL) var stopEvent = function (evt) { @@ -3351,6 +3366,9 @@ if ($mysql) { var visitorIdEnd = tracker.getVisitorId(); ok( visitorIdStart == visitorIdEnd, "tracker.getVisitorId() same at the start and end of process"); + // Tracker custom request + tracker.trackRequest('myFoo=bar&baz=1'); + // Custom variables tracker.storeCustomVariablesInCookie(); tracker.setCookieNamePrefix("PREFIX"); @@ -3495,7 +3513,7 @@ if ($mysql) { xhr.open("GET", "piwik.php?requests=" + getToken(), false); xhr.send(null); results = xhr.responseText; - equal( (/<span\>([0-9]+)\<\/span\>/.exec(results))[1], "36", "count tracking events" ); + equal( (/<span\>([0-9]+)\<\/span\>/.exec(results))[1], "37", "count tracking events" ); // firing callback ok( trackLinkCallbackFired, "trackLink() callback fired" ); @@ -3529,6 +3547,9 @@ if ($mysql) { ok( /DoTrack/.test( results ), "setDoNotTrack(false)" ); ok( ! /DoNotTrack/.test( results ), "setDoNotTrack(true)" ); + // custom tracking request + ok( /myFoo=bar&baz=1&idsite=1/.test( results ), "trackRequest sends custom parameters"); + // Test Custom variables ok( /SaveCustomVariableCookie.*&cvar=%7B%222%22%3A%5B%22cookiename2PAGE%22%2C%22cookievalue2PAGE%22%5D%7D.*&_cvar=%7B%221%22%3A%5B%22cookiename%22%2C%22cookievalue%22%5D%2C%222%22%3A%5B%22cookiename2%22%2C%22cookievalue2%22%5D%7D/.test(results), "test custom vars are set"); -- GitLab