Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <thomas.steur@gmail.com>2015-11-04 05:30:26 +0300
committerThomas Steur <thomas.steur@gmail.com>2015-11-24 04:04:01 +0300
commitd995029b91f38bd81fbbb50f4d058f3b6b3c18a2 (patch)
tree3ba63b979bde2da095ddb06bb66fe92b2d72c31b
parentf9c851190ed680eb430d9d52040b72c7cf73f62a (diff)
refs #9129 added feature Custom Dimensions
-rw-r--r--.gitmodules11
-rw-r--r--CHANGELOG.md1
-rw-r--r--core/API/DataTableManipulator.php10
-rw-r--r--core/API/DocumentationGenerator.php1
-rw-r--r--core/DataArray.php74
-rw-r--r--core/Plugin/Manager.php13
-rw-r--r--core/Plugin/RequestProcessors.php3
-rw-r--r--core/Plugin/Visualization.php13
-rw-r--r--core/Tracker/Action.php19
-rw-r--r--core/Tracker/Model.php32
-rw-r--r--core/ViewDataTable/Config.php35
-rw-r--r--js/piwik.js125
-rw-r--r--lang/en.json4
m---------libs/PiwikTracker0
-rw-r--r--piwik.js84
-rw-r--r--plugins/API/API.php37
-rw-r--r--plugins/API/ProcessedReport.php10
-rw-r--r--plugins/API/RowEvolution.php14
-rw-r--r--plugins/Actions/Archiver.php8
-rw-r--r--plugins/Actions/Metrics.php12
-rw-r--r--plugins/CoreHome/Columns/UserId.php10
-rw-r--r--plugins/CoreHome/javascripts/broadcast.js7
-rw-r--r--plugins/CoreHome/javascripts/dataTable.js6
-rw-r--r--plugins/CoreHome/javascripts/dataTable_rowactions.js5
-rw-r--r--plugins/CoreHome/javascripts/notification.js10
-rw-r--r--plugins/CoreHome/templates/_menu.twig8
-rw-r--r--plugins/CoreHome/tests/Integration/Column/UserIdTest.php12
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php7
-rw-r--r--plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js5
m---------plugins/CustomDimensions0
-rw-r--r--plugins/CustomVariables/CustomVariables.php2
-rw-r--r--plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php22
-rw-r--r--plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js5
-rw-r--r--plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html4
-rw-r--r--plugins/Goals/Controller.php8
-rw-r--r--plugins/Goals/Reports/Base.php2
-rw-r--r--plugins/ImageGraph/API.php13
-rw-r--r--plugins/Insights/Visualizations/Insight.php4
-rw-r--r--plugins/Live/Live.php2
-rw-r--r--plugins/Live/javascripts/rowaction.js6
-rw-r--r--plugins/Monolog/tests/System/TrackerLoggingTest.php6
-rw-r--r--plugins/Overlay/Controller.php108
-rw-r--r--plugins/Overlay/client/client.js9
-rw-r--r--plugins/Overlay/javascripts/Overlay_Helper.js8
-rw-r--r--plugins/Overlay/javascripts/Piwik_Overlay.js31
-rw-r--r--plugins/Overlay/javascripts/rowaction.js33
-rw-r--r--plugins/Overlay/stylesheets/overlay.css12
-rw-r--r--plugins/Overlay/templates/index.twig4
-rw-r--r--plugins/Overlay/templates/index_noframe.twig2
-rw-r--r--plugins/Overlay/templates/renderSidebar.twig5
-rw-r--r--plugins/Overlay/templates/startOverlaySession.twig50
-rw-r--r--plugins/SegmentEditor/SegmentEditor.php6
-rw-r--r--plugins/SegmentEditor/SegmentFormatter.php142
-rw-r--r--plugins/SegmentEditor/SegmentList.php32
-rw-r--r--plugins/SegmentEditor/SegmentSelectorControl.php5
-rw-r--r--plugins/SegmentEditor/javascripts/Segmentation.js2
-rw-r--r--plugins/SegmentEditor/lang/en.json7
-rw-r--r--plugins/SegmentEditor/templates/_segmentSelector.twig2
-rw-r--r--plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php110
-rw-r--r--plugins/SegmentEditor/tests/Integration/SegmentListTest.php74
-rw-r--r--plugins/TestRunner/Commands/TestsRunUI.php2
-rw-r--r--plugins/TestRunner/Commands/TestsSetupFixture.php5
-rw-r--r--plugins/Transitions/javascripts/transitions.js59
-rw-r--r--tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php7
-rw-r--r--tests/PHPUnit/Fixtures/UITestFixture.php3
-rw-r--r--tests/PHPUnit/Framework/Fixture.php71
-rw-r--r--tests/PHPUnit/Framework/TestingEnvironmentVariables.php39
-rw-r--r--tests/PHPUnit/System/BackwardsCompatibility1XTest.php2
-rw-r--r--tests/PHPUnit/System/TrackerTest.php7
-rw-r--r--tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml15
-rw-r--r--tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml2
m---------tests/UI/expected-ui-screenshots0
-rw-r--r--tests/UI/specs/Dashboard_spec.js6
-rw-r--r--tests/UI/specs/Morpheus_spec.js6
-rw-r--r--tests/UI/specs/Overlay_spec.js17
-rw-r--r--tests/UI/specs/Theme_spec.js8
-rw-r--r--tests/UI/specs/UIIntegration_spec.js21
-rw-r--r--tests/javascript/index.php33
-rw-r--r--tests/lib/screenshot-testing/support/chai-extras.js1
-rw-r--r--tests/lib/screenshot-testing/support/page-renderer.js2
-rw-r--r--tests/lib/screenshot-testing/support/test-environment.js28
93 files changed, 1450 insertions, 291 deletions
diff --git a/.gitmodules b/.gitmodules
index 6f50e7405e..8a6eb61371 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -29,6 +29,7 @@
[submodule "tests/UI/expected-ui-screenshots"]
path = tests/UI/expected-ui-screenshots
url = https://github.com/piwik/piwik-ui-tests.git
+ branch = master
[submodule "tests/travis"]
path = tests/travis
url = https://github.com/piwik/travis-scripts
@@ -40,9 +41,14 @@
[submodule "plugins/AnonymousPiwikUsageMeasurement"]
path = plugins/AnonymousPiwikUsageMeasurement
url = https://github.com/piwik/plugin-AnonymousPiwikUsageMeasurement.git
+ branch = master
+[submodule "plugins/CustomDimensions"]
+ path = plugins/CustomDimensions
+ url = https://github.com/piwik/plugin-CustomDimensions.git
+ branch = master
-# Add new Plugin submodule above this line ^
+# Add new Plugin submodule above this line ^^
#
# Note: when you add a submodule that SHOULD be left in the packaged release such as the few submodules below,
# then you MUST add these submodules names in the SUBMODULES_PACKAGED_WITH_CORE variable in:
@@ -55,3 +61,6 @@
path = misc/log-analytics
url = https://github.com/piwik/piwik-log-analytics.git
branch = master
+
+# Note: do not add new plugin submodules here, but a few lines above
+
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33d9a60b7e..c1b4fd8cda 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API'
* When generating a new plugin skeleton via `generate:plugin` command, plugin name must now contain only letters and numbers.
* JavaScript Tracker tests no longer require `SQLite`. The existing MySQL configuration for tests is used now. In order to run the tests make sure Piwik is installed and `[database_tests]` is configured in `config/config.ini.php`.
* The definitions for search engine and social network detection have been moved from bundled data files to a separate package (see [https://github.com/piwik/searchengine-and-social-list](https://github.com/piwik/searchengine-and-social-list)).
+ * In [UI screenshot tests](https://developer.piwik.org/guides/tests-ui), a test environment `configOverride` setting should be no longer overwritten. Instead new values should be added to the existing `configOverride` array in PHP or JavaScript. For example instead of `testEnvironment.configOverride = {group: {name: 1}}` use `testEnvironment.overrideConfig('group', 'name', '1')`.
### New APIs
* Add your own SMS/Text provider by creating a new class in the `SMSProvider` directory of your plugin. The class has to extend `Piwik\Plugins\MobileMessaging\SMSProvider` and implement the required methods.
diff --git a/core/API/DataTableManipulator.php b/core/API/DataTableManipulator.php
index 76c52f4958..d084d6d156 100644
--- a/core/API/DataTableManipulator.php
+++ b/core/API/DataTableManipulator.php
@@ -152,7 +152,15 @@ abstract class DataTableManipulator
$idSite = 'all';
}
- $meta = API::getInstance()->getMetadata($idSite, $this->apiModule, $this->apiMethod);
+ $apiParameters = array();
+ if (!empty($request['idDimension'])) {
+ $apiParameters['idDimension'] = $request['idDimension'];
+ }
+ if (!empty($request['idGoal'])) {
+ $apiParameters['idGoal'] = $request['idGoal'];
+ }
+
+ $meta = API::getInstance()->getMetadata($idSite, $this->apiModule, $this->apiMethod, $apiParameters);
if (empty($meta)) {
throw new Exception(sprintf(
diff --git a/core/API/DocumentationGenerator.php b/core/API/DocumentationGenerator.php
index 64c25bf9b0..60807267df 100644
--- a/core/API/DocumentationGenerator.php
+++ b/core/API/DocumentationGenerator.php
@@ -291,6 +291,7 @@ class DocumentationGenerator
$aParameters['disable_queued_filters'] = false;
$aParameters['disable_generic_filters'] = false;
$aParameters['expanded'] = false;
+ $aParameters['idDimenson'] = false;
$moduleName = Proxy::getInstance()->getModuleNameFromClassName($class);
$aParameters = array_merge(array('module' => 'API', 'method' => $moduleName . '.' . $methodName), $aParameters);
diff --git a/core/DataArray.php b/core/DataArray.php
index 386eae29a3..1be1942312 100644
--- a/core/DataArray.php
+++ b/core/DataArray.php
@@ -47,7 +47,7 @@ class DataArray
public function sumMetricsVisits($label, $row)
{
if (!isset($this->data[$label])) {
- $this->data[$label] = self::makeEmptyRow();
+ $this->data[$label] = static::makeEmptyRow();
}
$this->doSumVisitsMetrics($row, $this->data[$label]);
}
@@ -80,7 +80,7 @@ class DataArray
*
* @return void
*/
- protected function doSumVisitsMetrics($newRowToAdd, &$oldRowToUpdate, $onlyMetricsAvailableInActionsTable = false)
+ protected function doSumVisitsMetrics($newRowToAdd, &$oldRowToUpdate)
{
// Pre 1.2 format: string indexed rows are returned from the DB
// Left here for Backward compatibility with plugins doing custom SQL queries using these metrics as string
@@ -88,9 +88,6 @@ class DataArray
$oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd['nb_visits'];
$oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions'];
$oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors'];
- if ($onlyMetricsAvailableInActionsTable) {
- return;
- }
$oldRowToUpdate[Metrics::INDEX_NB_USERS] += $newRowToAdd['nb_users'];
$oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS] = (float)max($newRowToAdd['max_actions'], $oldRowToUpdate[Metrics::INDEX_MAX_ACTIONS]);
$oldRowToUpdate[Metrics::INDEX_SUM_VISIT_LENGTH] += $newRowToAdd['sum_visit_length'];
@@ -107,9 +104,6 @@ class DataArray
$oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd[Metrics::INDEX_NB_VISITS];
$oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd[Metrics::INDEX_NB_ACTIONS];
$oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Metrics::INDEX_NB_UNIQ_VISITORS];
- if ($onlyMetricsAvailableInActionsTable) {
- return;
- }
// In case the existing Row had no action metrics (eg. Custom Variable XYZ with "visit" scope)
// but the new Row has action metrics (eg. same Custom Variable XYZ this time with a "page" scope)
@@ -133,11 +127,50 @@ class DataArray
$oldRowToUpdate[Metrics::INDEX_NB_VISITS_CONVERTED] += $newRowToAdd[Metrics::INDEX_NB_VISITS_CONVERTED];
}
+ /**
+ * Adds the given row $newRowToAdd to the existing $oldRowToUpdate passed by reference
+ * The rows are php arrays Name => value
+ *
+ * @param array $newRowToAdd
+ * @param array $oldRowToUpdate
+ * @param bool $onlyMetricsAvailableInActionsTable
+ *
+ * @return void
+ */
+ protected function doSumActionsMetrics($newRowToAdd, &$oldRowToUpdate)
+ {
+ // Pre 1.2 format: string indexed rows are returned from the DB
+ // Left here for Backward compatibility with plugins doing custom SQL queries using these metrics as string
+ if (!isset($newRowToAdd[Metrics::INDEX_NB_VISITS])) {
+ $oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd['nb_visits'];
+ $oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd['nb_actions'];
+ $oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd['nb_uniq_visitors'];
+ return;
+ }
+
+ // Edge case fail safe
+ if (!isset($oldRowToUpdate[Metrics::INDEX_NB_VISITS])) {
+ return;
+ }
+
+ $oldRowToUpdate[Metrics::INDEX_NB_VISITS] += $newRowToAdd[Metrics::INDEX_NB_VISITS];
+ if (array_key_exists(Metrics::INDEX_NB_ACTIONS, $newRowToAdd)) {
+ $oldRowToUpdate[Metrics::INDEX_NB_ACTIONS] += $newRowToAdd[Metrics::INDEX_NB_ACTIONS];
+ }
+ if (array_key_exists(Metrics::INDEX_PAGE_NB_HITS, $newRowToAdd)) {
+ if (!array_key_exists(Metrics::INDEX_PAGE_NB_HITS, $oldRowToUpdate)) {
+ $oldRowToUpdate[Metrics::INDEX_PAGE_NB_HITS] = 0;
+ }
+ $oldRowToUpdate[Metrics::INDEX_PAGE_NB_HITS] += $newRowToAdd[Metrics::INDEX_PAGE_NB_HITS];
+ }
+ $oldRowToUpdate[Metrics::INDEX_NB_UNIQ_VISITORS] += $newRowToAdd[Metrics::INDEX_NB_UNIQ_VISITORS];
+ }
+
public function sumMetricsGoals($label, $row)
{
$idGoal = $row['idgoal'];
if (!isset($this->data[$label][Metrics::INDEX_GOALS][$idGoal])) {
- $this->data[$label][Metrics::INDEX_GOALS][$idGoal] = self::makeEmptyGoalRow($idGoal);
+ $this->data[$label][Metrics::INDEX_GOALS][$idGoal] = static::makeEmptyGoalRow($idGoal);
}
$this->doSumGoalsMetrics($row, $this->data[$label][Metrics::INDEX_GOALS][$idGoal]);
}
@@ -201,9 +234,10 @@ class DataArray
public function sumMetricsActions($label, $row)
{
if (!isset($this->data[$label])) {
- $this->data[$label] = self::makeEmptyActionRow();
+ $this->data[$label] = static::makeEmptyActionRow();
}
- $this->doSumVisitsMetrics($row, $this->data[$label], $onlyMetricsAvailableInActionsTable = true);
+
+ $this->doSumActionsMetrics($row, $this->data[$label]);
}
protected static function makeEmptyActionRow()
@@ -218,7 +252,7 @@ class DataArray
public function sumMetricsEvents($label, $row)
{
if (!isset($this->data[$label])) {
- $this->data[$label] = self::makeEmptyEventRow();
+ $this->data[$label] = static::makeEmptyEventRow();
}
$this->doSumEventsMetrics($row, $this->data[$label], $onlyMetricsAvailableInActionsTable = true);
}
@@ -250,16 +284,16 @@ class DataArray
$oldRowToUpdate[Metrics::INDEX_EVENT_NB_HITS] += $newRowToAdd[Metrics::INDEX_EVENT_NB_HITS];
$oldRowToUpdate[Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE] += $newRowToAdd[Metrics::INDEX_EVENT_NB_HITS_WITH_VALUE];
- $newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE], self::EVENT_VALUE_PRECISION);
+ $newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE], static::EVENT_VALUE_PRECISION);
$oldRowToUpdate[Metrics::INDEX_EVENT_SUM_EVENT_VALUE] += $newRowToAdd[Metrics::INDEX_EVENT_SUM_EVENT_VALUE];
- $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE] = round(max($newRowToAdd[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]), self::EVENT_VALUE_PRECISION);
+ $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE] = round(max($newRowToAdd[Metrics::INDEX_EVENT_MAX_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MAX_EVENT_VALUE]), static::EVENT_VALUE_PRECISION);
// Update minimum only if it is set
if ($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] !== false) {
if ($oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] === false) {
- $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], self::EVENT_VALUE_PRECISION);
+ $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], static::EVENT_VALUE_PRECISION);
} else {
- $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round(min($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]), self::EVENT_VALUE_PRECISION);
+ $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE] = round(min($newRowToAdd[Metrics::INDEX_EVENT_MIN_EVENT_VALUE], $oldRowToUpdate[Metrics::INDEX_EVENT_MIN_EVENT_VALUE]), static::EVENT_VALUE_PRECISION);
}
}
}
@@ -290,7 +324,7 @@ class DataArray
public function sumMetricsVisitsPivot($parentLabel, $label, $row)
{
if (!isset($this->dataTwoLevels[$parentLabel][$label])) {
- $this->dataTwoLevels[$parentLabel][$label] = self::makeEmptyRow();
+ $this->dataTwoLevels[$parentLabel][$label] = static::makeEmptyRow();
}
$this->doSumVisitsMetrics($row, $this->dataTwoLevels[$parentLabel][$label]);
}
@@ -299,7 +333,7 @@ class DataArray
{
$idGoal = $row['idgoal'];
if (!isset($this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal])) {
- $this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal] = self::makeEmptyGoalRow($idGoal);
+ $this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal] = static::makeEmptyGoalRow($idGoal);
}
$this->doSumGoalsMetrics($row, $this->dataTwoLevels[$parentLabel][$label][Metrics::INDEX_GOALS][$idGoal]);
}
@@ -309,7 +343,7 @@ class DataArray
if (!isset($this->dataTwoLevels[$parentLabel][$label])) {
$this->dataTwoLevels[$parentLabel][$label] = $this->makeEmptyActionRow();
}
- $this->doSumVisitsMetrics($row, $this->dataTwoLevels[$parentLabel][$label], $onlyMetricsAvailableInActionsTable = true);
+ $this->doSumActionsMetrics($row, $this->dataTwoLevels[$parentLabel][$label]);
}
public function sumMetricsEventsPivot($parentLabel, $label, $row)
@@ -382,7 +416,7 @@ class DataArray
*/
public static function isRowActions($row)
{
- return (count($row) == count(self::makeEmptyActionRow())) && isset($row[Metrics::INDEX_NB_ACTIONS]);
+ return (count($row) == count(static::makeEmptyActionRow())) && isset($row[Metrics::INDEX_NB_ACTIONS]);
}
/**
diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php
index 150151ee3e..c3974937a0 100644
--- a/core/Plugin/Manager.php
+++ b/core/Plugin/Manager.php
@@ -1070,11 +1070,20 @@ class Manager
if ($saveConfig) {
PiwikConfig::getInstance()->forceSave();
+ $this->clearCache($pluginName);
}
}
public function isTrackerPlugin(Plugin $plugin)
{
+ if (!$this->isPluginInstalled($plugin->getPluginName())) {
+ return false;
+ }
+
+ if ($plugin->isTrackerPlugin()) {
+ return true;
+ }
+
$dimensions = VisitDimension::getDimensions($plugin);
if (!empty($dimensions)) {
return true;
@@ -1101,10 +1110,6 @@ class Manager
return true;
}
- if ($plugin->isTrackerPlugin()) {
- return true;
- }
-
return false;
}
diff --git a/core/Plugin/RequestProcessors.php b/core/Plugin/RequestProcessors.php
index ef69eb59d5..827274485e 100644
--- a/core/Plugin/RequestProcessors.php
+++ b/core/Plugin/RequestProcessors.php
@@ -14,7 +14,8 @@ class RequestProcessors
{
public function getRequestProcessors()
{
- $processors = Manager::getInstance()->findMultipleComponents('Tracker', 'Piwik\\Tracker\\RequestProcessor');
+ $manager = Manager::getInstance();
+ $processors = $manager->findMultipleComponents('Tracker', 'Piwik\\Tracker\\RequestProcessor');
$instances = array();
foreach ($processors as $processor) {
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index 8a35a971b2..fcd8891280 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -272,7 +272,18 @@ class Visualization extends ViewDataTable
$idSite = Common::getRequestVar('idSite', null, 'string', $request);
$module = $this->requestConfig->getApiModuleToRequest();
$action = $this->requestConfig->getApiMethodToRequest();
- $metadata = ApiApi::getInstance()->getMetadata($idSite, $module, $action);
+
+ $apiParameters = array();
+ $idDimension = Common::getRequestVar('idDimension', 0, 'int');
+ $idGoal = Common::getRequestVar('idGoal', 0, 'int');
+ if ($idDimension > 0) {
+ $apiParameters['idDimension'] = $idDimension;
+ }
+ if ($idGoal > 0) {
+ $apiParameters['idGoal'] = $idGoal;
+ }
+
+ $metadata = ApiApi::getInstance()->getMetadata($idSite, $module, $action, $apiParameters);
if (!empty($metadata)) {
return array_shift($metadata);
diff --git a/core/Tracker/Action.php b/core/Tracker/Action.php
index b30d695e73..b841c210a9 100644
--- a/core/Tracker/Action.php
+++ b/core/Tracker/Action.php
@@ -62,6 +62,7 @@ abstract class Action
private $idLinkVisitAction;
private $actionIdsCached = array();
+ private $customFields = array();
private $actionName;
private $actionType;
@@ -228,6 +229,16 @@ abstract class Action
return false;
}
+ public function setCustomField($field, $value)
+ {
+ $this->customFields[$field] = $value;
+ }
+
+ public function getCustomFields()
+ {
+ return $this->customFields;
+ }
+
public function getIdActionUrl()
{
$idUrl = $this->actionIdsCached['idaction_url'];
@@ -379,13 +390,7 @@ abstract class Action
$visitAction[self::DB_COLUMN_CUSTOM_FLOAT] = Common::forceDotAsSeparatorForDecimalPoint($customValue);
}
- $customVariables = $this->getCustomVariables();
- if (!empty($customVariables)) {
- Common::printDebug("Page level Custom Variables: ");
- Common::printDebug($customVariables);
- }
-
- $visitAction = array_merge($visitAction, $customVariables);
+ $visitAction = array_merge($visitAction, $this->customFields);
$this->idLinkVisitAction = $this->getModel()->createAction($visitAction);
diff --git a/core/Tracker/Model.php b/core/Tracker/Model.php
index c39820e571..afffd5faeb 100644
--- a/core/Tracker/Model.php
+++ b/core/Tracker/Model.php
@@ -289,7 +289,7 @@ class Model
public function updateVisit($idSite, $idVisit, $valuesToUpdate)
{
- list($updateParts, $sqlBind) = $this->visitFieldsToQuery($valuesToUpdate);
+ list($updateParts, $sqlBind) = $this->fieldsToQuery($valuesToUpdate);
$parts = implode($updateParts, ', ');
$table = Common::prefixTable('log_visit');
@@ -312,6 +312,34 @@ class Model
return $wasInserted;
}
+ public function updateAction($idLinkVa, $valuesToUpdate)
+ {
+ if (empty($idLinkVa)) {
+ return;
+ }
+
+ list($updateParts, $sqlBind) = $this->fieldsToQuery($valuesToUpdate);
+
+ $parts = implode($updateParts, ', ');
+ $table = Common::prefixTable('log_link_visit_action');
+
+ $sqlQuery = "UPDATE $table SET $parts WHERE idlink_va = ?";
+
+ $sqlBind[] = $idLinkVa;
+
+ $db = $this->getDb();
+ $result = $db->query($sqlQuery, $sqlBind);
+ $wasInserted = $db->rowCount($result) != 0;
+
+ if (!$wasInserted) {
+ Common::printDebug("Action with this idLinkVa wasn't found in the DB.");
+ Common::printDebug("$sqlQuery --- ");
+ Common::printDebug($sqlBind);
+ }
+
+ return $wasInserted;
+ }
+
public function findVisitor($idSite, $configId, $idVisitor, $fieldsToRead, $shouldMatchOneFieldOnly, $isVisitorIdToLookup, $timeLookBack, $timeLookAhead)
{
$selectCustomVariables = '';
@@ -396,7 +424,7 @@ class Model
return $result == null;
}
- private function visitFieldsToQuery($valuesToUpdate)
+ private function fieldsToQuery($valuesToUpdate)
{
$updateParts = array();
$sqlBind = array();
diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php
index e856473d46..bd73ea846a 100644
--- a/core/ViewDataTable/Config.php
+++ b/core/ViewDataTable/Config.php
@@ -10,6 +10,7 @@
namespace Piwik\ViewDataTable;
use Piwik\API\Request as ApiRequest;
+use Piwik\Common;
use Piwik\DataTable;
use Piwik\DataTable\Filter\PivotByDimension;
use Piwik\Metrics;
@@ -486,7 +487,28 @@ class Config
{
$this->metrics_documentation = array();
- $report = API::getInstance()->getMetadata(0, $this->controllerName, $this->controllerAction);
+ $idSite = Common::getRequestVar('idSite', 0, 'int');
+
+ if ($idSite < 1) {
+ return;
+ }
+
+ $apiParameters = array();
+ $idDimension = Common::getRequestVar('idDimension', 0, 'int');
+ $idGoal = Common::getRequestVar('idGoal', 0, 'int');
+ if ($idDimension > 0) {
+ $apiParameters['idDimension'] = $idDimension;
+ }
+ if ($idGoal > 0) {
+ $apiParameters['idGoal'] = $idGoal;
+ }
+
+ $report = API::getInstance()->getMetadata($idSite, $this->controllerName, $this->controllerAction, $apiParameters);
+
+ if (empty($report)) {
+ return;
+ }
+
$report = $report[0];
if (isset($report['metricsDocumentation'])) {
@@ -556,6 +578,17 @@ class Config
$this->columns_to_display = array_filter($columnsToDisplay);
}
+ public function removeColumnToDisplay($columnToRemove)
+ {
+ if (!empty($this->columns_to_display)) {
+
+ $key = array_search($columnToRemove, $this->columns_to_display);
+ if (false !== $key) {
+ unset($this->columns_to_display[$key]);
+ }
+ }
+ }
+
/**
* @ignore
*/
diff --git a/js/piwik.js b/js/piwik.js
index 5a6fde9e30..fa9d9f57e5 100644
--- a/js/piwik.js
+++ b/js/piwik.js
@@ -979,8 +979,8 @@ if (typeof JSON2 !== 'object' && typeof window.JSON === 'object' && window.JSON.
getAttributionReferrerTimestamp, getAttributionReferrerUrl,
setCustomData, getCustomData,
setCustomRequestProcessing,
- setCustomVariable, getCustomVariable, deleteCustomVariable, storeCustomVariablesInCookie,
- setDownloadExtensions, addDownloadExtensions, removeDownloadExtensions,
+ setCustomVariable, getCustomVariable, deleteCustomVariable, storeCustomVariablesInCookie, setCustomDimension, getCustomDimension,
+ deleteCustomDimension, setDownloadExtensions, addDownloadExtensions, removeDownloadExtensions,
setDomains, setIgnoreClasses, setRequestMethod, setRequestContentType,
setReferrerUrl, setCustomUrl, setAPIUrl, setDocumentTitle,
setDownloadClasses, setLinkClasses,
@@ -1143,6 +1143,23 @@ if (typeof Piwik !== 'object') {
return typeof property === 'string' || property instanceof String;
}
+ function isObjectEmpty(property)
+ {
+ if (!property) {
+ return true;
+ }
+
+ var i;
+ var isEmpty = true;
+ for (i in property) {
+ if (Object.prototype.hasOwnProperty.call(property, i)) {
+ isEmpty = false;
+ }
+ }
+
+ return isEmpty;
+ }
+
/*
* apply wrapper
*
@@ -2656,7 +2673,7 @@ if (typeof Piwik !== 'object') {
// check whether we were redirected from the piwik overlay plugin
var referrerRegExp = new RegExp('index\\.php\\?module=Overlay&action=startOverlaySession'
- + '&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)$');
+ + '&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$');
var match = referrerRegExp.exec(documentAlias.referrer);
@@ -2670,15 +2687,22 @@ if (typeof Piwik !== 'object') {
// store overlay session info in window name
var period = match[2],
- date = match[3];
+ date = match[3],
+ segment = match[4];
+
+ if (!segment) {
+ segment = '';
+ } else if (segment.indexOf('&segment=') === 0) {
+ segment = segment.substr('&segment='.length);
+ }
- windowAlias.name = windowName + '###' + period + '###' + date;
+ windowAlias.name = windowName + '###' + period + '###' + date + '###' + segment;
}
// retrieve and check data from window name
var windowNameParts = windowAlias.name.split('###');
- return windowNameParts.length === 3 && windowNameParts[0] === windowName;
+ return windowNameParts.length === 4 && windowNameParts[0] === windowName;
}
/*
@@ -2688,12 +2712,13 @@ if (typeof Piwik !== 'object') {
var windowNameParts = windowAlias.name.split('###'),
period = windowNameParts[1],
date = windowNameParts[2],
+ segment = windowNameParts[3],
piwikUrl = getPiwikUrlForOverlay(configTrackerUrl, configApiUrl);
loadScript(
piwikUrl + 'plugins/Overlay/client/client.js?v=1',
function () {
- Piwik_Overlay_Client.initialize(piwikUrl, configTrackerSiteId, period, date);
+ Piwik_Overlay_Client.initialize(piwikUrl, configTrackerSiteId, period, date, segment);
}
);
}
@@ -2856,6 +2881,9 @@ if (typeof Piwik !== 'object') {
// Custom Variables, scope "event"
customVariablesEvent = {},
+ // Custom Dimensions (can be any scope)
+ customDimensions = {},
+
// Custom Variables names and values are each truncated before being sent in the request or recorded in the cookie
customVariableMaximumLength = 200,
@@ -3686,6 +3714,34 @@ if (typeof Piwik !== 'object') {
}
}
+ var customDimensionIdsAlreadyHandled = [];
+ if (customData) {
+ for (i in customData) {
+ if (Object.prototype.hasOwnProperty.call(customData, i) && /^dimension\d+$/.test(i)) {
+ var index = i.replace('dimension', '');
+ customDimensionIdsAlreadyHandled.push(parseInt(index, 10));
+ customDimensionIdsAlreadyHandled.push(String(index));
+ request += '&' + i + '=' + customData[i];
+ delete customData[i];
+ }
+ }
+ }
+
+ if (customData && isObjectEmpty(customData)) {
+ customData = null;
+ // we deleted all keys from custom data
+ }
+
+ // custom dimensions
+ for (i in customDimensions) {
+ if (Object.prototype.hasOwnProperty.call(customDimensions, i)) {
+ var isNotSetYet = (-1 === customDimensionIdsAlreadyHandled.indexOf(i));
+ if (isNotSetYet) {
+ request += '&dimension' + i + '=' + customDimensions[i];
+ }
+ }
+ }
+
// custom data
if (customData) {
request += '&data=' + encodeWrapper(JSON2.stringify(customData));
@@ -5101,6 +5157,50 @@ if (typeof Piwik !== 'object') {
},
/**
+ * Set Custom Dimensions. Any set Custom Dimension will be cleared after a tracked pageview. Make
+ * sure to set them again if needed.
+ *
+ * @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;
+ }
+ },
+
+ /**
+ * 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];
+ }
+ },
+
+ /**
+ * Delete a custom dimension.
+ *
+ * @param int index Custom dimension Id
+ */
+ 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
@@ -5173,6 +5273,7 @@ if (typeof Piwik !== 'object') {
* 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
@@ -5912,10 +6013,11 @@ if (typeof Piwik !== 'object') {
* @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) {
+ trackEvent: function (category, action, name, value, customData) {
trackCallback(function () {
- logEvent(category, action, name, value);
+ logEvent(category, action, name, value, customData);
});
},
@@ -5925,10 +6027,11 @@ if (typeof Piwik !== 'object') {
* @param string keyword
* @param string category
* @param int resultsCount
+ * @param mixed customData
*/
- trackSiteSearch: function (keyword, category, resultsCount) {
+ trackSiteSearch: function (keyword, category, resultsCount, customData) {
trackCallback(function () {
- logSiteSearch(keyword, category, resultsCount);
+ logSiteSearch(keyword, category, resultsCount, customData);
});
},
diff --git a/lang/en.json b/lang/en.json
index c5a32ed0d8..0245f42a57 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -268,6 +268,7 @@
"OperationEndsWith": "Ends with",
"OptionalSmtpPort": "Optional. Defaults to 25 for unencrypted and TLS SMTP, and 465 for SSL SMTP.",
"Options": "Options",
+ "Or": "or",
"OrCancel": "or %s Cancel %s",
"Others": "Others",
"Outlink": "Outlink",
@@ -358,6 +359,9 @@
"TotalRatioTooltip": "This is %1$s of all %2$s %3$s.",
"TotalRevenue": "Total Revenue",
"TotalVisitsPageviewsActionsRevenue": "(Total: %s visits, %s pageviews, %s actions, %s revenue)",
+ "TrackingScopeAction": "Action",
+ "TrackingScopePage": "Page",
+ "TrackingScopeVisit": "Visit",
"TransitionsRowActionTooltip": "See what visitors did before and after viewing this page",
"TransitionsRowActionTooltipTitle": "Open Transitions",
"TranslatorName": "-",
diff --git a/libs/PiwikTracker b/libs/PiwikTracker
-Subproject fcbbc833bf02f4b549f66e0704f2dc0868309a4
+Subproject ac3e26bb3e2c8a428ccbf6ca663c2ef37fa47a5
diff --git a/piwik.js b/piwik.js
index 3148f91858..401ae10af1 100644
--- a/piwik.js
+++ b/piwik.js
@@ -18,46 +18,48 @@ 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 Piwik!=="object"){Piwik=(function(){var k,a={},v=document,e=navigator,L=screen,H=window,f=H.performance||H.mozPerformance||H.msPerformance||H.webkitPerformance,q=false,F=[],m=H.encodeURIComponent,G=H.decodeURIComponent,h=unescape,M,u,d;
-function j(X){try{return G(X)}catch(Y){return unescape(X)}}function x(Y){var X=typeof Y;return X!=="undefined"}function r(X){return typeof X==="function"}function K(X){return typeof X==="object"}function o(X){return typeof X==="string"||X instanceof String}function S(){var X,Z,Y;for(X=0;X<arguments.length;X+=1){Y=arguments[X];Z=Y.shift();if(o(Z)){M[Z].apply(M,Y)}else{Z.apply(M,Y)}}}function W(aa,Z,Y,X){if(aa.addEventListener){aa.addEventListener(Z,Y,X);return true}if(aa.attachEvent){return aa.attachEvent("on"+Z,Y)}aa["on"+Z]=Y}function P(Y,ab){var X="",aa,Z;for(aa in a){if(Object.prototype.hasOwnProperty.call(a,aa)){Z=a[aa][Y];if(r(Z)){X+=Z(ab)}}}return X}function T(){var X;P("unload");if(k){do{X=new Date()}while(X.getTimeAlias()<k)}}function Q(){var X;if(!q){q=true;P("load");for(X=0;X<F.length;X++){F[X]()}}return true}function p(){var Y;if(v.addEventListener){W(v,"DOMContentLoaded",function X(){v.removeEventListener("DOMContentLoaded",X,false);Q()})}else{if(v.attachEvent){v.attachEvent("onreadystatechange",function X(){if(v.readyState==="complete"){v.detachEvent("onreadystatechange",X);
-Q()}});if(v.documentElement.doScroll&&H===H.top){(function X(){if(!q){try{v.documentElement.doScroll("left")}catch(Z){setTimeout(X,0);return}Q()}}())}}}if((new RegExp("WebKit")).test(e.userAgent)){Y=setInterval(function(){if(q||/loaded|complete/.test(v.readyState)){clearInterval(Y);Q()}},10)}W(H,"load",Q,false)}function i(Z,Y){var X=v.createElement("script");X.type="text/javascript";X.src=Z;if(X.readyState){X.onreadystatechange=function(){var aa=this.readyState;if(aa==="loaded"||aa==="complete"){X.onreadystatechange=null;Y()}}}else{X.onload=Y}v.getElementsByTagName("head")[0].appendChild(X)}function y(){var X="";try{X=H.top.document.referrer}catch(Z){if(H.parent){try{X=H.parent.document.referrer}catch(Y){X=""}}}if(X===""){X=v.referrer}return X}function l(X){var Z=new RegExp("^([a-z]+):"),Y=Z.exec(X);return Y?Y[1]:null}function c(X){var Z=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),Y=Z.exec(X);return Y?Y[1]:X}function J(Z,Y){var X="[\\?&#]"+Y+"=([^&#]*)";var ab=new RegExp(X);
-var aa=ab.exec(Z);return aa?G(aa[1]):""}function t(X){return unescape(m(X))}function V(am){var Z=function(at,ar){return(at<<ar)|(at>>>(32-ar))},an=function(av){var at="",au,ar;for(au=7;au>=0;au--){ar=(av>>>(au*4))&15;at+=ar.toString(16)}return at},ac,ap,ao,Y=[],ag=1732584193,ae=4023233417,ad=2562383102,ab=271733878,aa=3285377520,al,ak,aj,ai,ah,aq,X,af=[];am=t(am);X=am.length;for(ap=0;ap<X-3;ap+=4){ao=am.charCodeAt(ap)<<24|am.charCodeAt(ap+1)<<16|am.charCodeAt(ap+2)<<8|am.charCodeAt(ap+3);af.push(ao)}switch(X&3){case 0:ap=2147483648;break;case 1:ap=am.charCodeAt(X-1)<<24|8388608;break;case 2:ap=am.charCodeAt(X-2)<<24|am.charCodeAt(X-1)<<16|32768;break;case 3:ap=am.charCodeAt(X-3)<<24|am.charCodeAt(X-2)<<16|am.charCodeAt(X-1)<<8|128;break}af.push(ap);while((af.length&15)!==14){af.push(0)}af.push(X>>>29);af.push((X<<3)&4294967295);for(ac=0;ac<af.length;ac+=16){for(ap=0;ap<16;ap++){Y[ap]=af[ac+ap]}for(ap=16;ap<=79;ap++){Y[ap]=Z(Y[ap-3]^Y[ap-8]^Y[ap-14]^Y[ap-16],1)}al=ag;ak=ae;aj=ad;ai=ab;ah=aa;
-for(ap=0;ap<=19;ap++){aq=(Z(al,5)+((ak&aj)|(~ak&ai))+ah+Y[ap]+1518500249)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}for(ap=20;ap<=39;ap++){aq=(Z(al,5)+(ak^aj^ai)+ah+Y[ap]+1859775393)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}for(ap=40;ap<=59;ap++){aq=(Z(al,5)+((ak&aj)|(ak&ai)|(aj&ai))+ah+Y[ap]+2400959708)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}for(ap=60;ap<=79;ap++){aq=(Z(al,5)+(ak^aj^ai)+ah+Y[ap]+3395469782)&4294967295;ah=ai;ai=aj;aj=Z(ak,30);ak=al;al=aq}ag=(ag+al)&4294967295;ae=(ae+ak)&4294967295;ad=(ad+aj)&4294967295;ab=(ab+ai)&4294967295;aa=(aa+ah)&4294967295}aq=an(ag)+an(ae)+an(ad)+an(ab)+an(aa);return aq.toLowerCase()}function O(Z,X,Y){if(!Z){Z=""}if(!X){X=""}if(Z==="translate.googleusercontent.com"){if(Y===""){Y=X}X=J(X,"u");Z=c(X)}else{if(Z==="cc.bingj.com"||Z==="webcache.googleusercontent.com"||Z.slice(0,5)==="74.6."){X=v.links[0].href;Z=c(X)}}return[Z,X,Y]}function z(Y){var X=Y.length;if(Y.charAt(--X)==="."){Y=Y.slice(0,X)}if(Y.slice(0,2)==="*."){Y=Y.slice(1)
-}return Y}function U(Y){Y=Y&&Y.text?Y.text:Y;if(!o(Y)){var X=v.getElementsByTagName("title");if(X&&x(X[0])){Y=X[0].text}}return Y}function D(X){if(!X){return[]}if(!x(X.children)&&x(X.childNodes)){return X.children}if(x(X.children)){return X.children}return[]}function I(Y,X){if(!Y||!X){return false}if(Y.contains){return Y.contains(X)}if(Y===X){return true}if(Y.compareDocumentPosition){return !!(Y.compareDocumentPosition(X)&16)}return false}function A(Z,aa){if(Z&&Z.indexOf){return Z.indexOf(aa)}if(!x(Z)||Z===null){return -1}if(!Z.length){return -1}var X=Z.length;if(X===0){return -1}var Y=0;while(Y<X){if(Z[Y]===aa){return Y}Y++}return -1}function g(Z){if(!Z){return false}function X(ab,ac){if(H.getComputedStyle){return v.defaultView.getComputedStyle(ab,null)[ac]}if(ab.currentStyle){return ab.currentStyle[ac]}}function aa(ab){ab=ab.parentNode;while(ab){if(ab===v){return true}ab=ab.parentNode}return false}function Y(ad,aj,ab,ag,ae,ah,af){var ac=ad.parentNode,ai=1;if(!aa(ad)){return false}if(9===ac.nodeType){return true
-}if("0"===X(ad,"opacity")||"none"===X(ad,"display")||"hidden"===X(ad,"visibility")){return false}if(!x(aj)||!x(ab)||!x(ag)||!x(ae)||!x(ah)||!x(af)){aj=ad.offsetTop;ae=ad.offsetLeft;ag=aj+ad.offsetHeight;ab=ae+ad.offsetWidth;ah=ad.offsetWidth;af=ad.offsetHeight}if(Z===ad&&(0===af||0===ah)&&"hidden"===X(ad,"overflow")){return false}if(ac){if(("hidden"===X(ac,"overflow")||"scroll"===X(ac,"overflow"))){if(ae+ai>ac.offsetWidth+ac.scrollLeft||ae+ah-ai<ac.scrollLeft||aj+ai>ac.offsetHeight+ac.scrollTop||aj+af-ai<ac.scrollTop){return false}}if(ad.offsetParent===ac){ae+=ac.offsetLeft;aj+=ac.offsetTop}return Y(ac,aj,ab,ag,ae,ah,af)}return true}return Y(Z)}var R={htmlCollectionToArray:function(Z){var X=[],Y;if(!Z||!Z.length){return X}for(Y=0;Y<Z.length;Y++){X.push(Z[Y])}return X},find:function(X){if(!document.querySelectorAll||!X){return[]}var Y=document.querySelectorAll(X);return this.htmlCollectionToArray(Y)},findMultiple:function(Z){if(!Z||!Z.length){return[]}var Y,aa;var X=[];for(Y=0;Y<Z.length;
-Y++){aa=this.find(Z[Y]);X=X.concat(aa)}X=this.makeNodesUnique(X);return X},findNodesByTagName:function(Y,X){if(!Y||!X||!Y.getElementsByTagName){return[]}var Z=Y.getElementsByTagName(X);return this.htmlCollectionToArray(Z)},makeNodesUnique:function(X){var ac=[].concat(X);X.sort(function(ae,ad){if(ae===ad){return 0}var ag=A(ac,ae);var af=A(ac,ad);if(ag===af){return 0}return ag>af?-1:1});if(X.length<=1){return X}var Y=0;var aa=0;var ab=[];var Z;Z=X[Y++];while(Z){if(Z===X[Y]){aa=ab.push(Y)}Z=X[Y++]||null}while(aa--){X.splice(ab[aa],1)}return X},getAttributeValueFromNode:function(ab,Z){if(!this.hasNodeAttribute(ab,Z)){return}if(ab&&ab.getAttribute){return ab.getAttribute(Z)}if(!ab||!ab.attributes){return}var aa=(typeof ab.attributes[Z]);if("undefined"===aa){return}if(ab.attributes[Z].value){return ab.attributes[Z].value}if(ab.attributes[Z].nodeValue){return ab.attributes[Z].nodeValue}var Y;var X=ab.attributes;if(!X){return}for(Y=0;Y<X.length;Y++){if(X[Y].nodeName===Z){return X[Y].nodeValue}}return null
-},hasNodeAttributeWithValue:function(Y,X){var Z=this.getAttributeValueFromNode(Y,X);return !!Z},hasNodeAttribute:function(Z,X){if(Z&&Z.hasAttribute){return Z.hasAttribute(X)}if(Z&&Z.attributes){var Y=(typeof Z.attributes[X]);return"undefined"!==Y}return false},hasNodeCssClass:function(Z,X){if(Z&&X&&Z.className){var Y=typeof Z.className==="string"?Z.className.split(" "):[];if(-1!==A(Y,X)){return true}}return false},findNodesHavingAttribute:function(ab,Z,X){if(!X){X=[]}if(!ab||!Z){return X}var aa=D(ab);if(!aa||!aa.length){return X}var Y,ac;for(Y=0;Y<aa.length;Y++){ac=aa[Y];if(this.hasNodeAttribute(ac,Z)){X.push(ac)}X=this.findNodesHavingAttribute(ac,Z,X)}return X},findFirstNodeHavingAttribute:function(Z,Y){if(!Z||!Y){return}if(this.hasNodeAttribute(Z,Y)){return Z}var X=this.findNodesHavingAttribute(Z,Y);if(X&&X.length){return X[0]}},findFirstNodeHavingAttributeWithValue:function(aa,Z){if(!aa||!Z){return}if(this.hasNodeAttributeWithValue(aa,Z)){return aa}var X=this.findNodesHavingAttribute(aa,Z);
-if(!X||!X.length){return}var Y;for(Y=0;Y<X.length;Y++){if(this.getAttributeValueFromNode(X[Y],Z)){return X[Y]}}},findNodesHavingCssClass:function(ab,aa,X){if(!X){X=[]}if(!ab||!aa){return X}if(ab.getElementsByClassName){var ac=ab.getElementsByClassName(aa);return this.htmlCollectionToArray(ac)}var Z=D(ab);if(!Z||!Z.length){return[]}var Y,ad;for(Y=0;Y<Z.length;Y++){ad=Z[Y];if(this.hasNodeCssClass(ad,aa)){X.push(ad)}X=this.findNodesHavingCssClass(ad,aa,X)}return X},findFirstNodeHavingClass:function(Z,Y){if(!Z||!Y){return}if(this.hasNodeCssClass(Z,Y)){return Z}var X=this.findNodesHavingCssClass(Z,Y);if(X&&X.length){return X[0]}},isLinkElement:function(Y){if(!Y){return false}var X=String(Y.nodeName).toLowerCase();var aa=["a","area"];var Z=A(aa,X);return Z!==-1},setAnyAttribute:function(Y,X,Z){if(!Y||!X){return}if(Y.setAttribute){Y.setAttribute(X,Z)}else{Y[X]=Z}}};var n={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 Y="."+this.CONTENT_CLASS;
-var X="["+this.CONTENT_ATTR+"]";var Z=R.findMultiple([Y,X]);return Z},findContentNodesWithinNode:function(aa){if(!aa){return[]}var Y=R.findNodesHavingCssClass(aa,this.CONTENT_CLASS);var X=R.findNodesHavingAttribute(aa,this.CONTENT_ATTR);if(X&&X.length){var Z;for(Z=0;Z<X.length;Z++){Y.push(X[Z])}}if(R.hasNodeAttribute(aa,this.CONTENT_ATTR)){Y.push(aa)}else{if(R.hasNodeCssClass(aa,this.CONTENT_CLASS)){Y.push(aa)}}Y=R.makeNodesUnique(Y);return Y},findParentContentNode:function(Y){if(!Y){return}var Z=Y;var X=0;while(Z&&Z!==v&&Z.parentNode){if(R.hasNodeAttribute(Z,this.CONTENT_ATTR)){return Z}if(R.hasNodeCssClass(Z,this.CONTENT_CLASS)){return Z}Z=Z.parentNode;if(X>1000){break}X++}},findPieceNode:function(Y){var X;X=R.findFirstNodeHavingAttribute(Y,this.CONTENT_PIECE_ATTR);if(!X){X=R.findFirstNodeHavingClass(Y,this.CONTENT_PIECE_CLASS)}if(X){return X}return Y},findTargetNodeNoDefault:function(X){if(!X){return}var Y=R.findFirstNodeHavingAttributeWithValue(X,this.CONTENT_TARGET_ATTR);if(Y){return Y
-}Y=R.findFirstNodeHavingAttribute(X,this.CONTENT_TARGET_ATTR);if(Y){return Y}Y=R.findFirstNodeHavingClass(X,this.CONTENT_TARGET_CLASS);if(Y){return Y}},findTargetNode:function(X){var Y=this.findTargetNodeNoDefault(X);if(Y){return Y}return X},findContentName:function(Y){if(!Y){return}var ab=R.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_NAME_ATTR);if(ab){return R.getAttributeValueFromNode(ab,this.CONTENT_NAME_ATTR)}var X=this.findContentPiece(Y);if(X){return this.removeDomainIfIsInLink(X)}if(R.hasNodeAttributeWithValue(Y,"title")){return R.getAttributeValueFromNode(Y,"title")}var Z=this.findPieceNode(Y);if(R.hasNodeAttributeWithValue(Z,"title")){return R.getAttributeValueFromNode(Z,"title")}var aa=this.findTargetNode(Y);if(R.hasNodeAttributeWithValue(aa,"title")){return R.getAttributeValueFromNode(aa,"title")}},findContentPiece:function(Y){if(!Y){return}var aa=R.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_PIECE_ATTR);if(aa){return R.getAttributeValueFromNode(aa,this.CONTENT_PIECE_ATTR)
-}var X=this.findPieceNode(Y);var Z=this.findMediaUrlInNode(X);if(Z){return this.toAbsoluteUrl(Z)}},findContentTarget:function(Z){if(!Z){return}var aa=this.findTargetNode(Z);if(R.hasNodeAttributeWithValue(aa,this.CONTENT_TARGET_ATTR)){return R.getAttributeValueFromNode(aa,this.CONTENT_TARGET_ATTR)}var Y;if(R.hasNodeAttributeWithValue(aa,"href")){Y=R.getAttributeValueFromNode(aa,"href");return this.toAbsoluteUrl(Y)}var X=this.findPieceNode(Z);if(R.hasNodeAttributeWithValue(X,"href")){Y=R.getAttributeValueFromNode(X,"href");return this.toAbsoluteUrl(Y)}},isSameDomain:function(X){if(!X||!X.indexOf){return false}if(0===X.indexOf(this.getLocation().origin)){return true}var Y=X.indexOf(this.getLocation().host);if(8>=Y&&0<=Y){return true}return false},removeDomainIfIsInLink:function(Z){var Y="^https?://[^/]+";var X="^.*//[^/]+";if(Z&&Z.search&&-1!==Z.search(new RegExp(Y))&&this.isSameDomain(Z)){Z=Z.replace(new RegExp(X),"");if(!Z){Z="/"}}return Z},findMediaUrlInNode:function(ab){if(!ab){return}var Z=["img","embed","video","audio"];
-var X=ab.nodeName.toLowerCase();if(-1!==A(Z,X)&&R.findFirstNodeHavingAttributeWithValue(ab,"src")){var aa=R.findFirstNodeHavingAttributeWithValue(ab,"src");return R.getAttributeValueFromNode(aa,"src")}if(X==="object"&&R.hasNodeAttributeWithValue(ab,"data")){return R.getAttributeValueFromNode(ab,"data")}if(X==="object"){var ac=R.findNodesByTagName(ab,"param");if(ac&&ac.length){var Y;for(Y=0;Y<ac.length;Y++){if("movie"===R.getAttributeValueFromNode(ac[Y],"name")&&R.hasNodeAttributeWithValue(ac[Y],"value")){return R.getAttributeValueFromNode(ac[Y],"value")}}}var ad=R.findNodesByTagName(ab,"embed");if(ad&&ad.length){return this.findMediaUrlInNode(ad[0])}}},trim:function(X){if(X&&String(X)===X){return X.replace(/^\s+|\s+$/g,"")}return X},isOrWasNodeInViewport:function(ac){if(!ac||!ac.getBoundingClientRect||ac.nodeType!==1){return true}var ab=ac.getBoundingClientRect();var aa=v.documentElement||{};var Z=ab.top<0;if(Z&&ac.offsetTop){Z=(ac.offsetTop+ab.height)>0}var Y=aa.clientWidth;if(H.innerWidth&&Y>H.innerWidth){Y=H.innerWidth
-}var X=aa.clientHeight;if(H.innerHeight&&X>H.innerHeight){X=H.innerHeight}return((ab.bottom>0||Z)&&ab.right>0&&ab.left<Y&&((ab.top<X)||Z))},isNodeVisible:function(Y){var X=g(Y);var Z=this.isOrWasNodeInViewport(Y);return X&&Z},buildInteractionRequestParams:function(X,Y,Z,aa){var ab="";if(X){ab+="c_i="+m(X)}if(Y){if(ab){ab+="&"}ab+="c_n="+m(Y)}if(Z){if(ab){ab+="&"}ab+="c_p="+m(Z)}if(aa){if(ab){ab+="&"}ab+="c_t="+m(aa)}return ab},buildImpressionRequestParams:function(X,Y,Z){var aa="c_n="+m(X)+"&c_p="+m(Y);if(Z){aa+="&c_t="+m(Z)}return aa},buildContentBlock:function(Z){if(!Z){return}var X=this.findContentName(Z);var Y=this.findContentPiece(Z);var aa=this.findContentTarget(Z);X=this.trim(X);Y=this.trim(Y);aa=this.trim(aa);return{name:X||"Unknown",piece:Y||"Unknown",target:aa||""}},collectContent:function(aa){if(!aa||!aa.length){return[]}var Z=[];var X,Y;for(X=0;X<aa.length;X++){Y=this.buildContentBlock(aa[X]);if(x(Y)){Z.push(Y)}}return Z},setLocation:function(X){this.location=X},getLocation:function(){var X=this.location||H.location;
-if(!X.origin){X.origin=X.protocol+"//"+X.hostname+(X.port?":"+X.port:"")}return X},toAbsoluteUrl:function(Y){if((!Y||String(Y)!==Y)&&Y!==""){return Y}if(""===Y){return this.getLocation().href}if(Y.search(/^\/\//)!==-1){return this.getLocation().protocol+Y}if(Y.search(/:\/\//)!==-1){return Y}if(0===Y.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+Y}if(0===Y.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+Y}if(0===Y.search("^[a-zA-Z]{2,11}:")){return Y}if(Y.search(/^\//)!==-1){return this.getLocation().origin+Y}var X="(.*/)";var Z=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(X))[0];return Z+Y},isUrlToCurrentDomain:function(Y){var Z=this.toAbsoluteUrl(Y);if(!Z){return false}var X=this.getLocation().origin;if(X===Z){return true}if(0===String(Z).indexOf(X)){if(":"===String(Z).substr(X.length,1)){return false}return true}return false},setHrefAttribute:function(Y,X){if(!Y||!X){return}R.setAnyAttribute(Y,"href",X)},shouldIgnoreInteraction:function(Z){var Y=R.hasNodeAttribute(Z,this.CONTENT_IGNOREINTERACTION_ATTR);
-var X=R.hasNodeCssClass(Z,this.CONTENT_IGNOREINTERACTION_CLASS);return Y||X}};function C(X,Y){if(Y){return Y}if(X.slice(-9)==="piwik.php"){X=X.slice(0,X.length-9)}return X}function B(ab){var X="Piwik_Overlay";var ae=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)$");var Z=ae.exec(v.referrer);if(Z){var aa=Z[1];if(aa!==String(ab)){return false}var ad=Z[2],Y=Z[3];H.name=X+"###"+ad+"###"+Y}var ac=H.name.split("###");return ac.length===3&&ac[0]===X}function N(Y,ad,aa){var ac=H.name.split("###"),ab=ac[1],X=ac[2],Z=C(Y,ad);i(Z+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(Z,aa,ab,X)})}function E(aJ,by){var ae=O(v.domain,H.location.href,y()),bY=z(ae[0]),cj=j(ae[1]),bG=j(ae[2]),cn=false,bC="GET",bE=bC,bj="application/x-www-form-urlencoded; charset=UTF-8",aP=bj,ab=aJ||"",az="",bA="",b5=by||"",aO="",a7="",bd,aX=v.title,aZ=["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"],bB=[bY],aj=[],bp=[],aH=[],bz=500,ak,b3,br,al,ao,a3=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],aU=["pk_kwd","piwik_kwd","utm_term"],ch="_pk_",ar,ci,ap=false,b9,a5,ba,ay=33955200000,aE=1800000,bh=15768000000,a6=true,aM=0,bc=false,ah=false,av,bq={},ac={},ca=200,bR={},b6={},ai=[],aB=false,bg=false,bK=false,b7=false,bN=false,bo=null,bb,bu,au,a2=V,bJ;
-function bT(cw,ct,cs,cv,cr,cu){if(ap){return}var cq;if(cs){cq=new Date();cq.setTime(cq.getTime()+cs)}v.cookie=cw+"="+m(ct)+(cs?";expires="+cq.toGMTString():"")+";path="+(cv||"/")+(cr?";domain="+cr:"")+(cu?";secure":"")}function aw(cs){if(ap){return 0}var cq=new RegExp("(^|;)[ ]*"+cs+"=([^;]*)"),cr=cq.exec(v.cookie);return cr?G(cr[2]):0}function cd(cq){var cr;if(al){cr=new RegExp("#.*");return cq.replace(cr,"")}return cq}function bX(cs,cq){var ct=l(cq),cr;if(ct){return cq}if(cq.slice(0,1)==="/"){return l(cs)+"://"+c(cs)+cq}cs=cd(cs);cr=cs.indexOf("?");if(cr>=0){cs=cs.slice(0,cr)}cr=cs.lastIndexOf("/");if(cr!==cs.length-1){cs=cs.slice(0,cr+1)}return cs+cq}function bD(ct){var cr,cq,cs;for(cr=0;cr<bB.length;cr++){cq=z(bB[cr].toLowerCase());if(ct===cq){return true}if(cq.slice(0,1)==="."){if(ct===cq.slice(1)){return true}cs=ct.length-cq.length;if((cs>0)&&(ct.slice(cs)===cq)){return true}}}return false}function cp(cq,cs){var cr=new Image(1,1);cr.onload=function(){u=0;if(typeof cs==="function"){cs()
-}};cr.src=ab+(ab.indexOf("?")<0?"?":"&")+cq}function bU(cr,cu,cq){if(!x(cq)||null===cq){cq=true}try{var ct=H.XMLHttpRequest?new H.XMLHttpRequest():H.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;ct.open("POST",ab,true);ct.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cq){cp(cr,cu)}else{if(typeof cu==="function"){cu()}}};ct.setRequestHeader("Content-Type",aP);ct.send(cr)}catch(cs){if(cq){cp(cr,cu)}}}function cc(cr){var cq=new Date();var cs=cq.getTime()+cr;if(!k||cs>k){k=cs}}function aC(cq){if(bb||!b3){return}bb=setTimeout(function cr(){bb=null;if(br()){return}var cs=new Date(),ct=b3-(cs.getTime()-bo);ct=Math.min(b3,ct);aC(ct)},cq||b3)}function be(){if(!bb){return}clearTimeout(bb);bb=null}function ax(){if(br()){return}aC()}function bm(){be()}function bF(){if(bN||!b3){return}bN=true;W(H,"focus",ax);W(H,"blur",bm);aC()}function aI(cu){var cr=new Date();var cq=cr.getTime();bo=cq;if(bg&&cq<bg){var cs=bg-cq;setTimeout(cu,cs);cc(cs+50);bg+=50;
-return}if(bg===false){var ct=800;bg=cq+ct}cu()}function a4(cr,cq,cs){if(!b9&&cr){aI(function(){if(bE==="POST"){bU(cr,cs)}else{cp(cr,cs)}cc(cq)})}if(!bN){bF()}else{aC()}}function bi(cq){if(b9){return false}return(cq&&cq.length)}function aq(cs,cq){if(!bi(cs)){return}var cr='{"requests":["?'+cs.join('","?')+'"]}';aI(function(){bU(cr,null,false);cc(cq)})}function bS(cq){return ch+cq+"."+b5+"."+bJ}function af(){if(ap){return"0"}if(!x(e.cookieEnabled)){var cq=bS("testcookie");bT(cq,"1");return aw(cq)==="1"?"1":"0"}return e.cookieEnabled?"1":"0"}function bv(){bJ=a2((ar||bY)+(ci||"/")).slice(0,4)}function at(){var cr=bS("cvar"),cq=aw(cr);if(cq.length){cq=JSON2.parse(cq);if(K(cq)){return cq}}return{}}function aa(){if(ah===false){ah=at()}}function cf(){return a2((e.userAgent||"")+(e.platform||"")+JSON2.stringify(b6)+(new Date()).getTime()+Math.random()).slice(0,16)}function Z(){var cs=new Date(),cq=Math.round(cs.getTime()/1000),cr=bS("id"),cv=aw(cr),cu,ct;if(cv){cu=cv.split(".");cu.unshift("0");if(a7.length){cu[1]=a7
-}return cu}if(a7.length){ct=a7}else{if("0"===af()){ct=""}else{ct=cf()}}cu=["1",ct,cq,0,cq,"",""];return cu}function bM(){var cx=Z(),ct=cx[0],cu=cx[1],cr=cx[2],cq=cx[3],cv=cx[4],cs=cx[5];if(!x(cx[6])){cx[6]=""}var cw=cx[6];return{newVisitor:ct,uuid:cu,createTs:cr,visitCount:cq,currentVisitTs:cv,lastVisitTs:cs,lastEcommerceOrderTs:cw}}function aQ(){var ct=new Date(),cr=ct.getTime(),cu=bM().createTs;var cq=parseInt(cu,10);var cs=(cq*1000)+ay-cr;return cs}function an(cq){if(!b5){return}var cs=new Date(),cr=Math.round(cs.getTime()/1000);if(!x(cq)){cq=bM()}var ct=cq.uuid+"."+cq.createTs+"."+cq.visitCount+"."+cr+"."+cq.lastVisitTs+"."+cq.lastEcommerceOrderTs;bT(bS("id"),ct,aQ(),ci,ar)}function Y(){var cq=aw(bS("ref"));if(cq.length){try{cq=JSON2.parse(cq);if(K(cq)){return cq}}catch(cr){}}return["","",0,""]}function b4(cs,cr,cq){bT(cs,"",-86400,cr,cq)}function bn(cr){var cq="testvalue";bT("test",cq,10000,null,cr);if(aw("test")===cq){b4("test",null,cr);return true}return false}function X(){var cs=ap;
-ap=false;var cq=["id","ses","cvar","ref"];var cr,ct;for(cr=0;cr<cq.length;cr++){ct=bS(cq[cr]);if(0!==aw(ct)){b4(ct,ci,ar)}}ap=cs}function cm(cq){b5=cq;an()}function b2(cu){if(!cu||!K(cu)){return}var ct=[];var cs;for(cs in cu){if(Object.prototype.hasOwnProperty.call(cu,cs)){ct.push(cs)}}var cv={};ct.sort();var cq=ct.length;var cr;for(cr=0;cr<cq;cr++){cv[ct[cr]]=cu[ct[cr]]}return cv}function a9(){bT(bS("ses"),"*",aE,ci,ar)}function aY(cs,cL,cM,ct){var cK,cr=new Date(),cz=Math.round(cr.getTime()/1000),cw,cJ,cu=1024,cQ,cA,cH=ah,cv=bS("ses"),cF=bS("ref"),cC=bS("cvar"),cD=aw(cv),cI=Y(),cO=bd||cj,cx,cq;if(ap){X()}if(b9){return""}var cE=bM();if(!x(ct)){ct=""}var cB=v.characterSet||v.charset;if(!cB||cB.toLowerCase()==="utf-8"){cB=null}cx=cI[0];cq=cI[1];cw=cI[2];cJ=cI[3];if(!cD){var cN=aE/1000;if(!cE.lastVisitTs||(cz-cE.lastVisitTs)>cN){cE.visitCount++;cE.lastVisitTs=cE.currentVisitTs}if(!ba||!cx.length){for(cK in a3){if(Object.prototype.hasOwnProperty.call(a3,cK)){cx=J(cO,a3[cK]);if(cx.length){break
-}}}for(cK in aU){if(Object.prototype.hasOwnProperty.call(aU,cK)){cq=J(cO,aU[cK]);if(cq.length){break}}}}cQ=c(bG);cA=cJ.length?c(cJ):"";if(cQ.length&&!bD(cQ)&&(!ba||!cA.length||bD(cA))){cJ=bG}if(cJ.length||cx.length){cw=cz;cI=[cx,cq,cw,cd(cJ.slice(0,cu))];bT(cF,JSON2.stringify(cI),bh,ci,ar)}}cs+="&idsite="+b5+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cr.getHours()+"&m="+cr.getMinutes()+"&s="+cr.getSeconds()+"&url="+m(cd(cO))+(bG.length?"&urlref="+m(cd(bG)):"")+((aO&&aO.length)?"&uid="+m(aO):"")+"&_id="+cE.uuid+"&_idts="+cE.createTs+"&_idvc="+cE.visitCount+"&_idn="+cE.newVisitor+(cx.length?"&_rcn="+m(cx):"")+(cq.length?"&_rck="+m(cq):"")+"&_refts="+cw+"&_viewts="+cE.lastVisitTs+(String(cE.lastEcommerceOrderTs).length?"&_ects="+cE.lastEcommerceOrderTs:"")+(String(cJ).length?"&_ref="+m(cd(cJ.slice(0,cu))):"")+(cB?"&cs="+m(cB):"")+"&send_image=0";for(cK in b6){if(Object.prototype.hasOwnProperty.call(b6,cK)){cs+="&"+cK+"="+b6[cK]}}if(cL){cs+="&data="+m(JSON2.stringify(cL))}else{if(ao){cs+="&data="+m(JSON2.stringify(ao))
-}}function cy(cR,cS){var cT=JSON2.stringify(cR);if(cT.length>2){return"&"+cS+"="+m(cT)}return""}var cP=b2(bq);var cG=b2(ac);cs+=cy(cP,"cvar");cs+=cy(cG,"e_cvar");if(ah){cs+=cy(ah,"_cvar");for(cK in cH){if(Object.prototype.hasOwnProperty.call(cH,cK)){if(ah[cK][0]===""||ah[cK][1]===""){delete ah[cK]}}}if(bc){bT(cC,JSON2.stringify(ah),aE,ci,ar)}}if(a6){if(aM){cs+="&gt_ms="+aM}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cs+="&gt_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cE.lastEcommerceOrderTs=x(ct)&&String(ct).length?ct:cE.lastEcommerceOrderTs;an(cE);a9();cs+=P(cM);if(bA.length){cs+="&"+bA}if(r(av)){cs=av(cs)}return cs}br=function bw(){var cq=new Date();if(bo+b3<=cq.getTime()){var cr=aY("ping=1",null,"ping");a4(cr,bz);return true}return false};function bW(ct,cs,cx,cu,cq,cA){var cv="idgoal=0",cw,cr=new Date(),cy=[],cz;if(String(ct).length){cv+="&ec_id="+m(ct);cw=Math.round(cr.getTime()/1000)}cv+="&revenue="+cs;if(String(cx).length){cv+="&ec_st="+cx}if(String(cu).length){cv+="&ec_tx="+cu
-}if(String(cq).length){cv+="&ec_sh="+cq}if(String(cA).length){cv+="&ec_dt="+cA}if(bR){for(cz in bR){if(Object.prototype.hasOwnProperty.call(bR,cz)){if(!x(bR[cz][1])){bR[cz][1]=""}if(!x(bR[cz][2])){bR[cz][2]=""}if(!x(bR[cz][3])||String(bR[cz][3]).length===0){bR[cz][3]=0}if(!x(bR[cz][4])||String(bR[cz][4]).length===0){bR[cz][4]=1}cy.push(bR[cz])}}cv+="&ec_items="+m(JSON2.stringify(cy))}cv=aY(cv,ao,"ecommerce",cw);a4(cv,bz)}function bV(cq,cu,ct,cs,cr,cv){if(String(cq).length&&x(cu)){bW(cq,cu,ct,cs,cr,cv)}}function cg(cq){if(x(cq)){bW("",cq,"","","","")}}function bl(cs,ct){var cq=new Date(),cr=aY("action_name="+m(U(cs||aX)),ct,"log");a4(cr,bz)}function aL(cs,cr){var ct,cq="(^| )(piwik[_-]"+cr;if(cs){for(ct=0;ct<cs.length;ct++){cq+="|"+cs[ct]}}cq+=")( |$)";return new RegExp(cq)}function bP(cq){return(ab&&cq&&0===String(cq).indexOf(ab))}function b1(cu,cq,cv,cr){if(bP(cq)){return 0}var ct=aL(bp,"download"),cs=aL(aH,"link"),cw=new RegExp("\\.("+aZ.join("|")+")([?&#]|$)","i");if(cs.test(cu)){return"link"
-}if(cr||ct.test(cu)||cw.test(cq)){return"download"}if(cv){return 0}return"link"}function bf(cr){var cq;cq=cr.parentNode;while(cq!==null&&x(cq)){if(R.isLinkElement(cr)){break}cr=cq;cq=cr.parentNode}return cr}function bt(cu){cu=bf(cu);if(!R.hasNodeAttribute(cu,"href")){return}if(!x(cu.href)){return}var ct=R.getAttributeValueFromNode(cu,"href");if(bP(ct)){return}var cv=cu.hostname||c(cu.href);var cw=cv.toLowerCase();var cr=cu.href.replace(cv,cw);var cs=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i");if(!cs.test(cr)){var cq=b1(cu.className,cr,bD(cw),R.hasNodeAttribute(cu,"download"));if(cq){return{type:cq,href:cr}}}}function cl(cq,cr,cs,ct){var cu=n.buildInteractionRequestParams(cq,cr,cs,ct);if(!cu){return}return aY(cu,null,"contentInteraction")}function ck(cs,ct,cx,cq,cr){if(!x(cs)){return}if(bP(cs)){return cs}var cv=n.toAbsoluteUrl(cs);var cu="redirecturl="+m(cv)+"&";cu+=cl(ct,cx,cq,(cr||cs));var cw="&";if(ab.indexOf("?")<0){cw="?"}return ab+cw+cu}function a8(cq,cr){if(!cq||!cr){return false
-}var cs=n.findTargetNode(cq);if(n.shouldIgnoreInteraction(cs)){return false}cs=n.findTargetNodeNoDefault(cq);if(cs&&!I(cs,cr)){return false}return true}function aW(cs,cr,cu){if(!cs){return}var cq=n.findParentContentNode(cs);if(!cq){return}if(!a8(cq,cs)){return}var ct=n.buildContentBlock(cq);if(!ct){return}if(!ct.target&&cu){ct.target=cu}return n.buildInteractionRequestParams(cr,ct.name,ct.piece,ct.target)}function aT(cr){if(!ai||!ai.length){return false}var cq,cs;for(cq=0;cq<ai.length;cq++){cs=ai[cq];if(cs&&cs.name===cr.name&&cs.piece===cr.piece&&cs.target===cr.target){return true}}return false}function ad(ct){if(!ct){return false}var cw=n.findTargetNode(ct);if(!cw||n.shouldIgnoreInteraction(cw)){return false}var cx=bt(cw);if(b7&&cx&&cx.type){return false}if(R.isLinkElement(cw)&&R.hasNodeAttributeWithValue(cw,"href")){var cq=String(R.getAttributeValueFromNode(cw,"href"));if(0===cq.indexOf("#")){return false}if(bP(cq)){return true}if(!n.isUrlToCurrentDomain(cq)){return false}var cu=n.buildContentBlock(ct);
-if(!cu){return}var cs=cu.name;var cy=cu.piece;var cv=cu.target;if(!R.hasNodeAttributeWithValue(cw,n.CONTENT_TARGET_ATTR)||cw.wasContentTargetAttrReplaced){cw.wasContentTargetAttrReplaced=true;cv=n.toAbsoluteUrl(cq);R.setAnyAttribute(cw,n.CONTENT_TARGET_ATTR,cv)}var cr=ck(cq,"click",cs,cy,cv);n.setHrefAttribute(cw,cr);return true}return false}function ag(cr){if(!cr||!cr.length){return}var cq;for(cq=0;cq<cr.length;cq++){ad(cr[cq])}}function bs(cq){return function(cr){if(!cq){return}var cu=n.findParentContentNode(cq);var cv;if(cr){cv=cr.target||cr.srcElement}if(!cv){cv=cq}if(!a8(cu,cv)){return}cc(bz);if(R.isLinkElement(cq)&&R.hasNodeAttributeWithValue(cq,"href")&&R.hasNodeAttributeWithValue(cq,n.CONTENT_TARGET_ATTR)){var cs=R.getAttributeValueFromNode(cq,"href");if(!bP(cs)&&cq.wasContentTargetAttrReplaced){R.setAnyAttribute(cq,n.CONTENT_TARGET_ATTR,"")}}var cz=bt(cq);if(bK&&cz&&cz.type){return cz.type}if(ad(cu)){return"href"}var cw=n.buildContentBlock(cu);if(!cw){return}var ct=cw.name;var cA=cw.piece;
-var cy=cw.target;var cx=cl("click",ct,cA,cy);a4(cx,bz);return cx}}function aK(cs){if(!cs||!cs.length){return}var cq,cr;for(cq=0;cq<cs.length;cq++){cr=n.findTargetNode(cs[cq]);if(cr&&!cr.contentInteractionTrackingSetupDone){cr.contentInteractionTrackingSetupDone=true;W(cr,"click",bs(cr))}}}function aG(cs,ct){if(!cs||!cs.length){return[]}var cq,cr;for(cq=0;cq<cs.length;cq++){if(aT(cs[cq])){cs.splice(cq,1);cq--}else{ai.push(cs[cq])}}if(!cs||!cs.length){return[]}ag(ct);aK(ct);var cu=[];for(cq=0;cq<cs.length;cq++){cr=aY(n.buildImpressionRequestParams(cs[cq].name,cs[cq].piece,cs[cq].target),undefined,"contentImpressions");cu.push(cr)}return cu}function a1(cr){var cq=n.collectContent(cr);return aG(cq,cr)}function bO(cr){if(!cr||!cr.length){return[]}var cq;for(cq=0;cq<cr.length;cq++){if(!n.isNodeVisible(cr[cq])){cr.splice(cq,1);cq--}}if(!cr||!cr.length){return[]}return a1(cr)}function bZ(cs,cq,cr){var ct=n.buildImpressionRequestParams(cs,cq,cr);return aY(ct,null,"contentImpression")}function a0(ct,cr){if(!ct){return
-}var cq=n.findParentContentNode(ct);var cs=n.buildContentBlock(cq);if(!cs){return}if(!cr){cr="Unknown"}return cl(cr,cs.name,cs.piece,cs.target)}function bI(cr,ct,cq,cs){return"e_c="+m(cr)+"&e_a="+m(ct)+(x(cq)?"&e_n="+m(cq):"")+(x(cs)?"&e_v="+m(cs):"")}function am(cs,cu,cq,ct,cv){if(String(cs).length===0||String(cu).length===0){return false}var cr=aY(bI(cs,cu,cq,ct),cv,"event");a4(cr,bz)}function aS(cq,ct,cr,cu){var cs=aY("search="+m(cq)+(ct?"&search_cat="+m(ct):"")+(x(cr)?"&search_count="+cr:""),cu,"sitesearch");a4(cs,bz)}function bx(cq,ct,cs){var cr=aY("idgoal="+cq+(ct?"&revenue="+ct:""),cs,"goal");a4(cr,bz)}function b0(ct,cq,cx,cw,cs){var cv=cq+"="+m(cd(ct));var cr=aW(cs,"click",ct);if(cr){cv+="&"+cr}var cu=aY(cv,cx,"link");a4(cu,(cw?0:bz),cw)}function b8(cr,cq){if(cr!==""){return cr+cq.charAt(0).toUpperCase()+cq.slice(1)}return cq}function aR(cv){var cu,cq,ct=["","webkit","ms","moz"],cs;if(!a5){for(cq=0;cq<ct.length;cq++){cs=ct[cq];if(Object.prototype.hasOwnProperty.call(v,b8(cs,"hidden"))){if(v[b8(cs,"visibilityState")]==="prerender"){cu=true
-}break}}}if(cu){W(v,cs+"visibilitychange",function cr(){v.removeEventListener(cs+"visibilitychange",cr,false);cv()});return}cv()}function aV(cq){if(v.readyState==="complete"){cq()}else{if(H.addEventListener){H.addEventListener("load",cq)}else{if(H.attachEvent){H.attachEvent("onLoad",cq)}}}}function aF(cr){var cq=false;if(v.attachEvent){cq=v.readyState==="complete"}else{cq=v.readyState!=="loading"}if(cq){cr()}else{if(v.addEventListener){v.addEventListener("DOMContentLoaded",cr)}else{if(v.attachEvent){v.attachEvent("onreadystatechange",cr)}}}}function bQ(cq){var cr=bt(cq);if(cr&&cr.type){cr.href=j(cr.href);b0(cr.href,cr.type,undefined,null,cq)}}function aN(){return v.all&&!v.addEventListener}function aD(cq){var cs=cq.which;var cr=(typeof cq.button);if(!cs&&cr!=="undefined"){if(aN()){if(cq.button&1){cs=1}else{if(cq.button&2){cs=3}else{if(cq.button&4){cs=2}}}}else{if(cq.button===0||cq.button==="0"){cs=1}else{if(cq.button&1){cs=2}else{if(cq.button&2){cs=3}}}}}return cs}function aA(cq){switch(aD(cq)){case 1:return"left";
-case 2:return"middle";case 3:return"right"}}function cb(cq){return cq.target||cq.srcElement}function co(cq){return function(ct){ct=ct||H.event;var cs=aA(ct);var cu=cb(ct);if(ct.type==="click"){var cr=false;if(cq&&cs==="middle"){cr=true}if(cu&&!cr){bQ(cu)}}else{if(ct.type==="mousedown"){if(cs==="middle"&&cu){bu=cs;au=cu}else{bu=au=null}}else{if(ct.type==="mouseup"){if(cs===bu&&cu===au){bQ(cu)}bu=au=null}else{if(ct.type==="contextmenu"){bQ(cu)}}}}}}function bL(cr,cq){W(cr,"click",co(cq),false);if(cq){W(cr,"mouseup",co(cq),false);W(cr,"mousedown",co(cq),false);W(cr,"contextmenu",co(cq),false)}}function bk(cr){if(!bK){bK=true;var cs,cq=aL(aj,"ignore"),ct=v.links;if(ct){for(cs=0;cs<ct.length;cs++){if(!cq.test(ct[cs].className)){bL(ct[cs],cr)}}}}}function bH(cs,cu,cv){if(aB){return true}aB=true;var cw=false;var ct,cr;function cq(){cw=true}aV(function(){function cx(cz){setTimeout(function(){if(!aB){return}cw=false;cv.trackVisibleContentImpressions();cx(cz)},cz)}function cy(cz){setTimeout(function(){if(!aB){return
-}if(cw){cw=false;cv.trackVisibleContentImpressions()}cy(cz)},cz)}if(cs){ct=["scroll","resize"];for(cr=0;cr<ct.length;cr++){if(v.addEventListener){v.addEventListener(ct[cr],cq)}else{H.attachEvent("on"+ct[cr],cq)}}cy(100)}if(cu&&cu>0){cu=parseInt(cu,10);cx(cu)}})}function ce(){var cr,cs,ct={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"},cq=H.devicePixelRatio||1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(cr in ct){if(Object.prototype.hasOwnProperty.call(ct,cr)){cs=e.mimeTypes[ct[cr]];b6[cr]=(cs&&cs.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&x(e.javaEnabled)&&e.javaEnabled()){b6.java="1"}if(r(H.GearsFactory)){b6.gears="1"}b6.cookie=af()}b6.res=L.width*cq+"x"+L.height*cq}ce();bv();an();return{getVisitorId:function(){return bM().uuid
-},getVisitorInfo:function(){return Z()},getAttributionInfo:function(){return Y()},getAttributionCampaignName:function(){return Y()[0]},getAttributionCampaignKeyword:function(){return Y()[1]},getAttributionReferrerTimestamp:function(){return Y()[2]},getAttributionReferrerUrl:function(){return Y()[3]},setTrackerUrl:function(cq){ab=cq},getTrackerUrl:function(){return ab},getSiteId:function(){return b5},setSiteId:function(cq){cm(cq)},setUserId:function(cq){if(!x(cq)||!cq.length){return}aO=cq;a7=a2(aO).substr(0,16)},getUserId:function(){return aO},setCustomData:function(cq,cr){if(K(cq)){ao=cq}else{if(!ao){ao={}}ao[cq]=cr}},getCustomData:function(){return ao},setCustomRequestProcessing:function(cq){av=cq},appendToTrackingUrl:function(cq){bA=cq},getRequest:function(cq){return aY(cq)},addPlugin:function(cq,cr){a[cq]=cr},setCustomVariable:function(cr,cq,cu,cs){var ct;if(!x(cs)){cs="visit"}if(!x(cq)){return}if(!x(cu)){cu=""}if(cr>0){cq=!o(cq)?String(cq):cq;cu=!o(cu)?String(cu):cu;ct=[cq.slice(0,ca),cu.slice(0,ca)];
-if(cs==="visit"||cs===2){aa();ah[cr]=ct}else{if(cs==="page"||cs===3){bq[cr]=ct}else{if(cs==="event"){ac[cr]=ct}}}}},getCustomVariable:function(cr,cs){var cq;if(!x(cs)){cs="visit"}if(cs==="page"||cs===3){cq=bq[cr]}else{if(cs==="event"){cq=ac[cr]}else{if(cs==="visit"||cs===2){aa();cq=ah[cr]}}}if(!x(cq)||(cq&&cq[0]==="")){return false}return cq},deleteCustomVariable:function(cq,cr){if(this.getCustomVariable(cq,cr)){this.setCustomVariable(cq,"","",cr)}},storeCustomVariablesInCookie:function(){bc=true},setLinkTrackingTimer:function(cq){bz=cq},setDownloadExtensions:function(cq){if(o(cq)){cq=cq.split("|")}aZ=cq},addDownloadExtensions:function(cr){var cq;if(o(cr)){cr=cr.split("|")}for(cq=0;cq<cr.length;cq++){aZ.push(cr[cq])}},removeDownloadExtensions:function(cs){var cr,cq=[];if(o(cs)){cs=cs.split("|")}for(cr=0;cr<aZ.length;cr++){if(A(cs,aZ[cr])===-1){cq.push(aZ[cr])}}aZ=cq},setDomains:function(cq){bB=o(cq)?[cq]:cq;bB.push(bY)},setIgnoreClasses:function(cq){aj=o(cq)?[cq]:cq},setRequestMethod:function(cq){bE=cq||bC
-},setRequestContentType:function(cq){aP=cq||bj},setReferrerUrl:function(cq){bG=cq},setCustomUrl:function(cq){bd=bX(cj,cq)},setDocumentTitle:function(cq){aX=cq},setAPIUrl:function(cq){az=cq},setDownloadClasses:function(cq){bp=o(cq)?[cq]:cq},setLinkClasses:function(cq){aH=o(cq)?[cq]:cq},setCampaignNameKey:function(cq){a3=o(cq)?[cq]:cq},setCampaignKeywordKey:function(cq){aU=o(cq)?[cq]:cq},discardHashTag:function(cq){al=cq},setCookieNamePrefix:function(cq){ch=cq;ah=at()},setCookieDomain:function(cq){var cr=z(cq);if(bn(cr)){ar=cr;bv()}},setCookiePath:function(cq){ci=cq;bv()},setVisitorCookieTimeout:function(cq){ay=cq*1000},setSessionCookieTimeout:function(cq){aE=cq*1000},setReferralCookieTimeout:function(cq){bh=cq*1000},setConversionAttributionFirstReferrer:function(cq){ba=cq},disableCookies:function(){ap=true;b6.cookie="0";if(b5){X()}},deleteCookies:function(){X()},setDoNotTrack:function(cr){var cq=e.doNotTrack||e.msDoNotTrack;b9=cr&&(cq==="yes"||cq==="1");if(b9){this.disableCookies()}},addListener:function(cr,cq){bL(cr,cq)
-},enableLinkTracking:function(cq){b7=true;if(q){bk(cq)}else{F.push(function(){bk(cq)})}},enableJSErrorTracking:function(){if(cn){return}cn=true;var cq=H.onerror;H.onerror=function(cv,ct,cs,cu,cr){aR(function(){var cw="JavaScript Errors";var cx=ct+":"+cs;if(cu){cx+=":"+cu}am(cw,cx,cv)});if(cq){return cq(cv,ct,cs,cu,cr)}return false}},disablePerformanceTracking:function(){a6=false},setGenerationTimeMs:function(cq){aM=parseInt(cq,10)},enableHeartBeatTimer:function(cq){cq=Math.max(cq,1);b3=(cq||15)*1000;if(bo!==null){bF()}},killFrame:function(){if(H.location!==H.top.location){H.top.location=H.location}},redirectFile:function(cq){if(H.location.protocol==="file:"){H.location=cq}},setCountPreRendered:function(cq){a5=cq},trackGoal:function(cq,cs,cr){aR(function(){bx(cq,cs,cr)})},trackLink:function(cr,cq,ct,cs){aR(function(){b0(cr,cq,ct,cs)})},trackPageView:function(cq,cr){ai=[];if(B(b5)){aR(function(){N(ab,az,b5)})}else{aR(function(){bl(cq,cr)})}},trackAllContentImpressions:function(){if(B(b5)){return
-}aR(function(){aF(function(){var cq=n.findContentNodes();var cr=a1(cq);aq(cr,bz)})})},trackVisibleContentImpressions:function(cq,cr){if(B(b5)){return}if(!x(cq)){cq=true}if(!x(cr)){cr=750}bH(cq,cr,this);aR(function(){aV(function(){var cs=n.findContentNodes();var ct=bO(cs);aq(ct,bz)})})},trackContentImpression:function(cs,cq,cr){if(B(b5)){return}if(!cs){return}cq=cq||"Unknown";aR(function(){var ct=bZ(cs,cq,cr);a4(ct,bz)})},trackContentImpressionsWithinNode:function(cq){if(B(b5)||!cq){return}aR(function(){if(aB){aV(function(){var cr=n.findContentNodesWithinNode(cq);var cs=bO(cr);aq(cs,bz)})}else{aF(function(){var cr=n.findContentNodesWithinNode(cq);var cs=a1(cr);aq(cs,bz)})}})},trackContentInteraction:function(cs,ct,cq,cr){if(B(b5)){return}if(!cs||!ct){return}cq=cq||"Unknown";aR(function(){var cu=cl(cs,ct,cq,cr);a4(cu,bz)})},trackContentInteractionNode:function(cr,cq){if(B(b5)||!cr){return}aR(function(){var cs=a0(cr,cq);a4(cs,bz)})},logAllContentBlocksOnPage:function(){var cr=n.findContentNodes();
-var cq=n.collectContent(cr);if(console!==undefined&&console&&console.log){console.log(cq)}},trackEvent:function(cr,ct,cq,cs){aR(function(){am(cr,ct,cq,cs)})},trackSiteSearch:function(cq,cs,cr){aR(function(){aS(cq,cs,cr)})},setEcommerceView:function(ct,cq,cs,cr){if(!x(cs)||!cs.length){cs=""}else{if(cs instanceof Array){cs=JSON2.stringify(cs)}}bq[5]=["_pkc",cs];if(x(cr)&&String(cr).length){bq[2]=["_pkp",cr]}if((!x(ct)||!ct.length)&&(!x(cq)||!cq.length)){return}if(x(ct)&&ct.length){bq[3]=["_pks",ct]}if(!x(cq)||!cq.length){cq=""}bq[4]=["_pkn",cq]},addEcommerceItem:function(cu,cq,cs,cr,ct){if(cu.length){bR[cu]=[cu,cq,cs,cr,ct]}},trackEcommerceOrder:function(cq,cu,ct,cs,cr,cv){bV(cq,cu,ct,cs,cr,cv)},trackEcommerceCartUpdate:function(cq){cg(cq)}}}function w(){return{push:S}}function b(ac,ab){var ad={};var Z,aa;for(Z=0;Z<ab.length;Z++){var X=ab[Z];ad[X]=1;for(aa=0;aa<ac.length;aa++){if(ac[aa]&&ac[aa][0]){var Y=ac[aa][0];if(X===Y){S(ac[aa]);delete ac[aa];if(ad[Y]>1){if(console!==undefined&&console&&console.error){console.error("The method "+Y+' 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')
-}}ad[Y]++}}}}return ac}W(H,"beforeunload",T,false);p();Date.prototype.getTimeAlias=Date.prototype.getTime;M=new E();var s=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setUserId","setSiteId","enableLinkTracking"];_paq=b(_paq,s);for(u=0;u<_paq.length;u++){if(_paq[u]){S(_paq[u])}}_paq=new w();d={addPlugin:function(X,Y){a[X]=Y},getTracker:function(X,Y){if(!x(Y)){Y=this.getAsyncTracker().getSiteId()}if(!x(X)){X=this.getAsyncTracker().getTrackerUrl()}return new E(X,Y)},getAsyncTracker:function(){return M}};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=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=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 Piwik!=="object"){Piwik=(function(){var k,a={},w=document,e=navigator,M=screen,I=window,f=I.performance||I.mozPerformance||I.msPerformance||I.webkitPerformance,q=false,G=[],m=I.encodeURIComponent,H=I.decodeURIComponent,h=unescape,N,v,d;
+function j(Y){try{return H(Y)}catch(Z){return unescape(Y)}}function y(Z){var Y=typeof Z;return Y!=="undefined"}function r(Y){return typeof Y==="function"}function L(Y){return typeof Y==="object"}function o(Y){return typeof Y==="string"||Y instanceof String}function s(Z){if(!Z){return true}var Y;var aa=true;for(Y in Z){if(Object.prototype.hasOwnProperty.call(Z,Y)){aa=false}}return aa}function T(){var Y,aa,Z;for(Y=0;Y<arguments.length;Y+=1){Z=arguments[Y];aa=Z.shift();if(o(aa)){N[aa].apply(N,Z)}else{aa.apply(N,Z)}}}function X(ab,aa,Z,Y){if(ab.addEventListener){ab.addEventListener(aa,Z,Y);return true}if(ab.attachEvent){return ab.attachEvent("on"+aa,Z)}ab["on"+aa]=Z}function Q(Z,ac){var Y="",ab,aa;for(ab in a){if(Object.prototype.hasOwnProperty.call(a,ab)){aa=a[ab][Z];if(r(aa)){Y+=aa(ac)}}}return Y}function U(){var Y;Q("unload");if(k){do{Y=new Date()}while(Y.getTimeAlias()<k)}}function R(){var Y;if(!q){q=true;Q("load");for(Y=0;Y<G.length;Y++){G[Y]()}}return true}function p(){var Z;if(w.addEventListener){X(w,"DOMContentLoaded",function Y(){w.removeEventListener("DOMContentLoaded",Y,false);
+R()})}else{if(w.attachEvent){w.attachEvent("onreadystatechange",function Y(){if(w.readyState==="complete"){w.detachEvent("onreadystatechange",Y);R()}});if(w.documentElement.doScroll&&I===I.top){(function Y(){if(!q){try{w.documentElement.doScroll("left")}catch(aa){setTimeout(Y,0);return}R()}}())}}}if((new RegExp("WebKit")).test(e.userAgent)){Z=setInterval(function(){if(q||/loaded|complete/.test(w.readyState)){clearInterval(Z);R()}},10)}X(I,"load",R,false)}function i(aa,Z){var Y=w.createElement("script");Y.type="text/javascript";Y.src=aa;if(Y.readyState){Y.onreadystatechange=function(){var ab=this.readyState;if(ab==="loaded"||ab==="complete"){Y.onreadystatechange=null;Z()}}}else{Y.onload=Z}w.getElementsByTagName("head")[0].appendChild(Y)}function z(){var Y="";try{Y=I.top.document.referrer}catch(aa){if(I.parent){try{Y=I.parent.document.referrer}catch(Z){Y=""}}}if(Y===""){Y=w.referrer}return Y}function l(Y){var aa=new RegExp("^([a-z]+):"),Z=aa.exec(Y);return Z?Z[1]:null}function c(Y){var aa=new RegExp("^(?:(?:https?|ftp):)/*(?:[^@]+@)?([^:/#]+)"),Z=aa.exec(Y);
+return Z?Z[1]:Y}function K(aa,Z){var Y="[\\?&#]"+Z+"=([^&#]*)";var ac=new RegExp(Y);var ab=ac.exec(aa);return ab?H(ab[1]):""}function u(Y){return unescape(m(Y))}function W(an){var aa=function(au,at){return(au<<at)|(au>>>(32-at))},ao=function(aw){var au="",av,at;for(av=7;av>=0;av--){at=(aw>>>(av*4))&15;au+=at.toString(16)}return au},ad,aq,ap,Z=[],ah=1732584193,af=4023233417,ae=2562383102,ac=271733878,ab=3285377520,am,al,ak,aj,ai,ar,Y,ag=[];an=u(an);Y=an.length;for(aq=0;aq<Y-3;aq+=4){ap=an.charCodeAt(aq)<<24|an.charCodeAt(aq+1)<<16|an.charCodeAt(aq+2)<<8|an.charCodeAt(aq+3);ag.push(ap)}switch(Y&3){case 0:aq=2147483648;break;case 1:aq=an.charCodeAt(Y-1)<<24|8388608;break;case 2:aq=an.charCodeAt(Y-2)<<24|an.charCodeAt(Y-1)<<16|32768;break;case 3:aq=an.charCodeAt(Y-3)<<24|an.charCodeAt(Y-2)<<16|an.charCodeAt(Y-1)<<8|128;break}ag.push(aq);while((ag.length&15)!==14){ag.push(0)}ag.push(Y>>>29);ag.push((Y<<3)&4294967295);for(ad=0;ad<ag.length;ad+=16){for(aq=0;aq<16;aq++){Z[aq]=ag[ad+aq]}for(aq=16;
+aq<=79;aq++){Z[aq]=aa(Z[aq-3]^Z[aq-8]^Z[aq-14]^Z[aq-16],1)}am=ah;al=af;ak=ae;aj=ac;ai=ab;for(aq=0;aq<=19;aq++){ar=(aa(am,5)+((al&ak)|(~al&aj))+ai+Z[aq]+1518500249)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}for(aq=20;aq<=39;aq++){ar=(aa(am,5)+(al^ak^aj)+ai+Z[aq]+1859775393)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}for(aq=40;aq<=59;aq++){ar=(aa(am,5)+((al&ak)|(al&aj)|(ak&aj))+ai+Z[aq]+2400959708)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}for(aq=60;aq<=79;aq++){ar=(aa(am,5)+(al^ak^aj)+ai+Z[aq]+3395469782)&4294967295;ai=aj;aj=ak;ak=aa(al,30);al=am;am=ar}ah=(ah+am)&4294967295;af=(af+al)&4294967295;ae=(ae+ak)&4294967295;ac=(ac+aj)&4294967295;ab=(ab+ai)&4294967295}ar=ao(ah)+ao(af)+ao(ae)+ao(ac)+ao(ab);return ar.toLowerCase()}function P(aa,Y,Z){if(!aa){aa=""}if(!Y){Y=""}if(aa==="translate.googleusercontent.com"){if(Z===""){Z=Y}Y=K(Y,"u");aa=c(Y)}else{if(aa==="cc.bingj.com"||aa==="webcache.googleusercontent.com"||aa.slice(0,5)==="74.6."){Y=w.links[0].href;aa=c(Y)}}return[aa,Y,Z]
+}function A(Z){var Y=Z.length;if(Z.charAt(--Y)==="."){Z=Z.slice(0,Y)}if(Z.slice(0,2)==="*."){Z=Z.slice(1)}return Z}function V(Z){Z=Z&&Z.text?Z.text:Z;if(!o(Z)){var Y=w.getElementsByTagName("title");if(Y&&y(Y[0])){Z=Y[0].text}}return Z}function E(Y){if(!Y){return[]}if(!y(Y.children)&&y(Y.childNodes)){return Y.children}if(y(Y.children)){return Y.children}return[]}function J(Z,Y){if(!Z||!Y){return false}if(Z.contains){return Z.contains(Y)}if(Z===Y){return true}if(Z.compareDocumentPosition){return !!(Z.compareDocumentPosition(Y)&16)}return false}function B(aa,ab){if(aa&&aa.indexOf){return aa.indexOf(ab)}if(!y(aa)||aa===null){return -1}if(!aa.length){return -1}var Y=aa.length;if(Y===0){return -1}var Z=0;while(Z<Y){if(aa[Z]===ab){return Z}Z++}return -1}function g(aa){if(!aa){return false}function Y(ac,ad){if(I.getComputedStyle){return w.defaultView.getComputedStyle(ac,null)[ad]}if(ac.currentStyle){return ac.currentStyle[ad]}}function ab(ac){ac=ac.parentNode;while(ac){if(ac===w){return true}ac=ac.parentNode
+}return false}function Z(ae,ak,ac,ah,af,ai,ag){var ad=ae.parentNode,aj=1;if(!ab(ae)){return false}if(9===ad.nodeType){return true}if("0"===Y(ae,"opacity")||"none"===Y(ae,"display")||"hidden"===Y(ae,"visibility")){return false}if(!y(ak)||!y(ac)||!y(ah)||!y(af)||!y(ai)||!y(ag)){ak=ae.offsetTop;af=ae.offsetLeft;ah=ak+ae.offsetHeight;ac=af+ae.offsetWidth;ai=ae.offsetWidth;ag=ae.offsetHeight}if(aa===ae&&(0===ag||0===ai)&&"hidden"===Y(ae,"overflow")){return false}if(ad){if(("hidden"===Y(ad,"overflow")||"scroll"===Y(ad,"overflow"))){if(af+aj>ad.offsetWidth+ad.scrollLeft||af+ai-aj<ad.scrollLeft||ak+aj>ad.offsetHeight+ad.scrollTop||ak+ag-aj<ad.scrollTop){return false}}if(ae.offsetParent===ad){af+=ad.offsetLeft;ak+=ad.offsetTop}return Z(ad,ak,ac,ah,af,ai,ag)}return true}return Z(aa)}var S={htmlCollectionToArray:function(aa){var Y=[],Z;if(!aa||!aa.length){return Y}for(Z=0;Z<aa.length;Z++){Y.push(aa[Z])}return Y},find:function(Y){if(!document.querySelectorAll||!Y){return[]}var Z=document.querySelectorAll(Y);
+return this.htmlCollectionToArray(Z)},findMultiple:function(aa){if(!aa||!aa.length){return[]}var Z,ab;var Y=[];for(Z=0;Z<aa.length;Z++){ab=this.find(aa[Z]);Y=Y.concat(ab)}Y=this.makeNodesUnique(Y);return Y},findNodesByTagName:function(Z,Y){if(!Z||!Y||!Z.getElementsByTagName){return[]}var aa=Z.getElementsByTagName(Y);return this.htmlCollectionToArray(aa)},makeNodesUnique:function(Y){var ad=[].concat(Y);Y.sort(function(af,ae){if(af===ae){return 0}var ah=B(ad,af);var ag=B(ad,ae);if(ah===ag){return 0}return ah>ag?-1:1});if(Y.length<=1){return Y}var Z=0;var ab=0;var ac=[];var aa;aa=Y[Z++];while(aa){if(aa===Y[Z]){ab=ac.push(Z)}aa=Y[Z++]||null}while(ab--){Y.splice(ac[ab],1)}return Y},getAttributeValueFromNode:function(ac,aa){if(!this.hasNodeAttribute(ac,aa)){return}if(ac&&ac.getAttribute){return ac.getAttribute(aa)}if(!ac||!ac.attributes){return}var ab=(typeof ac.attributes[aa]);if("undefined"===ab){return}if(ac.attributes[aa].value){return ac.attributes[aa].value}if(ac.attributes[aa].nodeValue){return ac.attributes[aa].nodeValue
+}var Z;var Y=ac.attributes;if(!Y){return}for(Z=0;Z<Y.length;Z++){if(Y[Z].nodeName===aa){return Y[Z].nodeValue}}return null},hasNodeAttributeWithValue:function(Z,Y){var aa=this.getAttributeValueFromNode(Z,Y);return !!aa},hasNodeAttribute:function(aa,Y){if(aa&&aa.hasAttribute){return aa.hasAttribute(Y)}if(aa&&aa.attributes){var Z=(typeof aa.attributes[Y]);return"undefined"!==Z}return false},hasNodeCssClass:function(aa,Y){if(aa&&Y&&aa.className){var Z=typeof aa.className==="string"?aa.className.split(" "):[];if(-1!==B(Z,Y)){return true}}return false},findNodesHavingAttribute:function(ac,aa,Y){if(!Y){Y=[]}if(!ac||!aa){return Y}var ab=E(ac);if(!ab||!ab.length){return Y}var Z,ad;for(Z=0;Z<ab.length;Z++){ad=ab[Z];if(this.hasNodeAttribute(ad,aa)){Y.push(ad)}Y=this.findNodesHavingAttribute(ad,aa,Y)}return Y},findFirstNodeHavingAttribute:function(aa,Z){if(!aa||!Z){return}if(this.hasNodeAttribute(aa,Z)){return aa}var Y=this.findNodesHavingAttribute(aa,Z);if(Y&&Y.length){return Y[0]}},findFirstNodeHavingAttributeWithValue:function(ab,aa){if(!ab||!aa){return
+}if(this.hasNodeAttributeWithValue(ab,aa)){return ab}var Y=this.findNodesHavingAttribute(ab,aa);if(!Y||!Y.length){return}var Z;for(Z=0;Z<Y.length;Z++){if(this.getAttributeValueFromNode(Y[Z],aa)){return Y[Z]}}},findNodesHavingCssClass:function(ac,ab,Y){if(!Y){Y=[]}if(!ac||!ab){return Y}if(ac.getElementsByClassName){var ad=ac.getElementsByClassName(ab);return this.htmlCollectionToArray(ad)}var aa=E(ac);if(!aa||!aa.length){return[]}var Z,ae;for(Z=0;Z<aa.length;Z++){ae=aa[Z];if(this.hasNodeCssClass(ae,ab)){Y.push(ae)}Y=this.findNodesHavingCssClass(ae,ab,Y)}return Y},findFirstNodeHavingClass:function(aa,Z){if(!aa||!Z){return}if(this.hasNodeCssClass(aa,Z)){return aa}var Y=this.findNodesHavingCssClass(aa,Z);if(Y&&Y.length){return Y[0]}},isLinkElement:function(Z){if(!Z){return false}var Y=String(Z.nodeName).toLowerCase();var ab=["a","area"];var aa=B(ab,Y);return aa!==-1},setAnyAttribute:function(Z,Y,aa){if(!Z||!Y){return}if(Z.setAttribute){Z.setAttribute(Y,aa)}else{Z[Y]=aa}}};var n={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 Z="."+this.CONTENT_CLASS;
+var Y="["+this.CONTENT_ATTR+"]";var aa=S.findMultiple([Z,Y]);return aa},findContentNodesWithinNode:function(ab){if(!ab){return[]}var Z=S.findNodesHavingCssClass(ab,this.CONTENT_CLASS);var Y=S.findNodesHavingAttribute(ab,this.CONTENT_ATTR);if(Y&&Y.length){var aa;for(aa=0;aa<Y.length;aa++){Z.push(Y[aa])}}if(S.hasNodeAttribute(ab,this.CONTENT_ATTR)){Z.push(ab)}else{if(S.hasNodeCssClass(ab,this.CONTENT_CLASS)){Z.push(ab)}}Z=S.makeNodesUnique(Z);return Z},findParentContentNode:function(Z){if(!Z){return}var aa=Z;var Y=0;while(aa&&aa!==w&&aa.parentNode){if(S.hasNodeAttribute(aa,this.CONTENT_ATTR)){return aa}if(S.hasNodeCssClass(aa,this.CONTENT_CLASS)){return aa}aa=aa.parentNode;if(Y>1000){break}Y++}},findPieceNode:function(Z){var Y;Y=S.findFirstNodeHavingAttribute(Z,this.CONTENT_PIECE_ATTR);if(!Y){Y=S.findFirstNodeHavingClass(Z,this.CONTENT_PIECE_CLASS)}if(Y){return Y}return Z},findTargetNodeNoDefault:function(Y){if(!Y){return}var Z=S.findFirstNodeHavingAttributeWithValue(Y,this.CONTENT_TARGET_ATTR);
+if(Z){return Z}Z=S.findFirstNodeHavingAttribute(Y,this.CONTENT_TARGET_ATTR);if(Z){return Z}Z=S.findFirstNodeHavingClass(Y,this.CONTENT_TARGET_CLASS);if(Z){return Z}},findTargetNode:function(Y){var Z=this.findTargetNodeNoDefault(Y);if(Z){return Z}return Y},findContentName:function(Z){if(!Z){return}var ac=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_NAME_ATTR);if(ac){return S.getAttributeValueFromNode(ac,this.CONTENT_NAME_ATTR)}var Y=this.findContentPiece(Z);if(Y){return this.removeDomainIfIsInLink(Y)}if(S.hasNodeAttributeWithValue(Z,"title")){return S.getAttributeValueFromNode(Z,"title")}var aa=this.findPieceNode(Z);if(S.hasNodeAttributeWithValue(aa,"title")){return S.getAttributeValueFromNode(aa,"title")}var ab=this.findTargetNode(Z);if(S.hasNodeAttributeWithValue(ab,"title")){return S.getAttributeValueFromNode(ab,"title")}},findContentPiece:function(Z){if(!Z){return}var ab=S.findFirstNodeHavingAttributeWithValue(Z,this.CONTENT_PIECE_ATTR);if(ab){return S.getAttributeValueFromNode(ab,this.CONTENT_PIECE_ATTR)
+}var Y=this.findPieceNode(Z);var aa=this.findMediaUrlInNode(Y);if(aa){return this.toAbsoluteUrl(aa)}},findContentTarget:function(aa){if(!aa){return}var ab=this.findTargetNode(aa);if(S.hasNodeAttributeWithValue(ab,this.CONTENT_TARGET_ATTR)){return S.getAttributeValueFromNode(ab,this.CONTENT_TARGET_ATTR)}var Z;if(S.hasNodeAttributeWithValue(ab,"href")){Z=S.getAttributeValueFromNode(ab,"href");return this.toAbsoluteUrl(Z)}var Y=this.findPieceNode(aa);if(S.hasNodeAttributeWithValue(Y,"href")){Z=S.getAttributeValueFromNode(Y,"href");return this.toAbsoluteUrl(Z)}},isSameDomain:function(Y){if(!Y||!Y.indexOf){return false}if(0===Y.indexOf(this.getLocation().origin)){return true}var Z=Y.indexOf(this.getLocation().host);if(8>=Z&&0<=Z){return true}return false},removeDomainIfIsInLink:function(aa){var Z="^https?://[^/]+";var Y="^.*//[^/]+";if(aa&&aa.search&&-1!==aa.search(new RegExp(Z))&&this.isSameDomain(aa)){aa=aa.replace(new RegExp(Y),"");if(!aa){aa="/"}}return aa},findMediaUrlInNode:function(ac){if(!ac){return
+}var aa=["img","embed","video","audio"];var Y=ac.nodeName.toLowerCase();if(-1!==B(aa,Y)&&S.findFirstNodeHavingAttributeWithValue(ac,"src")){var ab=S.findFirstNodeHavingAttributeWithValue(ac,"src");return S.getAttributeValueFromNode(ab,"src")}if(Y==="object"&&S.hasNodeAttributeWithValue(ac,"data")){return S.getAttributeValueFromNode(ac,"data")}if(Y==="object"){var ad=S.findNodesByTagName(ac,"param");if(ad&&ad.length){var Z;for(Z=0;Z<ad.length;Z++){if("movie"===S.getAttributeValueFromNode(ad[Z],"name")&&S.hasNodeAttributeWithValue(ad[Z],"value")){return S.getAttributeValueFromNode(ad[Z],"value")}}}var ae=S.findNodesByTagName(ac,"embed");if(ae&&ae.length){return this.findMediaUrlInNode(ae[0])}}},trim:function(Y){if(Y&&String(Y)===Y){return Y.replace(/^\s+|\s+$/g,"")}return Y},isOrWasNodeInViewport:function(ad){if(!ad||!ad.getBoundingClientRect||ad.nodeType!==1){return true}var ac=ad.getBoundingClientRect();var ab=w.documentElement||{};var aa=ac.top<0;if(aa&&ad.offsetTop){aa=(ad.offsetTop+ac.height)>0
+}var Z=ab.clientWidth;if(I.innerWidth&&Z>I.innerWidth){Z=I.innerWidth}var Y=ab.clientHeight;if(I.innerHeight&&Y>I.innerHeight){Y=I.innerHeight}return((ac.bottom>0||aa)&&ac.right>0&&ac.left<Z&&((ac.top<Y)||aa))},isNodeVisible:function(Z){var Y=g(Z);var aa=this.isOrWasNodeInViewport(Z);return Y&&aa},buildInteractionRequestParams:function(Y,Z,aa,ab){var ac="";if(Y){ac+="c_i="+m(Y)}if(Z){if(ac){ac+="&"}ac+="c_n="+m(Z)}if(aa){if(ac){ac+="&"}ac+="c_p="+m(aa)}if(ab){if(ac){ac+="&"}ac+="c_t="+m(ab)}return ac},buildImpressionRequestParams:function(Y,Z,aa){var ab="c_n="+m(Y)+"&c_p="+m(Z);if(aa){ab+="&c_t="+m(aa)}return ab},buildContentBlock:function(aa){if(!aa){return}var Y=this.findContentName(aa);var Z=this.findContentPiece(aa);var ab=this.findContentTarget(aa);Y=this.trim(Y);Z=this.trim(Z);ab=this.trim(ab);return{name:Y||"Unknown",piece:Z||"Unknown",target:ab||""}},collectContent:function(ab){if(!ab||!ab.length){return[]}var aa=[];var Y,Z;for(Y=0;Y<ab.length;Y++){Z=this.buildContentBlock(ab[Y]);
+if(y(Z)){aa.push(Z)}}return aa},setLocation:function(Y){this.location=Y},getLocation:function(){var Y=this.location||I.location;if(!Y.origin){Y.origin=Y.protocol+"//"+Y.hostname+(Y.port?":"+Y.port:"")}return Y},toAbsoluteUrl:function(Z){if((!Z||String(Z)!==Z)&&Z!==""){return Z}if(""===Z){return this.getLocation().href}if(Z.search(/^\/\//)!==-1){return this.getLocation().protocol+Z}if(Z.search(/:\/\//)!==-1){return Z}if(0===Z.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+Z}if(0===Z.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+Z}if(0===Z.search("^[a-zA-Z]{2,11}:")){return Z}if(Z.search(/^\//)!==-1){return this.getLocation().origin+Z}var Y="(.*/)";var aa=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(Y))[0];return aa+Z},isUrlToCurrentDomain:function(Z){var aa=this.toAbsoluteUrl(Z);if(!aa){return false}var Y=this.getLocation().origin;if(Y===aa){return true}if(0===String(aa).indexOf(Y)){if(":"===String(aa).substr(Y.length,1)){return false
+}return true}return false},setHrefAttribute:function(Z,Y){if(!Z||!Y){return}S.setAnyAttribute(Z,"href",Y)},shouldIgnoreInteraction:function(aa){var Z=S.hasNodeAttribute(aa,this.CONTENT_IGNOREINTERACTION_ATTR);var Y=S.hasNodeCssClass(aa,this.CONTENT_IGNOREINTERACTION_CLASS);return Z||Y}};function D(Y,Z){if(Z){return Z}if(Y.slice(-9)==="piwik.php"){Y=Y.slice(0,Y.length-9)}return Y}function C(ae){var ag="Piwik_Overlay";var Z=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)(&segment=.*)?$");var aa=Z.exec(w.referrer);if(aa){var ac=aa[1];if(ac!==String(ae)){return false}var ad=aa[2],Y=aa[3],ab=aa[4];if(!ab){ab=""}else{if(ab.indexOf("&segment=")===0){ab=ab.substr("&segment=".length)}}I.name=ag+"###"+ad+"###"+Y+"###"+ab}var af=I.name.split("###");return af.length===4&&af[0]===ag}function O(Z,af,ab){var ae=I.name.split("###"),ad=ae[1],Y=ae[2],ac=ae[3],aa=D(Z,af);i(aa+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(aa,ab,ad,Y,ac)
+})}function F(aK,bz){var af=P(w.domain,I.location.href,z()),b0=A(af[0]),cl=j(af[1]),bH=j(af[2]),cp=false,bD="GET",bF=bD,bk="application/x-www-form-urlencoded; charset=UTF-8",aQ=bk,ac=aK||"",aA="",bB="",b7=bz||"",aP="",a8="",be,aY=w.title,a0=["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"],bC=[b0],ak=[],bq=[],aI=[],bA=500,al,b5,bs,am,ap,a4=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],aV=["pk_kwd","piwik_kwd","utm_term"],cj="_pk_",at,ck,aq=false,cb,a6,bb,az=33955200000,aF=1800000,bi=15768000000,a7=true,aN=0,bd=false,ai=false,aw,br={},ad={},bS={},cc=200,bT={},b8={},aj=[],aC=false,bh=false,bL=false,b9=false,bO=false,bp=null,bc,bv,av,a3=W,bK;
+function bV(cy,cv,cu,cx,ct,cw){if(aq){return}var cs;if(cu){cs=new Date();cs.setTime(cs.getTime()+cu)}w.cookie=cy+"="+m(cv)+(cu?";expires="+cs.toGMTString():"")+";path="+(cx||"/")+(ct?";domain="+ct:"")+(cw?";secure":"")}function ax(cu){if(aq){return 0}var cs=new RegExp("(^|;)[ ]*"+cu+"=([^;]*)"),ct=cs.exec(w.cookie);return ct?H(ct[2]):0}function cf(cs){var ct;if(am){ct=new RegExp("#.*");return cs.replace(ct,"")}return cs}function bZ(cu,cs){var cv=l(cs),ct;if(cv){return cs}if(cs.slice(0,1)==="/"){return l(cu)+"://"+c(cu)+cs}cu=cf(cu);ct=cu.indexOf("?");if(ct>=0){cu=cu.slice(0,ct)}ct=cu.lastIndexOf("/");if(ct!==cu.length-1){cu=cu.slice(0,ct+1)}return cu+cs}function bE(cv){var ct,cs,cu;for(ct=0;ct<bC.length;ct++){cs=A(bC[ct].toLowerCase());if(cv===cs){return true}if(cs.slice(0,1)==="."){if(cv===cs.slice(1)){return true}cu=cv.length-cs.length;if((cu>0)&&(cv.slice(cu)===cs)){return true}}}return false}function cr(cs,cu){var ct=new Image(1,1);ct.onload=function(){v=0;if(typeof cu==="function"){cu()
+}};ct.src=ac+(ac.indexOf("?")<0?"?":"&")+cs}function bW(ct,cw,cs){if(!y(cs)||null===cs){cs=true}try{var cv=I.XMLHttpRequest?new I.XMLHttpRequest():I.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cv.open("POST",ac,true);cv.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cs){cr(ct,cw)}else{if(typeof cw==="function"){cw()}}};cv.setRequestHeader("Content-Type",aQ);cv.send(ct)}catch(cu){if(cs){cr(ct,cw)}}}function ce(ct){var cs=new Date();var cu=cs.getTime()+ct;if(!k||cu>k){k=cu}}function aD(cs){if(bc||!b5){return}bc=setTimeout(function ct(){bc=null;if(bs()){return}var cu=new Date(),cv=b5-(cu.getTime()-bp);cv=Math.min(b5,cv);aD(cv)},cs||b5)}function bf(){if(!bc){return}clearTimeout(bc);bc=null}function ay(){if(bs()){return}aD()}function bn(){bf()}function bG(){if(bO||!b5){return}bO=true;X(I,"focus",ay);X(I,"blur",bn);aD()}function aJ(cw){var ct=new Date();var cs=ct.getTime();bp=cs;if(bh&&cs<bh){var cu=bh-cs;setTimeout(cw,cu);ce(cu+50);bh+=50;
+return}if(bh===false){var cv=800;bh=cs+cv}cw()}function a5(ct,cs,cu){if(!cb&&ct){aJ(function(){if(bF==="POST"){bW(ct,cu)}else{cr(ct,cu)}ce(cs)})}if(!bO){bG()}else{aD()}}function bj(cs){if(cb){return false}return(cs&&cs.length)}function ar(cu,cs){if(!bj(cu)){return}var ct='{"requests":["?'+cu.join('","?')+'"]}';aJ(function(){bW(ct,null,false);ce(cs)})}function bU(cs){return cj+cs+"."+b7+"."+bK}function ag(){if(aq){return"0"}if(!y(e.cookieEnabled)){var cs=bU("testcookie");bV(cs,"1");return ax(cs)==="1"?"1":"0"}return e.cookieEnabled?"1":"0"}function bw(){bK=a3((at||b0)+(ck||"/")).slice(0,4)}function au(){var ct=bU("cvar"),cs=ax(ct);if(cs.length){cs=JSON2.parse(cs);if(L(cs)){return cs}}return{}}function ab(){if(ai===false){ai=au()}}function ch(){return a3((e.userAgent||"")+(e.platform||"")+JSON2.stringify(b8)+(new Date()).getTime()+Math.random()).slice(0,16)}function aa(){var cu=new Date(),cs=Math.round(cu.getTime()/1000),ct=bU("id"),cx=ax(ct),cw,cv;if(cx){cw=cx.split(".");cw.unshift("0");
+if(a8.length){cw[1]=a8}return cw}if(a8.length){cv=a8}else{if("0"===ag()){cv=""}else{cv=ch()}}cw=["1",cv,cs,0,cs,"",""];return cw}function bN(){var cz=aa(),cv=cz[0],cw=cz[1],ct=cz[2],cs=cz[3],cx=cz[4],cu=cz[5];if(!y(cz[6])){cz[6]=""}var cy=cz[6];return{newVisitor:cv,uuid:cw,createTs:ct,visitCount:cs,currentVisitTs:cx,lastVisitTs:cu,lastEcommerceOrderTs:cy}}function aR(){var cv=new Date(),ct=cv.getTime(),cw=bN().createTs;var cs=parseInt(cw,10);var cu=(cs*1000)+az-ct;return cu}function ao(cs){if(!b7){return}var cu=new Date(),ct=Math.round(cu.getTime()/1000);if(!y(cs)){cs=bN()}var cv=cs.uuid+"."+cs.createTs+"."+cs.visitCount+"."+ct+"."+cs.lastVisitTs+"."+cs.lastEcommerceOrderTs;bV(bU("id"),cv,aR(),ck,at)}function Z(){var cs=ax(bU("ref"));if(cs.length){try{cs=JSON2.parse(cs);if(L(cs)){return cs}}catch(ct){}}return["","",0,""]}function b6(cu,ct,cs){bV(cu,"",-86400,ct,cs)}function bo(ct){var cs="testvalue";bV("test",cs,10000,null,ct);if(ax("test")===cs){b6("test",null,ct);return true}return false
+}function Y(){var cu=aq;aq=false;var cs=["id","ses","cvar","ref"];var ct,cv;for(ct=0;ct<cs.length;ct++){cv=bU(cs[ct]);if(0!==ax(cv)){b6(cv,ck,at)}}aq=cu}function co(cs){b7=cs;ao()}function b4(cw){if(!cw||!L(cw)){return}var cv=[];var cu;for(cu in cw){if(Object.prototype.hasOwnProperty.call(cw,cu)){cv.push(cu)}}var cx={};cv.sort();var cs=cv.length;var ct;for(ct=0;ct<cs;ct++){cx[cv[ct]]=cw[cv[ct]]}return cx}function ba(){bV(bU("ses"),"*",aF,ck,at)}function aZ(cu,cP,cQ,cv){var cO,ct=new Date(),cC=Math.round(ct.getTime()/1000),cz,cN,cw=1024,cV,cD,cL=ai,cx=bU("ses"),cJ=bU("ref"),cG=bU("cvar"),cH=ax(cx),cM=Z(),cS=be||cl,cA,cs;if(aq){Y()}if(cb){return""}var cI=bN();if(!y(cv)){cv=""}var cF=w.characterSet||w.charset;if(!cF||cF.toLowerCase()==="utf-8"){cF=null}cA=cM[0];cs=cM[1];cz=cM[2];cN=cM[3];if(!cH){var cR=aF/1000;if(!cI.lastVisitTs||(cC-cI.lastVisitTs)>cR){cI.visitCount++;cI.lastVisitTs=cI.currentVisitTs}if(!bb||!cA.length){for(cO in a4){if(Object.prototype.hasOwnProperty.call(a4,cO)){cA=K(cS,a4[cO]);
+if(cA.length){break}}}for(cO in aV){if(Object.prototype.hasOwnProperty.call(aV,cO)){cs=K(cS,aV[cO]);if(cs.length){break}}}}cV=c(bH);cD=cN.length?c(cN):"";if(cV.length&&!bE(cV)&&(!bb||!cD.length||bE(cD))){cN=bH}if(cN.length||cA.length){cz=cC;cM=[cA,cs,cz,cf(cN.slice(0,cw))];bV(cJ,JSON2.stringify(cM),bi,ck,at)}}cu+="&idsite="+b7+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+ct.getHours()+"&m="+ct.getMinutes()+"&s="+ct.getSeconds()+"&url="+m(cf(cS))+(bH.length?"&urlref="+m(cf(bH)):"")+((aP&&aP.length)?"&uid="+m(aP):"")+"&_id="+cI.uuid+"&_idts="+cI.createTs+"&_idvc="+cI.visitCount+"&_idn="+cI.newVisitor+(cA.length?"&_rcn="+m(cA):"")+(cs.length?"&_rck="+m(cs):"")+"&_refts="+cz+"&_viewts="+cI.lastVisitTs+(String(cI.lastEcommerceOrderTs).length?"&_ects="+cI.lastEcommerceOrderTs:"")+(String(cN).length?"&_ref="+m(cf(cN.slice(0,cw))):"")+(cF?"&cs="+m(cF):"")+"&send_image=0";for(cO in b8){if(Object.prototype.hasOwnProperty.call(b8,cO)){cu+="&"+cO+"="+b8[cO]}}var cU=[];if(cP){for(cO in cP){if(Object.prototype.hasOwnProperty.call(cP,cO)&&/^dimension\d+$/.test(cO)){var cy=cO.replace("dimension","");
+cU.push(parseInt(cy,10));cU.push(String(cy));cu+="&"+cO+"="+cP[cO];delete cP[cO]}}}if(cP&&s(cP)){cP=null}for(cO in bS){if(Object.prototype.hasOwnProperty.call(bS,cO)){var cE=(-1===cU.indexOf(cO));if(cE){cu+="&dimension"+cO+"="+bS[cO]}}}if(cP){cu+="&data="+m(JSON2.stringify(cP))}else{if(ap){cu+="&data="+m(JSON2.stringify(ap))}}function cB(cW,cX){var cY=JSON2.stringify(cW);if(cY.length>2){return"&"+cX+"="+m(cY)}return""}var cT=b4(br);var cK=b4(ad);cu+=cB(cT,"cvar");cu+=cB(cK,"e_cvar");if(ai){cu+=cB(ai,"_cvar");for(cO in cL){if(Object.prototype.hasOwnProperty.call(cL,cO)){if(ai[cO][0]===""||ai[cO][1]===""){delete ai[cO]}}}if(bd){bV(cG,JSON2.stringify(ai),aF,ck,at)}}if(a7){if(aN){cu+="&gt_ms="+aN}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cu+="&gt_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cI.lastEcommerceOrderTs=y(cv)&&String(cv).length?cv:cI.lastEcommerceOrderTs;ao(cI);ba();cu+=Q(cQ);if(bB.length){cu+="&"+bB}if(r(aw)){cu=aw(cu)}return cu}bs=function bx(){var cs=new Date();
+if(bp+b5<=cs.getTime()){var ct=aZ("ping=1",null,"ping");a5(ct,bA);return true}return false};function bY(cv,cu,cz,cw,cs,cC){var cx="idgoal=0",cy,ct=new Date(),cA=[],cB;if(String(cv).length){cx+="&ec_id="+m(cv);cy=Math.round(ct.getTime()/1000)}cx+="&revenue="+cu;if(String(cz).length){cx+="&ec_st="+cz}if(String(cw).length){cx+="&ec_tx="+cw}if(String(cs).length){cx+="&ec_sh="+cs}if(String(cC).length){cx+="&ec_dt="+cC}if(bT){for(cB in bT){if(Object.prototype.hasOwnProperty.call(bT,cB)){if(!y(bT[cB][1])){bT[cB][1]=""}if(!y(bT[cB][2])){bT[cB][2]=""}if(!y(bT[cB][3])||String(bT[cB][3]).length===0){bT[cB][3]=0}if(!y(bT[cB][4])||String(bT[cB][4]).length===0){bT[cB][4]=1}cA.push(bT[cB])}}cx+="&ec_items="+m(JSON2.stringify(cA))}cx=aZ(cx,ap,"ecommerce",cy);a5(cx,bA)}function bX(cs,cw,cv,cu,ct,cx){if(String(cs).length&&y(cw)){bY(cs,cw,cv,cu,ct,cx)}}function ci(cs){if(y(cs)){bY("",cs,"","","","")}}function bm(cu,cv){var cs=new Date(),ct=aZ("action_name="+m(V(cu||aY)),cv,"log");a5(ct,bA)}function aM(cu,ct){var cv,cs="(^| )(piwik[_-]"+ct;
+if(cu){for(cv=0;cv<cu.length;cv++){cs+="|"+cu[cv]}}cs+=")( |$)";return new RegExp(cs)}function bQ(cs){return(ac&&cs&&0===String(cs).indexOf(ac))}function b3(cw,cs,cx,ct){if(bQ(cs)){return 0}var cv=aM(bq,"download"),cu=aM(aI,"link"),cy=new RegExp("\\.("+a0.join("|")+")([?&#]|$)","i");if(cu.test(cw)){return"link"}if(ct||cv.test(cw)||cy.test(cs)){return"download"}if(cx){return 0}return"link"}function bg(ct){var cs;cs=ct.parentNode;while(cs!==null&&y(cs)){if(S.isLinkElement(ct)){break}ct=cs;cs=ct.parentNode}return ct}function bu(cw){cw=bg(cw);if(!S.hasNodeAttribute(cw,"href")){return}if(!y(cw.href)){return}var cv=S.getAttributeValueFromNode(cw,"href");if(bQ(cv)){return}var cx=cw.hostname||c(cw.href);var cy=cx.toLowerCase();var ct=cw.href.replace(cx,cy);var cu=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i");if(!cu.test(ct)){var cs=b3(cw.className,ct,bE(cy),S.hasNodeAttribute(cw,"download"));if(cs){return{type:cs,href:ct}}}}function cn(cs,ct,cu,cv){var cw=n.buildInteractionRequestParams(cs,ct,cu,cv);
+if(!cw){return}return aZ(cw,null,"contentInteraction")}function cm(cu,cv,cz,cs,ct){if(!y(cu)){return}if(bQ(cu)){return cu}var cx=n.toAbsoluteUrl(cu);var cw="redirecturl="+m(cx)+"&";cw+=cn(cv,cz,cs,(ct||cu));var cy="&";if(ac.indexOf("?")<0){cy="?"}return ac+cy+cw}function a9(cs,ct){if(!cs||!ct){return false}var cu=n.findTargetNode(cs);if(n.shouldIgnoreInteraction(cu)){return false}cu=n.findTargetNodeNoDefault(cs);if(cu&&!J(cu,ct)){return false}return true}function aX(cu,ct,cw){if(!cu){return}var cs=n.findParentContentNode(cu);if(!cs){return}if(!a9(cs,cu)){return}var cv=n.buildContentBlock(cs);if(!cv){return}if(!cv.target&&cw){cv.target=cw}return n.buildInteractionRequestParams(ct,cv.name,cv.piece,cv.target)}function aU(ct){if(!aj||!aj.length){return false}var cs,cu;for(cs=0;cs<aj.length;cs++){cu=aj[cs];if(cu&&cu.name===ct.name&&cu.piece===ct.piece&&cu.target===ct.target){return true}}return false}function ae(cv){if(!cv){return false}var cy=n.findTargetNode(cv);if(!cy||n.shouldIgnoreInteraction(cy)){return false
+}var cz=bu(cy);if(b9&&cz&&cz.type){return false}if(S.isLinkElement(cy)&&S.hasNodeAttributeWithValue(cy,"href")){var cs=String(S.getAttributeValueFromNode(cy,"href"));if(0===cs.indexOf("#")){return false}if(bQ(cs)){return true}if(!n.isUrlToCurrentDomain(cs)){return false}var cw=n.buildContentBlock(cv);if(!cw){return}var cu=cw.name;var cA=cw.piece;var cx=cw.target;if(!S.hasNodeAttributeWithValue(cy,n.CONTENT_TARGET_ATTR)||cy.wasContentTargetAttrReplaced){cy.wasContentTargetAttrReplaced=true;cx=n.toAbsoluteUrl(cs);S.setAnyAttribute(cy,n.CONTENT_TARGET_ATTR,cx)}var ct=cm(cs,"click",cu,cA,cx);n.setHrefAttribute(cy,ct);return true}return false}function ah(ct){if(!ct||!ct.length){return}var cs;for(cs=0;cs<ct.length;cs++){ae(ct[cs])}}function bt(cs){return function(ct){if(!cs){return}var cw=n.findParentContentNode(cs);var cx;if(ct){cx=ct.target||ct.srcElement}if(!cx){cx=cs}if(!a9(cw,cx)){return}ce(bA);if(S.isLinkElement(cs)&&S.hasNodeAttributeWithValue(cs,"href")&&S.hasNodeAttributeWithValue(cs,n.CONTENT_TARGET_ATTR)){var cu=S.getAttributeValueFromNode(cs,"href");
+if(!bQ(cu)&&cs.wasContentTargetAttrReplaced){S.setAnyAttribute(cs,n.CONTENT_TARGET_ATTR,"")}}var cB=bu(cs);if(bL&&cB&&cB.type){return cB.type}if(ae(cw)){return"href"}var cy=n.buildContentBlock(cw);if(!cy){return}var cv=cy.name;var cC=cy.piece;var cA=cy.target;var cz=cn("click",cv,cC,cA);a5(cz,bA);return cz}}function aL(cu){if(!cu||!cu.length){return}var cs,ct;for(cs=0;cs<cu.length;cs++){ct=n.findTargetNode(cu[cs]);if(ct&&!ct.contentInteractionTrackingSetupDone){ct.contentInteractionTrackingSetupDone=true;X(ct,"click",bt(ct))}}}function aH(cu,cv){if(!cu||!cu.length){return[]}var cs,ct;for(cs=0;cs<cu.length;cs++){if(aU(cu[cs])){cu.splice(cs,1);cs--}else{aj.push(cu[cs])}}if(!cu||!cu.length){return[]}ah(cv);aL(cv);var cw=[];for(cs=0;cs<cu.length;cs++){ct=aZ(n.buildImpressionRequestParams(cu[cs].name,cu[cs].piece,cu[cs].target),undefined,"contentImpressions");cw.push(ct)}return cw}function a2(ct){var cs=n.collectContent(ct);return aH(cs,ct)}function bP(ct){if(!ct||!ct.length){return[]}var cs;
+for(cs=0;cs<ct.length;cs++){if(!n.isNodeVisible(ct[cs])){ct.splice(cs,1);cs--}}if(!ct||!ct.length){return[]}return a2(ct)}function b1(cu,cs,ct){var cv=n.buildImpressionRequestParams(cu,cs,ct);return aZ(cv,null,"contentImpression")}function a1(cv,ct){if(!cv){return}var cs=n.findParentContentNode(cv);var cu=n.buildContentBlock(cs);if(!cu){return}if(!ct){ct="Unknown"}return cn(ct,cu.name,cu.piece,cu.target)}function bJ(ct,cv,cs,cu){return"e_c="+m(ct)+"&e_a="+m(cv)+(y(cs)?"&e_n="+m(cs):"")+(y(cu)?"&e_v="+m(cu):"")}function an(cu,cw,cs,cv,cx){if(String(cu).length===0||String(cw).length===0){return false}var ct=aZ(bJ(cu,cw,cs,cv),cx,"event");a5(ct,bA)}function aT(cs,cv,ct,cw){var cu=aZ("search="+m(cs)+(cv?"&search_cat="+m(cv):"")+(y(ct)?"&search_count="+ct:""),cw,"sitesearch");a5(cu,bA)}function by(cs,cv,cu){var ct=aZ("idgoal="+cs+(cv?"&revenue="+cv:""),cu,"goal");a5(ct,bA)}function b2(cv,cs,cz,cy,cu){var cx=cs+"="+m(cf(cv));var ct=aX(cu,"click",cv);if(ct){cx+="&"+ct}var cw=aZ(cx,cz,"link");a5(cw,(cy?0:bA),cy)
+}function ca(ct,cs){if(ct!==""){return ct+cs.charAt(0).toUpperCase()+cs.slice(1)}return cs}function aS(cx){var cw,cs,cv=["","webkit","ms","moz"],cu;if(!a6){for(cs=0;cs<cv.length;cs++){cu=cv[cs];if(Object.prototype.hasOwnProperty.call(w,ca(cu,"hidden"))){if(w[ca(cu,"visibilityState")]==="prerender"){cw=true}break}}}if(cw){X(w,cu+"visibilitychange",function ct(){w.removeEventListener(cu+"visibilitychange",ct,false);cx()});return}cx()}function aW(cs){if(w.readyState==="complete"){cs()}else{if(I.addEventListener){I.addEventListener("load",cs)}else{if(I.attachEvent){I.attachEvent("onLoad",cs)}}}}function aG(ct){var cs=false;if(w.attachEvent){cs=w.readyState==="complete"}else{cs=w.readyState!=="loading"}if(cs){ct()}else{if(w.addEventListener){w.addEventListener("DOMContentLoaded",ct)}else{if(w.attachEvent){w.attachEvent("onreadystatechange",ct)}}}}function bR(cs){var ct=bu(cs);if(ct&&ct.type){ct.href=j(ct.href);b2(ct.href,ct.type,undefined,null,cs)}}function aO(){return w.all&&!w.addEventListener
+}function aE(cs){var cu=cs.which;var ct=(typeof cs.button);if(!cu&&ct!=="undefined"){if(aO()){if(cs.button&1){cu=1}else{if(cs.button&2){cu=3}else{if(cs.button&4){cu=2}}}}else{if(cs.button===0||cs.button==="0"){cu=1}else{if(cs.button&1){cu=2}else{if(cs.button&2){cu=3}}}}}return cu}function aB(cs){switch(aE(cs)){case 1:return"left";case 2:return"middle";case 3:return"right"}}function cd(cs){return cs.target||cs.srcElement}function cq(cs){return function(cv){cv=cv||I.event;var cu=aB(cv);var cw=cd(cv);if(cv.type==="click"){var ct=false;if(cs&&cu==="middle"){ct=true}if(cw&&!ct){bR(cw)}}else{if(cv.type==="mousedown"){if(cu==="middle"&&cw){bv=cu;av=cw}else{bv=av=null}}else{if(cv.type==="mouseup"){if(cu===bv&&cw===av){bR(cw)}bv=av=null}else{if(cv.type==="contextmenu"){bR(cw)}}}}}}function bM(ct,cs){X(ct,"click",cq(cs),false);if(cs){X(ct,"mouseup",cq(cs),false);X(ct,"mousedown",cq(cs),false);X(ct,"contextmenu",cq(cs),false)}}function bl(ct){if(!bL){bL=true;var cu,cs=aM(ak,"ignore"),cv=w.links;if(cv){for(cu=0;
+cu<cv.length;cu++){if(!cs.test(cv[cu].className)){bM(cv[cu],ct)}}}}}function bI(cu,cw,cx){if(aC){return true}aC=true;var cy=false;var cv,ct;function cs(){cy=true}aW(function(){function cz(cB){setTimeout(function(){if(!aC){return}cy=false;cx.trackVisibleContentImpressions();cz(cB)},cB)}function cA(cB){setTimeout(function(){if(!aC){return}if(cy){cy=false;cx.trackVisibleContentImpressions()}cA(cB)},cB)}if(cu){cv=["scroll","resize"];for(ct=0;ct<cv.length;ct++){if(w.addEventListener){w.addEventListener(cv[ct],cs)}else{I.attachEvent("on"+cv[ct],cs)}}cA(100)}if(cw&&cw>0){cw=parseInt(cw,10);cz(cw)}})}function cg(){var ct,cu,cv={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"},cs=I.devicePixelRatio||1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(ct in cv){if(Object.prototype.hasOwnProperty.call(cv,ct)){cu=e.mimeTypes[cv[ct]];
+b8[ct]=(cu&&cu.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&y(e.javaEnabled)&&e.javaEnabled()){b8.java="1"}if(r(I.GearsFactory)){b8.gears="1"}b8.cookie=ag()}b8.res=M.width*cs+"x"+M.height*cs}cg();bw();ao();return{getVisitorId:function(){return bN().uuid},getVisitorInfo:function(){return aa()},getAttributionInfo:function(){return Z()},getAttributionCampaignName:function(){return Z()[0]},getAttributionCampaignKeyword:function(){return Z()[1]},getAttributionReferrerTimestamp:function(){return Z()[2]},getAttributionReferrerUrl:function(){return Z()[3]},setTrackerUrl:function(cs){ac=cs},getTrackerUrl:function(){return ac},getSiteId:function(){return b7},setSiteId:function(cs){co(cs)},setUserId:function(cs){if(!y(cs)||!cs.length){return}aP=cs;a8=a3(aP).substr(0,16)},getUserId:function(){return aP},setCustomData:function(cs,ct){if(L(cs)){ap=cs}else{if(!ap){ap={}}ap[cs]=ct}},getCustomData:function(){return ap},setCustomRequestProcessing:function(cs){aw=cs},appendToTrackingUrl:function(cs){bB=cs
+},getRequest:function(cs){return aZ(cs)},addPlugin:function(cs,ct){a[cs]=ct},setCustomDimension:function(cs,ct){cs=parseInt(cs,10);if(cs>0){if(!y(ct)){ct=""}if(!o(ct)){ct=String(ct)}bS[cs]=ct}},getCustomDimension:function(cs){cs=parseInt(cs,10);if(cs>0&&Object.prototype.hasOwnProperty.call(bS,cs)){return bS[cs]}},deleteCustomDimension:function(cs){cs=parseInt(cs,10);if(cs>0){delete bS[cs]}},setCustomVariable:function(ct,cs,cw,cu){var cv;if(!y(cu)){cu="visit"}if(!y(cs)){return}if(!y(cw)){cw=""}if(ct>0){cs=!o(cs)?String(cs):cs;cw=!o(cw)?String(cw):cw;cv=[cs.slice(0,cc),cw.slice(0,cc)];if(cu==="visit"||cu===2){ab();ai[ct]=cv}else{if(cu==="page"||cu===3){br[ct]=cv}else{if(cu==="event"){ad[ct]=cv}}}}},getCustomVariable:function(ct,cu){var cs;if(!y(cu)){cu="visit"}if(cu==="page"||cu===3){cs=br[ct]}else{if(cu==="event"){cs=ad[ct]}else{if(cu==="visit"||cu===2){ab();cs=ai[ct]}}}if(!y(cs)||(cs&&cs[0]==="")){return false}return cs},deleteCustomVariable:function(cs,ct){if(this.getCustomVariable(cs,ct)){this.setCustomVariable(cs,"","",ct)
+}},storeCustomVariablesInCookie:function(){bd=true},setLinkTrackingTimer:function(cs){bA=cs},setDownloadExtensions:function(cs){if(o(cs)){cs=cs.split("|")}a0=cs},addDownloadExtensions:function(ct){var cs;if(o(ct)){ct=ct.split("|")}for(cs=0;cs<ct.length;cs++){a0.push(ct[cs])}},removeDownloadExtensions:function(cu){var ct,cs=[];if(o(cu)){cu=cu.split("|")}for(ct=0;ct<a0.length;ct++){if(B(cu,a0[ct])===-1){cs.push(a0[ct])}}a0=cs},setDomains:function(cs){bC=o(cs)?[cs]:cs;bC.push(b0)},setIgnoreClasses:function(cs){ak=o(cs)?[cs]:cs},setRequestMethod:function(cs){bF=cs||bD},setRequestContentType:function(cs){aQ=cs||bk},setReferrerUrl:function(cs){bH=cs},setCustomUrl:function(cs){be=bZ(cl,cs)},setDocumentTitle:function(cs){aY=cs},setAPIUrl:function(cs){aA=cs},setDownloadClasses:function(cs){bq=o(cs)?[cs]:cs},setLinkClasses:function(cs){aI=o(cs)?[cs]:cs},setCampaignNameKey:function(cs){a4=o(cs)?[cs]:cs},setCampaignKeywordKey:function(cs){aV=o(cs)?[cs]:cs},discardHashTag:function(cs){am=cs},setCookieNamePrefix:function(cs){cj=cs;
+ai=au()},setCookieDomain:function(cs){var ct=A(cs);if(bo(ct)){at=ct;bw()}},setCookiePath:function(cs){ck=cs;bw()},setVisitorCookieTimeout:function(cs){az=cs*1000},setSessionCookieTimeout:function(cs){aF=cs*1000},setReferralCookieTimeout:function(cs){bi=cs*1000},setConversionAttributionFirstReferrer:function(cs){bb=cs},disableCookies:function(){aq=true;b8.cookie="0";if(b7){Y()}},deleteCookies:function(){Y()},setDoNotTrack:function(ct){var cs=e.doNotTrack||e.msDoNotTrack;cb=ct&&(cs==="yes"||cs==="1");if(cb){this.disableCookies()}},addListener:function(ct,cs){bM(ct,cs)},enableLinkTracking:function(cs){b9=true;if(q){bl(cs)}else{G.push(function(){bl(cs)})}},enableJSErrorTracking:function(){if(cp){return}cp=true;var cs=I.onerror;I.onerror=function(cx,cv,cu,cw,ct){aS(function(){var cy="JavaScript Errors";var cz=cv+":"+cu;if(cw){cz+=":"+cw}an(cy,cz,cx)});if(cs){return cs(cx,cv,cu,cw,ct)}return false}},disablePerformanceTracking:function(){a7=false},setGenerationTimeMs:function(cs){aN=parseInt(cs,10)
+},enableHeartBeatTimer:function(cs){cs=Math.max(cs,1);b5=(cs||15)*1000;if(bp!==null){bG()}},killFrame:function(){if(I.location!==I.top.location){I.top.location=I.location}},redirectFile:function(cs){if(I.location.protocol==="file:"){I.location=cs}},setCountPreRendered:function(cs){a6=cs},trackGoal:function(cs,cu,ct){aS(function(){by(cs,cu,ct)})},trackLink:function(ct,cs,cv,cu){aS(function(){b2(ct,cs,cv,cu)})},trackPageView:function(cs,ct){aj=[];if(C(b7)){aS(function(){O(ac,aA,b7)})}else{aS(function(){bm(cs,ct)})}},trackAllContentImpressions:function(){if(C(b7)){return}aS(function(){aG(function(){var cs=n.findContentNodes();var ct=a2(cs);ar(ct,bA)})})},trackVisibleContentImpressions:function(cs,ct){if(C(b7)){return}if(!y(cs)){cs=true}if(!y(ct)){ct=750}bI(cs,ct,this);aS(function(){aW(function(){var cu=n.findContentNodes();var cv=bP(cu);ar(cv,bA)})})},trackContentImpression:function(cu,cs,ct){if(C(b7)){return}if(!cu){return}cs=cs||"Unknown";aS(function(){var cv=b1(cu,cs,ct);a5(cv,bA)})},trackContentImpressionsWithinNode:function(cs){if(C(b7)||!cs){return
+}aS(function(){if(aC){aW(function(){var ct=n.findContentNodesWithinNode(cs);var cu=bP(ct);ar(cu,bA)})}else{aG(function(){var ct=n.findContentNodesWithinNode(cs);var cu=a2(ct);ar(cu,bA)})}})},trackContentInteraction:function(cu,cv,cs,ct){if(C(b7)){return}if(!cu||!cv){return}cs=cs||"Unknown";aS(function(){var cw=cn(cu,cv,cs,ct);a5(cw,bA)})},trackContentInteractionNode:function(ct,cs){if(C(b7)||!ct){return}aS(function(){var cu=a1(ct,cs);a5(cu,bA)})},logAllContentBlocksOnPage:function(){var ct=n.findContentNodes();var cs=n.collectContent(ct);if(console!==undefined&&console&&console.log){console.log(cs)}},trackEvent:function(ct,cv,cs,cu,cw){aS(function(){an(ct,cv,cs,cu,cw)})},trackSiteSearch:function(cs,cu,ct,cv){aS(function(){aT(cs,cu,ct,cv)})},setEcommerceView:function(cv,cs,cu,ct){if(!y(cu)||!cu.length){cu=""}else{if(cu instanceof Array){cu=JSON2.stringify(cu)}}br[5]=["_pkc",cu];if(y(ct)&&String(ct).length){br[2]=["_pkp",ct]}if((!y(cv)||!cv.length)&&(!y(cs)||!cs.length)){return}if(y(cv)&&cv.length){br[3]=["_pks",cv]
+}if(!y(cs)||!cs.length){cs=""}br[4]=["_pkn",cs]},addEcommerceItem:function(cw,cs,cu,ct,cv){if(cw.length){bT[cw]=[cw,cs,cu,ct,cv]}},trackEcommerceOrder:function(cs,cw,cv,cu,ct,cx){bX(cs,cw,cv,cu,ct,cx)},trackEcommerceCartUpdate:function(cs){ci(cs)}}}function x(){return{push:T}}function b(ad,ac){var ae={};var aa,ab;for(aa=0;aa<ac.length;aa++){var Y=ac[aa];ae[Y]=1;for(ab=0;ab<ad.length;ab++){if(ad[ab]&&ad[ab][0]){var Z=ad[ab][0];if(Y===Z){T(ad[ab]);delete ad[ab];if(ae[Z]>1){if(console!==undefined&&console&&console.error){console.error("The method "+Z+' 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')}}ae[Z]++}}}}return ad}X(I,"beforeunload",U,false);p();Date.prototype.getTimeAlias=Date.prototype.getTime;N=new F();var t=["disableCookies","setTrackerUrl","setAPIUrl","setCookiePath","setCookieDomain","setUserId","setSiteId","enableLinkTracking"];
+_paq=b(_paq,t);for(v=0;v<_paq.length;v++){if(_paq[v]){T(_paq[v])}}_paq=new x();d={addPlugin:function(Y,Z){a[Y]=Z},getTracker:function(Y,Z){if(!y(Z)){Z=this.getAsyncTracker().getSiteId()}if(!y(Y)){Y=this.getAsyncTracker().getTrackerUrl()}return new F(Y,Z)},getAsyncTracker:function(){return N}};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=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=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/API/API.php b/plugins/API/API.php
index e5d9fbcd2a..2f609b5e8b 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -22,7 +22,6 @@ use Piwik\Metrics;
use Piwik\Period;
use Piwik\Period\Range;
use Piwik\Piwik;
-use Piwik\Plugin\Dimension\VisitDimension;
use Piwik\Plugins\API\DataTable\MergeDataTables;
use Piwik\Plugins\CoreAdminHome\CustomLogo;
use Piwik\Translation\Translator;
@@ -88,8 +87,9 @@ class API extends \Piwik\Plugin\API
* are not visible in the UI and not present in the API meta data. These columns are
* translated here.
* @return array
+ * @deprecated since Piwik 2.15.1
*/
- public static function getDefaultMetricTranslations()
+ public function getDefaultMetricTranslations()
{
return Metrics::getDefaultMetricTranslations();
}
@@ -102,6 +102,8 @@ class API extends \Piwik\Plugin\API
*/
public function getAvailableMeasurableTypes()
{
+ Piwik::checkUserHasSomeViewAccess();
+
$typeManager = new TypeManager();
$types = $typeManager->getAllTypes();
@@ -120,13 +122,19 @@ class API extends \Piwik\Plugin\API
public function getSegmentsMetadata($idSites = array(), $_hideImplementationData = true)
{
- $isAuthenticatedWithViewAccess = Piwik::isUserHasViewAccess($idSites) && !Piwik::isUserIsAnonymous();
+ if (empty($idSites)) {
+ Piwik::checkUserHasSomeViewAccess();
+ } else {
+ Piwik::checkUserHasViewAccess($idSites);
+ }
+
+ $isNotAnonymous = !Piwik::isUserIsAnonymous();
$segments = array();
foreach (Dimension::getAllDimensions() as $dimension) {
foreach ($dimension->getSegments() as $segment) {
if ($segment->isRequiresAtLeastViewAccess()) {
- $segment->setPermission($isAuthenticatedWithViewAccess);
+ $segment->setPermission($isNotAnonymous);
}
$segments[] = $segment->toArray();
@@ -250,6 +258,7 @@ class API extends \Piwik\Plugin\API
*
* @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
* @return string
+ * @deprecated since Piwik 2.15.1
*/
public function getLogoUrl($pathOnly = false)
{
@@ -262,6 +271,7 @@ class API extends \Piwik\Plugin\API
*
* @param bool $pathOnly If true, returns path relative to doc root. Otherwise, returns a URL.
* @return string
+ * @deprecated since Piwik 2.15.1
*/
public function getHeaderLogoUrl($pathOnly = false)
{
@@ -300,6 +310,8 @@ class API extends \Piwik\Plugin\API
public function getMetadata($idSite, $apiModule, $apiAction, $apiParameters = array(), $language = false,
$period = false, $date = false, $hideMetricsDoc = false, $showSubtableReports = false)
{
+ Piwik::checkUserHasViewAccess($idSite);
+
if ($language) {
/** @var Translator $translator */
$translator = StaticContainer::get('Piwik\Translation\Translator');
@@ -325,6 +337,8 @@ class API extends \Piwik\Plugin\API
public function getReportMetadata($idSites = '', $period = false, $date = false, $hideMetricsDoc = false,
$showSubtableReports = false)
{
+ Piwik::checkUserHasViewAccess($idSites);
+
$reporter = new ProcessedReport();
$metadata = $reporter->getReportMetadata($idSites, $period, $date, $hideMetricsDoc, $showSubtableReports);
return $metadata;
@@ -333,11 +347,13 @@ class API extends \Piwik\Plugin\API
public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
$apiParameters = false, $idGoal = false, $language = false,
$showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false,
- $format_metrics = null)
+ $format_metrics = null, $idDimension = false)
{
+ Piwik::checkUserHasViewAccess($idSite);
+
$reporter = new ProcessedReport();
$processed = $reporter->getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment,
- $apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics);
+ $apiParameters, $idGoal, $language, $showTimer, $hideMetricsDoc, $idSubtable, $showRawMetrics, $format_metrics, $idDimension);
return $processed;
}
@@ -347,6 +363,8 @@ class API extends \Piwik\Plugin\API
*/
public function get($idSite, $period, $date, $segment = false, $columns = false)
{
+ Piwik::checkUserHasViewAccess($idSite);
+
$columns = Piwik::getArrayFromApiParameter($columns);
// build columns map for faster checks later on
@@ -427,13 +445,16 @@ class API extends \Piwik\Plugin\API
* @param bool|int $idGoal
* @param bool|string $legendAppendMetric
* @param bool|string $labelUseAbsoluteUrl
+ * @param bool|int $idDimension
* @return array
*/
- public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
+ public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false)
{
+ Piwik::checkUserHasViewAccess($idSite);
+
$rowEvolution = new RowEvolution();
return $rowEvolution->getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label, $segment, $column,
- $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl);
+ $language, $idGoal, $legendAppendMetric, $labelUseAbsoluteUrl, $idDimension);
}
/**
diff --git a/plugins/API/ProcessedReport.php b/plugins/API/ProcessedReport.php
index db87cd19c7..9c6893057d 100644
--- a/plugins/API/ProcessedReport.php
+++ b/plugins/API/ProcessedReport.php
@@ -380,17 +380,25 @@ class ProcessedReport
public function getProcessedReport($idSite, $period, $date, $apiModule, $apiAction, $segment = false,
$apiParameters = false, $idGoal = false, $language = false,
$showTimer = true, $hideMetricsDoc = false, $idSubtable = false, $showRawMetrics = false,
- $formatMetrics = null)
+ $formatMetrics = null, $idDimension = false)
{
$timer = new Timer();
if (empty($apiParameters)) {
$apiParameters = array();
}
+
if (!empty($idGoal)
&& empty($apiParameters['idGoal'])
) {
$apiParameters['idGoal'] = $idGoal;
}
+
+ if (!empty($idDimension)
+ && empty($apiParameters['idDimension'])
+ ) {
+ $apiParameters['idDimension'] = (int) $idDimension;
+ }
+
// Is this report found in the Metadata available reports?
$reportMetadata = $this->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
$period, $date, $hideMetricsDoc, $showSubtableReports = true);
diff --git a/plugins/API/RowEvolution.php b/plugins/API/RowEvolution.php
index c708608089..711a48316c 100644
--- a/plugins/API/RowEvolution.php
+++ b/plugins/API/RowEvolution.php
@@ -37,7 +37,7 @@ class RowEvolution
'getPageUrl'
);
- public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true)
+ public function getRowEvolution($idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $column = false, $language = false, $idGoal = false, $legendAppendMetric = true, $labelUseAbsoluteUrl = true, $idDimension = false)
{
// validation of requested $period & $date
if ($period == 'range') {
@@ -52,9 +52,9 @@ class RowEvolution
$label = DataTablePostProcessor::unsanitizeLabelParameter($label);
$labels = Piwik::getArrayFromApiParameter($label);
- $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal);
+ $metadata = $this->getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal, $idDimension);
- $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal);
+ $dataTable = $this->loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $labels, $segment, $idGoal, $idDimension);
if (empty($labels)) {
$labels = $this->getLabelsFromDataTable($dataTable, $labels);
@@ -249,7 +249,7 @@ class RowEvolution
* @throws Exception
* @return DataTable\Map|DataTable
*/
- private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false)
+ private function loadRowEvolutionDataFromAPI($metadata, $idSite, $period, $date, $apiModule, $apiAction, $label = false, $segment = false, $idGoal = false, $idDimension = false)
{
if (!is_array($label)) {
$label = array($label);
@@ -266,6 +266,7 @@ class RowEvolution
'serialize' => '0',
'segment' => $segment,
'idGoal' => $idGoal,
+ 'idDimension' => $idDimension,
// data for row evolution should NOT be limited
'filter_limit' => -1,
@@ -310,12 +311,15 @@ class RowEvolution
* @throws Exception
* @return array
*/
- private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false)
+ private function getRowEvolutionMetaData($idSite, $period, $date, $apiModule, $apiAction, $language, $idGoal = false, $idDimension = false)
{
$apiParameters = array();
if (!empty($idGoal) && $idGoal > 0) {
$apiParameters = array('idGoal' => $idGoal);
}
+ if (!empty($idDimension) && $idDimension > 0) {
+ $apiParameters = array('idDimension' => (int) $idDimension);
+ }
$reportMetadata = API::getInstance()->getMetadata($idSite, $apiModule, $apiAction, $apiParameters, $language,
$period, $date, $hideMetricsDoc = false, $showSubtableReports = true);
diff --git a/plugins/Actions/Archiver.php b/plugins/Actions/Archiver.php
index 5110130b84..7610e973ef 100644
--- a/plugins/Actions/Archiver.php
+++ b/plugins/Actions/Archiver.php
@@ -149,10 +149,7 @@ class Archiver extends \Piwik\Plugin\Archiver
$select = "log_action.name,
log_action.type,
log_action.idaction,
- log_action.url_prefix,
- count(distinct log_link_visit_action.idvisit) as `" . PiwikMetrics::INDEX_NB_VISITS . "`,
- count(distinct log_link_visit_action.idvisitor) as `" . PiwikMetrics::INDEX_NB_UNIQ_VISITORS . "`,
- count(*) as `" . PiwikMetrics::INDEX_PAGE_NB_HITS . "`";
+ log_action.url_prefix";
$select = $this->addMetricsToSelect($select, $metricsConfig);
@@ -178,8 +175,7 @@ class Archiver extends \Piwik\Plugin\Archiver
$rankingQuery = new RankingQuery($rankingQueryLimit);
$rankingQuery->setOthersLabel(DataTable::LABEL_SUMMARY_ROW);
$rankingQuery->addLabelColumn(array('idaction', 'name'));
- $rankingQuery->addColumn(array('url_prefix', PiwikMetrics::INDEX_NB_UNIQ_VISITORS));
- $rankingQuery->addColumn(array(PiwikMetrics::INDEX_PAGE_NB_HITS, PiwikMetrics::INDEX_NB_VISITS), 'sum');
+ $rankingQuery->addColumn('url_prefix');
if ($this->isSiteSearchEnabled()) {
$rankingQuery->addColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT, 'min');
diff --git a/plugins/Actions/Metrics.php b/plugins/Actions/Metrics.php
index 83b1c7370a..006fd6043d 100644
--- a/plugins/Actions/Metrics.php
+++ b/plugins/Actions/Metrics.php
@@ -50,6 +50,18 @@ class Metrics
public static function getActionMetrics()
{
$metricsConfig = array(
+ PiwikMetrics::INDEX_NB_VISITS => array(
+ 'aggregation' => 'sum',
+ 'query' => "count(distinct log_link_visit_action.idvisit)"
+ ),
+ PiwikMetrics::INDEX_NB_UNIQ_VISITORS => array(
+ 'aggregation' => false,
+ 'query' => "count(distinct log_link_visit_action.idvisitor)"
+ ),
+ PiwikMetrics::INDEX_PAGE_NB_HITS => array(
+ 'aggregation' => 'sum',
+ 'query' => "count(*)"
+ ),
PiwikMetrics::INDEX_PAGE_SUM_TIME_GENERATION => array(
'aggregation' => 'sum',
'query' => "sum(
diff --git a/plugins/CoreHome/Columns/UserId.php b/plugins/CoreHome/Columns/UserId.php
index bbe7e44c8f..a611ca8def 100644
--- a/plugins/CoreHome/Columns/UserId.php
+++ b/plugins/CoreHome/Columns/UserId.php
@@ -11,6 +11,7 @@ namespace Piwik\Plugins\CoreHome\Columns;
use Piwik\Cache;
use Piwik\DataTable;
use Piwik\DataTable\Map;
+use Piwik\Metrics;
use Piwik\Period\Range;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
@@ -124,7 +125,14 @@ class UserId extends VisitDimension
return false;
}
- $numUsers = $result->getColumn('nb_users');
+ $firstRow = $result->getFirstRow();
+ if ($firstRow instanceof DataTable\Row && $firstRow->hasColumn(Metrics::INDEX_NB_USERS)) {
+ $metric = Metrics::INDEX_NB_USERS;
+ } else {
+ $metric = 'nb_users';
+ }
+
+ $numUsers = $result->getColumn($metric);
$numUsers = array_sum($numUsers);
return !empty($numUsers);
diff --git a/plugins/CoreHome/javascripts/broadcast.js b/plugins/CoreHome/javascripts/broadcast.js
index 706a8e59d5..c0cc7d81c4 100644
--- a/plugins/CoreHome/javascripts/broadcast.js
+++ b/plugins/CoreHome/javascripts/broadcast.js
@@ -214,6 +214,10 @@ var broadcast = {
currentHashStr = broadcast.updateParamValue('idDashboard=', currentHashStr);
}
+ if (module != 'CustomDimensions') {
+ currentHashStr = broadcast.updateParamValue('idDimension=', currentHashStr);
+ }
+
if (disableHistory) {
var newLocation = window.location.href.split('#')[0] + '#?' + currentHashStr;
// window.location.replace changes the current url without pushing it on the browser's history stack
@@ -420,7 +424,8 @@ var broadcast = {
broadcast.getParamValue('action', urlAjax),
{
idGoal: broadcast.getParamValue('idGoal', urlAjax),
- idDashboard: broadcast.getParamValue('idDashboard', urlAjax)
+ idDashboard: broadcast.getParamValue('idDashboard', urlAjax),
+ idDimension: broadcast.getParamValue('idDimension', urlAjax)
}
);
});
diff --git a/plugins/CoreHome/javascripts/dataTable.js b/plugins/CoreHome/javascripts/dataTable.js
index 0561185de8..0232d91ec6 100644
--- a/plugins/CoreHome/javascripts/dataTable.js
+++ b/plugins/CoreHome/javascripts/dataTable.js
@@ -1069,6 +1069,7 @@ $.extend(DataTable.prototype, UIControl.prototype, {
var segment = self.param.segment;
var label = self.param.label;
var idGoal = self.param.idGoal;
+ var idDimension = self.param.idDimension;
var param_date = self.param.date;
var date = $(this).attr('date');
if (typeof date != 'undefined') {
@@ -1137,6 +1138,11 @@ $.extend(DataTable.prototype, UIControl.prototype, {
&& idGoal != '-1') {
str += '&idGoal=' + idGoal;
}
+ // Export Dimension specific reports
+ if (typeof idDimension != 'undefined'
+ && idDimension != '-1') {
+ str += '&idDimension=' + idDimension;
+ }
if (label) {
label = label.split(',');
diff --git a/plugins/CoreHome/javascripts/dataTable_rowactions.js b/plugins/CoreHome/javascripts/dataTable_rowactions.js
index 004e27e3b6..ab07537e2f 100644
--- a/plugins/CoreHome/javascripts/dataTable_rowactions.js
+++ b/plugins/CoreHome/javascripts/dataTable_rowactions.js
@@ -386,6 +386,11 @@ DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMeth
requestParams.action = 'getRowEvolutionPopover';
requestParams.colors = JSON.stringify(piwik.getSparklineColors());
+ var idDimension = broadcast.getValueFromHash('idDimension');
+ if (idDimension) {
+ requestParams.idDimension = parseInt(idDimension, 10);
+ }
+
$.extend(requestParams, extraParams);
var ajaxRequest = new ajaxHelper();
diff --git a/plugins/CoreHome/javascripts/notification.js b/plugins/CoreHome/javascripts/notification.js
index e6c3f017f2..527c845d87 100644
--- a/plugins/CoreHome/javascripts/notification.js
+++ b/plugins/CoreHome/javascripts/notification.js
@@ -54,6 +54,16 @@
this.$node = placeNotification(template, options);
};
+ /**
+ * Removes a previously shown notification having the given notification id.
+ *
+ *
+ * @param {string} notificationId The id of a notification that was previously registered.
+ */
+ Notification.prototype.remove = function (notificationId) {
+ $('[piwik-notification][notification-id=' + notificationId + ']').remove();
+ };
+
Notification.prototype.scrollToNotification = function () {
if (this.$node) {
piwikHelper.lazyScrollTo(this.$node, 250);
diff --git a/plugins/CoreHome/templates/_menu.twig b/plugins/CoreHome/templates/_menu.twig
index 4c4643723d..baaa8ab8ca 100644
--- a/plugins/CoreHome/templates/_menu.twig
+++ b/plugins/CoreHome/templates/_menu.twig
@@ -1,6 +1,6 @@
-{% macro submenuItem(name, url, anchorlink) %}
+{% macro submenuItem(name, url, anchorlink, tooltip) %}
{% if name|slice(0,1) != '_' %}
- <li role="menuitem" title="{{ name|translate|e('html_attr') }}">
+ <li role="menuitem" title="{{ tooltip|default(name)|translate|e('html_attr') }}">
<a class="item" href="{% if anchorlink %}#{% else %}index.php?{% endif %}{{ url|urlRewriteWithParameters|slice(1) }}">
{{ name|translate }}
</a>
@@ -14,7 +14,7 @@
{% for item in group.getItems %}
<a class="item menuItem"
href='{% if anchorlink %}#?{% else %}index.php?{% endif %}{{ item.url|urlRewriteWithParameters|slice(1) }}'
- {% if item.tooltip %}title="{{ item.tooltip|e('html_attr') }}"{% endif %}>
+ title="{% if item.tooltip %}{{ item.tooltip|e('html_attr') }}{% else %}{{ item.name|e('html_attr') }}{% endif %}">
{{ item.name|translate }}
</a>
{% endfor %}
@@ -55,7 +55,7 @@
{% if urlParameters._url is defined and urlParameters._url is not iterable %}
{{ _self.groupedItem(name,urlParameters._url, anchorlink) }}
{% elseif name|slice(0,1) != '_' %}
- {{ _self.submenuItem(name,urlParameters._url, anchorlink) }}
+ {{ _self.submenuItem(name,urlParameters._url, anchorlink, urlParameters._tooltip) }}
{% endif %}
{% endfor %}
</ul>
diff --git a/plugins/CoreHome/tests/Integration/Column/UserIdTest.php b/plugins/CoreHome/tests/Integration/Column/UserIdTest.php
index 33c268afae..70715a4dc2 100644
--- a/plugins/CoreHome/tests/Integration/Column/UserIdTest.php
+++ b/plugins/CoreHome/tests/Integration/Column/UserIdTest.php
@@ -12,6 +12,7 @@ use Piwik\Access;
use Piwik\Cache;
use Piwik\DataAccess\ArchiveTableCreator;
use Piwik\Db;
+use Piwik\Metrics;
use Piwik\Plugin\Manager;
use Piwik\Plugins\CoreHome\Columns\UserId;
use Piwik\Tests\Framework\Fixture;
@@ -164,6 +165,17 @@ class UserIdTest extends IntegrationTestCase
$this->assertDataTableHasUsers($this->getDataTableWithUsers());
}
+ public function test_hasDataTableUsers_shouldBeAbleToDetectIfNbUsersMetricIdIsused()
+ {
+ $table = $this->getDataTableWithZeroUsers();
+ $table->renameColumn('nb_users', Metrics::INDEX_NB_USERS);
+ $this->assertNotDataTableHasUsers($table);
+
+ $table = $this->getDataTableWithUsers();
+ $table->renameColumn('nb_users', Metrics::INDEX_NB_USERS);
+ $this->assertDataTableHasUsers($this->getDataTableWithUsers());
+ }
+
private function getDataTableWithoutUsersColumn()
{
$tableWithoutUsers = new DataTable();
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php
index 1a41a07df3..5acf9b50be 100644
--- a/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/AllColumns.php
@@ -10,6 +10,7 @@
namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\DataTable;
+use Piwik\Metrics;
use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
use Piwik\View;
@@ -40,11 +41,13 @@ class AllColumns extends HtmlTable
$this->dataTable->filter(function (DataTable $dataTable) use ($properties) {
$columnsToDisplay = array('label', 'nb_visits');
- if (in_array('nb_uniq_visitors', $dataTable->getColumns())) {
+ $columns = $dataTable->getColumns();
+
+ if (in_array('nb_uniq_visitors', $columns)) {
$columnsToDisplay[] = 'nb_uniq_visitors';
}
- if (in_array('nb_users', $dataTable->getColumns())) {
+ if (in_array('nb_users', $columns)) {
$columnsToDisplay[] = 'nb_users';
}
diff --git a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
index 3b62e9197e..2fa3cee126 100644
--- a/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
+++ b/plugins/CoreVisualizations/javascripts/jqplotEvolutionGraph.js
@@ -94,6 +94,7 @@
var module = broadcast.getValueFromHash('module');
var action = broadcast.getValueFromHash('action');
var idGoal = broadcast.getValueFromHash('idGoal');
+ var idDimension = broadcast.getValueFromHash('idDimension');
var idSite = broadcast.getValueFromUrl('idSite', url);
var period = broadcast.getValueFromUrl('period', url);
var date = broadcast.getValueFromUrl('date', url);
@@ -109,6 +110,10 @@
url += '&idGoal=' + idGoal;
}
+ if (idDimension) {
+ url += '&idDimension=' + idDimension;
+ }
+
if (period) {
url += '&period=' + period;
}
diff --git a/plugins/CustomDimensions b/plugins/CustomDimensions
new file mode 160000
+Subproject 32dc6a3ed363af0ed96cd7d1379f210333cbb03
diff --git a/plugins/CustomVariables/CustomVariables.php b/plugins/CustomVariables/CustomVariables.php
index 6f4796b7aa..9059482c33 100644
--- a/plugins/CustomVariables/CustomVariables.php
+++ b/plugins/CustomVariables/CustomVariables.php
@@ -162,6 +162,8 @@ class CustomVariables extends \Piwik\Plugin
$translationKeys[] = 'CustomVariables_CreatingCustomVariableTakesTime';
$translationKeys[] = 'CustomVariables_SlotsReportIsGeneratedOverTime';
$translationKeys[] = 'General_Loading';
+ $translationKeys[] = 'General_TrackingScopeVisit';
+ $translationKeys[] = 'General_TrackingScopePage';
}
public function getStylesheetFiles(&$stylesheets)
diff --git a/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php b/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php
index de1688d3ed..2b068ba03e 100644
--- a/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php
+++ b/plugins/CustomVariables/Tracker/CustomVariablesRequestProcessor.php
@@ -10,6 +10,7 @@ namespace Piwik\Plugins\CustomVariables\Tracker;
use Piwik\Common;
use Piwik\Plugins\CustomVariables\Model;
+use Piwik\Tracker\Action;
use Piwik\Tracker\Request;
use Piwik\Tracker\RequestProcessor;
use Piwik\Tracker\Visit\VisitProperties;
@@ -63,4 +64,25 @@ class CustomVariablesRequestProcessor extends RequestProcessor
$valuesToUpdate = array_merge($valuesToUpdate, $visitCustomVariables);
}
}
+
+ public function afterRequestProcessed(VisitProperties $visitProperties, Request $request)
+ {
+ $action = $request->getMetadata('Actions', 'action');
+
+ if (empty($action) || !($action instanceof Action)) {
+ return;
+ }
+
+ $customVariables = $action->getCustomVariables();
+
+ if (!empty($customVariables)) {
+ Common::printDebug("Page level Custom Variables: ");
+ Common::printDebug($customVariables);
+
+ foreach ($customVariables as $field => $value) {
+ $action->setCustomField($field, $value);
+ }
+ }
+
+ }
}
diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js
index 4cbc8dc3e8..f8098eb114 100644
--- a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js
+++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.controller.js
@@ -14,6 +14,9 @@
this.model = manageCustomVarsModel;
this.siteName = piwik.siteName;
- this.scopes = ['visit', 'page'];
+ this.scopes = [
+ {value: 'visit', name: _pk_translate('General_TrackingScopeVisit')},
+ {value: 'page', name: _pk_translate('General_TrackingScopePage')}
+ ];
}
})(); \ No newline at end of file
diff --git a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html
index ccba542a87..6ab9ea6ba6 100644
--- a/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html
+++ b/plugins/CustomVariables/angularjs/manage-custom-vars/manage-custom-vars.directive.html
@@ -10,7 +10,7 @@
</div>
<div ng-repeat="scope in manageCustomVars.scopes">
- <h2 class="secondary">{{ 'CustomVariables_ScopeX'|translate:(scope|ucfirst) }}</h2>
+ <h2 class="secondary">{{ 'CustomVariables_ScopeX'|translate:(scope.name) }}</h2>
<table class="dataTable entityTable">
<thead>
<tr>
@@ -22,7 +22,7 @@
<tr>
<td colspan="3" ng-show="manageCustomVars.model.isLoading">{{ 'General_Loading'|translate }}</td>
</tr>
- <tr ng-repeat="customVariables in manageCustomVars.model.customVariables|filter:{scope: scope}">
+ <tr ng-repeat="customVariables in manageCustomVars.model.customVariables|filter:{scope: scope.value}">
<td class="index">{{ customVariables.index }}</td>
<td>
<span ng-show="(customVariables.usages|length) === 0"
diff --git a/plugins/Goals/Controller.php b/plugins/Goals/Controller.php
index 15f7b40aab..7a66581c69 100644
--- a/plugins/Goals/Controller.php
+++ b/plugins/Goals/Controller.php
@@ -459,8 +459,14 @@ class Controller extends \Piwik\Plugin\Controller
}
$customParams['viewDataTable'] = $report['viewDataTable'];
+ if (!empty($report['parameters'])) {
+ $params = array_merge($customParams, $report['parameters']);
+ } else {
+ $params = $customParams;
+ }
+
$goalReportsByDimension->addReport(
- $categoryText, $report['name'], $report['module'] . '.' . $report['action'], $customParams);
+ $categoryText, $report['name'], $report['module'] . '.' . $report['action'], $params);
}
}
}
diff --git a/plugins/Goals/Reports/Base.php b/plugins/Goals/Reports/Base.php
index fd732035d3..3db8c4446b 100644
--- a/plugins/Goals/Reports/Base.php
+++ b/plugins/Goals/Reports/Base.php
@@ -33,7 +33,7 @@ abstract class Base extends \Piwik\Plugin\Report
$this->parameters = array('idGoal' => $goal['idgoal']);
$this->order = $this->orderGoal + $goal['idgoal'] * 3;
- $availableReports[] = $this->buildReportMetadata($availableReports, $infos);
+ $availableReports[] = $this->buildReportMetadata();
}
$this->init();
diff --git a/plugins/ImageGraph/API.php b/plugins/ImageGraph/API.php
index 4e7b466b21..ffdd223d46 100644
--- a/plugins/ImageGraph/API.php
+++ b/plugins/ImageGraph/API.php
@@ -122,7 +122,8 @@ class API extends \Piwik\Plugin\API
$gridColor = API::DEFAULT_GRID_COLOR,
$idSubtable = false,
$legendAppendMetric = true,
- $segment = false
+ $segment = false,
+ $idDimension = false
)
{
Piwik::checkUserHasViewAccess($idSite);
@@ -151,6 +152,9 @@ class API extends \Piwik\Plugin\API
if (!empty($idGoal)) {
$apiParameters = array('idGoal' => $idGoal);
}
+ if (!empty($idDimension)) {
+ $apiParameters = array('idDimension' => $idDimension);
+ }
// Fetch the metadata for given api-action
$parameters = array(
'idSite' => $idSite,
@@ -305,6 +309,7 @@ class API extends \Piwik\Plugin\API
'column' => $plottedMetric,
'language' => $languageLoaded,
'idGoal' => $idGoal,
+ 'idDimension' => $idDimension,
'legendAppendMetric' => $legendAppendMetric,
'labelUseAbsoluteUrl' => false
);
@@ -361,6 +366,7 @@ class API extends \Piwik\Plugin\API
'segment' => $segment,
'apiParameters' => false,
'idGoal' => $idGoal,
+ 'idDimension' => $idDimension,
'language' => $languageLoaded,
'showTimer' => true,
'hideMetricsDoc' => false,
@@ -506,7 +512,10 @@ class API extends \Piwik\Plugin\API
if ($idGoal != '') {
$idGoal = '_' . $idGoal;
}
- $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png';
+ if ($idDimension != '') {
+ $idDimension = '__' . $idDimension;
+ }
+ $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . $idDimension . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png';
$fileName = str_replace(array(' ', '/'), '_', $fileName);
if (!Filesystem::isValidFilename($fileName)) {
diff --git a/plugins/Insights/Visualizations/Insight.php b/plugins/Insights/Visualizations/Insight.php
index 7fe304e5be..91d2c90b63 100644
--- a/plugins/Insights/Visualizations/Insight.php
+++ b/plugins/Insights/Visualizations/Insight.php
@@ -40,6 +40,10 @@ class Insight extends Visualization
$report = $this->requestConfig->apiMethodToRequestDataTable;
$report = str_replace('.', '_', $report);
+ if (!empty($this->requestConfig->request_parameters_to_modify['reportUniqueId'])) {
+ $report = $this->requestConfig->request_parameters_to_modify['reportUniqueId'];
+ }
+
$this->requestConfig->apiMethodToRequestDataTable = 'Insights.getInsights';
$this->requestConfig->request_parameters_to_modify = array(
diff --git a/plugins/Live/Live.php b/plugins/Live/Live.php
index 93e8d67881..1a71aa7b83 100644
--- a/plugins/Live/Live.php
+++ b/plugins/Live/Live.php
@@ -54,5 +54,7 @@ class Live extends \Piwik\Plugin
$translationKeys[] = "Live_RowActionTooltipDefault";
$translationKeys[] = "Live_RowActionTooltipWithDimension";
$translationKeys[] = "Live_SegmentedVisitorLogTitle";
+ $translationKeys[] = "General_Segment";
+ $translationKeys[] = "General_And";
}
} \ No newline at end of file
diff --git a/plugins/Live/javascripts/rowaction.js b/plugins/Live/javascripts/rowaction.js
index 8baba18a2d..5c54e961b5 100644
--- a/plugins/Live/javascripts/rowaction.js
+++ b/plugins/Live/javascripts/rowaction.js
@@ -150,6 +150,12 @@
var segmentName = getDimensionFromApiMethod(apiMethod);
var segmentValue = findTitleOfRowHavingRawSegmentValue(apiMethod, segment);
+ if (!segmentName || (segment && segment.indexOf(';') > 0)) {
+ segmentName = _pk_translate('General_Segment');
+ var segmentParts = segment.split(';');
+ segmentValue = segmentParts.join(' ' + _pk_translate('General_And') + ' ');
+ }
+
segmentName = piwikHelper.escape(segmentName);
segmentName = piwikHelper.htmlEntities(segmentName);
segmentValue = piwikHelper.escape(segmentValue);
diff --git a/plugins/Monolog/tests/System/TrackerLoggingTest.php b/plugins/Monolog/tests/System/TrackerLoggingTest.php
index 70c933d13f..8716229116 100644
--- a/plugins/Monolog/tests/System/TrackerLoggingTest.php
+++ b/plugins/Monolog/tests/System/TrackerLoggingTest.php
@@ -83,10 +83,8 @@ DEBUG: 'apiv' => '1',", $response);
private function setTrackerConfig($trackerConfig)
{
$testingEnvironment = self::$fixture->getTestEnvironment();
- $configOverride = $testingEnvironment->configOverride;
- $configOverride['Tracker'] = $trackerConfig;
- $configOverride['log']['log_writers'] = array('screen');
- $testingEnvironment->configOverride = $configOverride;
+ $testingEnvironment->overrideConfig('Tracker', $trackerConfig);
+ $testingEnvironment->overrideConfig('log', 'log_writers', array('screen'));
$testingEnvironment->save();
}
diff --git a/plugins/Overlay/Controller.php b/plugins/Overlay/Controller.php
index 795d7b991d..2ed50641c5 100644
--- a/plugins/Overlay/Controller.php
+++ b/plugins/Overlay/Controller.php
@@ -16,14 +16,26 @@ use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugin\Report;
use Piwik\Plugins\Actions\ArchivingHelper;
+use Piwik\Plugins\SegmentEditor\SegmentFormatter;
use Piwik\Plugins\SitesManager\API as APISitesManager;
use Piwik\ProxyHttp;
+use Piwik\Segment;
use Piwik\Tracker\Action;
use Piwik\Tracker\PageUrl;
use Piwik\View;
class Controller extends \Piwik\Plugin\Controller
{
+ /**
+ * @var SegmentFormatter
+ */
+ private $segmentFormatter;
+
+ public function __construct(SegmentFormatter $segmentFormatter)
+ {
+ $this->segmentFormatter = $segmentFormatter;
+ parent::__construct();
+ }
/** The index of the plugin */
public function index()
@@ -38,6 +50,7 @@ class Controller extends \Piwik\Plugin\Controller
$view = new View($template);
$this->setGeneralVariablesView($view);
+ $view->segment = Request::getRawSegmentFromRequest();
$view->ssl = ProxyHttp::isHttps();
@@ -52,8 +65,9 @@ class Controller extends \Piwik\Plugin\Controller
$period = Common::getRequestVar('period');
$date = Common::getRequestVar('date');
$currentUrl = Common::getRequestVar('currentUrl');
+ $segment = Request::getRawSegmentFromRequest();
$currentUrl = Common::unsanitizeInputValue($currentUrl);
- $segment = '';
+ $segmentSidebar = '';
$normalizedCurrentUrl = PageUrl::excludeQueryParametersFromUrl($currentUrl, $idSite);
$normalizedCurrentUrl = Common::unsanitizeInputValue($normalizedCurrentUrl);
@@ -63,16 +77,22 @@ class Controller extends \Piwik\Plugin\Controller
$path = ArchivingHelper::getActionExplodedNames($normalizedCurrentUrl, Action::TYPE_PAGE_URL);
$path = array_map('urlencode', $path);
$label = implode('>', $path);
- $request = new Request(
- 'method=Actions.getPageUrls'
- . '&idSite=' . urlencode($idSite)
- . '&date=' . urlencode($date)
- . '&period=' . urlencode($period)
- . '&label=' . urlencode($label)
- . '&format=original'
- . '&format_metrics=0'
+
+ $params = array(
+ 'idSite' => $idSite,
+ 'date' => $date,
+ 'period' => $period,
+ 'label' => $label,
+ 'format' => 'original',
+ 'format_metrics' => 0,
+ 'serialize' => '0'
);
- $dataTable = $request->process();
+
+ if (!empty($segment)) {
+ $params['segment'] = $segment;
+ }
+
+ $dataTable = Request::processRequest('Actions.getPageUrls', $params);
$formatter = new Metrics\Formatter\Html();
@@ -84,7 +104,10 @@ class Controller extends \Piwik\Plugin\Controller
$showMetrics = array('nb_hits', 'nb_visits', 'nb_users', 'nb_uniq_visitors',
'bounce_rate', 'exit_rate', 'avg_time_on_page');
- $segment = $row->getMetadata('segment');
+ $segmentSidebar = $row->getMetadata('segment');
+ if (!empty($segmentSidebar) && !empty($segment)) {
+ $segmentSidebar = $segment . ';' . $segmentSidebar;
+ }
foreach ($showMetrics as $metric) {
$value = $row->getColumn($metric);
@@ -127,7 +150,8 @@ class Controller extends \Piwik\Plugin\Controller
$view->idSite = $idSite;
$view->period = $period;
$view->date = $date;
- $view->segment = $segment;
+ $view->segment = $segmentSidebar;
+ $view->segmentDescription = $this->segmentFormatter->getHumanReadable($segment, $idSite);
$this->outputCORSHeaders();
return $view->render();
@@ -142,64 +166,20 @@ class Controller extends \Piwik\Plugin\Controller
$idSite = Common::getRequestVar('idSite', 0, 'int');
Piwik::checkUserHasViewAccess($idSite);
+ $view = new View('@Overlay/startOverlaySession');
+
$sitesManager = APISitesManager::getInstance();
$site = $sitesManager->getSiteFromId($idSite);
$urls = $sitesManager->getSiteUrlsFromId($idSite);
+ $view->isHttps = ProxyHttp::isHttps();
+ $view->knownUrls = json_encode($urls);
+ $view->mainUrl = $site['main_url'];
+
$this->outputCORSHeaders();
Common::sendHeader('Content-Type: text/html; charset=UTF-8');
- return '
- <html><head><title></title></head><body>
- <script type="text/javascript">
- function handleProtocol(url) {
- if (' . (ProxyHttp::isHttps() ? 'true' : 'false') . ') {
- return url.replace(/http:\/\//i, "https://");
- } else {
- return url.replace(/https:\/\//i, "http://");
- }
- }
-
- function removeUrlPrefix(url) {
- return url.replace(/http(s)?:\/\/(www\.)?/i, "");
- }
-
- if (window.location.hash) {
- var match = false;
-
- var urlToRedirect = window.location.hash.substr(1);
- var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect);
-
- var knownUrls = ' . json_encode($urls) . ';
- for (var i = 0; i < knownUrls.length; i++) {
- var testUrl = removeUrlPrefix(knownUrls[i]);
- if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) {
- match = true;
- if (navigator.appName == "Microsoft Internet Explorer") {
- // internet explorer loses the referrer if we use window.location.href=X
- var referLink = document.createElement("a");
- referLink.href = handleProtocol(urlToRedirect);
- document.body.appendChild(referLink);
- referLink.click();
- } else {
- window.location.href = handleProtocol(urlToRedirect);
- }
- break;
- }
- }
-
- if (!match) {
- var idSite = window.location.href.match(/idSite=([0-9]+)/i)[1];
- window.location.href = "index.php?module=Overlay&action=showErrorWrongDomain"
- + "&idSite=" + idSite
- + "&url=" + encodeURIComponent(urlToRedirect);
- }
- }
- else {
- window.location.href = handleProtocol("' . $site['main_url'] . '");
- };
- </script>
- </body></html>
- ';
+
+ return $view->render();
}
/**
diff --git a/plugins/Overlay/client/client.js b/plugins/Overlay/client/client.js
index 04b1f88f13..f37b9a0cd3 100644
--- a/plugins/Overlay/client/client.js
+++ b/plugins/Overlay/client/client.js
@@ -10,7 +10,7 @@ var Piwik_Overlay_Client = (function () {
var idSite;
/** The current period and date */
- var period, date;
+ var period, date, segment;
/** Reference to the status bar DOM element */
var statusBar;
@@ -131,11 +131,12 @@ var Piwik_Overlay_Client = (function () {
return {
/** Initialize in-site analytics */
- initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate) {
+ initialize: function (pPiwikRoot, pIdSite, pPeriod, pDate, pSegment) {
piwikRoot = pPiwikRoot;
idSite = pIdSite;
period = pPeriod;
date = pDate;
+ segment = pSegment;
var load = this.loadScript;
var loading = this.loadingNotification;
@@ -193,6 +194,10 @@ var Piwik_Overlay_Client = (function () {
var url = piwikRoot + 'index.php?module=API&method=Overlay.' + method
+ '&idSite=' + idSite + '&period=' + period + '&date=' + date + '&format=JSON&filter_limit=-1';
+ if (segment) {
+ url += '&segment=' + segment;
+ }
+
if (additionalParams) {
url += '&' + additionalParams;
}
diff --git a/plugins/Overlay/javascripts/Overlay_Helper.js b/plugins/Overlay/javascripts/Overlay_Helper.js
index e0681e7b45..42ff388d81 100644
--- a/plugins/Overlay/javascripts/Overlay_Helper.js
+++ b/plugins/Overlay/javascripts/Overlay_Helper.js
@@ -20,11 +20,17 @@ var Overlay_Helper = {
},
/** Get the url to launch overlay */
- getOverlayLink: function (idSite, period, date, link) {
+ getOverlayLink: function (idSite, period, date, segment, link) {
var url = 'index.php?module=Overlay&period=' + encodeURIComponent(period) + '&date=' + encodeURIComponent(date) + '&idSite=' + encodeURIComponent(idSite);
+
+ if (segment) {
+ url += '&segment=' + encodeURIComponent(segment);
+ }
+
if (link) {
url += '#?l=' + Overlay_Helper.encodeFrameUrl(link);
}
+
return url;
}
diff --git a/plugins/Overlay/javascripts/Piwik_Overlay.js b/plugins/Overlay/javascripts/Piwik_Overlay.js
index 4c78080806..d04f8e5fbe 100644
--- a/plugins/Overlay/javascripts/Piwik_Overlay.js
+++ b/plugins/Overlay/javascripts/Piwik_Overlay.js
@@ -10,7 +10,7 @@ var Piwik_Overlay = (function () {
var $body, $iframe, $sidebar, $main, $location, $loading, $errorNotLoading;
var $rowEvolutionLink, $transitionsLink, $fullScreenLink, $visitorLogLink;
- var idSite, period, date;
+ var idSite, period, date, segment;
var iframeSrcBase;
var iframeDomain = '';
@@ -28,13 +28,19 @@ var Piwik_Overlay = (function () {
iframeCurrentPage = currentUrl;
iframeDomain = currentUrl.match(/http(s)?:\/\/(www\.)?([^\/]*)/i)[3];
- globalAjaxQueue.abort();
- var ajaxRequest = new ajaxHelper();
- ajaxRequest.addParams({
+ var params = {
module: 'Overlay',
action: 'renderSidebar',
currentUrl: currentUrl
- }, 'get');
+ };
+
+ if (segment) {
+ params.segment = segment;
+ }
+
+ globalAjaxQueue.abort();
+ var ajaxRequest = new ajaxHelper();
+ ajaxRequest.addParams(params, 'get');
ajaxRequest.setCallback(
function (response) {
hideLoading();
@@ -111,6 +117,16 @@ var Piwik_Overlay = (function () {
$fullScreenLink.show();
}
+ function getOverlaySegment(url) {
+ var location = broadcast.getParamValue('segment', url);
+
+ // angular will encode the value again since it is added as the fragment path, not the fragment query parameter,
+ // so we have to decode it again after getParamValue
+ location = decodeURIComponent(location);
+
+ return location;
+ }
+
function getOverlayLocationFromHash(urlHash) {
var location = broadcast.getParamValue('l', urlHash);
@@ -143,11 +159,12 @@ var Piwik_Overlay = (function () {
return {
/** This method is called when Overlay loads */
- init: function (iframeSrc, pIdSite, pPeriod, pDate) {
+ init: function (iframeSrc, pIdSite, pPeriod, pDate, pSegment) {
iframeSrcBase = iframeSrc;
idSite = pIdSite;
period = pPeriod;
date = pDate;
+ segment = pSegment;
$body = $('body');
$iframe = $('#overlayIframe');
@@ -201,7 +218,7 @@ var Piwik_Overlay = (function () {
if (parts.length == 2) {
period = parts[0];
date = parts[1];
- window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, iframeCurrentPage);
+ window.location.href = Overlay_Helper.getOverlayLink(idSite, period, date, segment, iframeCurrentPage);
}
});
diff --git a/plugins/Overlay/javascripts/rowaction.js b/plugins/Overlay/javascripts/rowaction.js
index c84e60a33b..8f11c736d0 100644
--- a/plugins/Overlay/javascripts/rowaction.js
+++ b/plugins/Overlay/javascripts/rowaction.js
@@ -19,12 +19,34 @@ DataTable_RowActions_Overlay.prototype.onClick = function (actionA, tr, e) {
if (!actionA.data('overlay-manipulated')) {
actionA.data('overlay-manipulated', 1);
- var link = tr.find('> td:first > a').attr('href');
- link = $('<textarea>').html(link).val(); // remove html entities
+ var segment, link;
+
+ if (DataTable_RowActions_Transitions.isActionCustomDimensionReport(this.dataTable.param)) {
+
+ link = this.getLabelFromTr(tr);
+ if (link && link.substr(0, 1) === '@') {
+ link = link.substr(1);
+ }
+
+ link = 'http://' + unescape(link);
+
+ var subtable = tr.closest('table');
+ if (subtable.is('.subDataTable')) {
+ var prev = subtable.closest('tr').prev();
+ segment = prev.attr('data-segment-filter');
+ }
+ } else {
+
+ link = tr.find('> td:first > a').attr('href');
+ link = $('<textarea>').html(link).val(); // remove html entities
+ }
+
+
+ var href = Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', segment, link);
actionA.attr({
target: '_blank',
- href: Overlay_Helper.getOverlayLink(this.dataTable.param.idSite, 'month', 'today', link)
+ href: href
});
}
@@ -54,6 +76,11 @@ DataTable_RowActions_Registry.register({
if (!window.DataTable_RowActions_Transitions) {
return false;
}
+
+ if (DataTable_RowActions_Transitions.isActionCustomDimensionReport(dataTableParams)) {
+ return true;
+ }
+
return DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action);
},
diff --git a/plugins/Overlay/stylesheets/overlay.css b/plugins/Overlay/stylesheets/overlay.css
index 4e30660775..a13dd67a46 100644
--- a/plugins/Overlay/stylesheets/overlay.css
+++ b/plugins/Overlay/stylesheets/overlay.css
@@ -35,12 +35,20 @@ a#overlayTitle .icon-help {
margin: 20px 10px;
}
-#overlayLocation {
+#overlayLocation, .overlaySegment {
width: 200px;
- margin: 0 0 30px 10px;
font-size: 12px;
}
+#overlayLocation {
+ margin: 0 0 10px 10px;
+}
+
+.overlaySegment {
+ margin: 0 0 30px 0;
+ word-break: break-all;
+}
+
#overlayLoading {
background: url(../../Morpheus/images/loading-blue.gif) no-repeat center 10px;
width: 190px;
diff --git a/plugins/Overlay/templates/index.twig b/plugins/Overlay/templates/index.twig
index 10a0b40d76..effe5fbade 100644
--- a/plugins/Overlay/templates/index.twig
+++ b/plugins/Overlay/templates/index.twig
@@ -61,8 +61,8 @@
<script type="text/javascript">
broadcast._isInit = true;
$(function () {
- var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ rawDate }}';
- Piwik_Overlay.init(iframeSrc, '{{ idSite }}', '{{ period }}', '{{ rawDate }}');
+ var iframeSrc = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ rawDate }}&segment={{ segment }}';
+ Piwik_Overlay.init(iframeSrc, '{{ idSite }}', '{{ period }}', '{{ rawDate }}', '{{ segment }}');
window.Piwik_Overlay_Translations = {
domain: "{{ 'Overlay_Domain'|translate }}"
diff --git a/plugins/Overlay/templates/index_noframe.twig b/plugins/Overlay/templates/index_noframe.twig
index 0478715edb..f3c9e61579 100644
--- a/plugins/Overlay/templates/index_noframe.twig
+++ b/plugins/Overlay/templates/index_noframe.twig
@@ -6,7 +6,7 @@
<div id="overlayNoFrame">
<script type="text/javascript">
- var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ date }}';
+ var newLocation = 'index.php?module=Overlay&action=startOverlaySession&idSite={{ idSite }}&period={{ period }}&date={{ date }}&segment={{ segment }}';
var locationParts = window.location.href.split('#');
if (locationParts.length > 1) {
diff --git a/plugins/Overlay/templates/renderSidebar.twig b/plugins/Overlay/templates/renderSidebar.twig
index 09d8cf0d40..36ff9c8ba1 100644
--- a/plugins/Overlay/templates/renderSidebar.twig
+++ b/plugins/Overlay/templates/renderSidebar.twig
@@ -8,6 +8,11 @@
</span>
</div>
+ <div class="overlaySegment">
+ <strong>{{ 'General_Segment'|translate }}:</strong>
+ <span>{{ segmentDescription }}</span>
+ </div>
+
{% if data|length > 0 %}
<h2 class="overlayMainMetrics">{{ 'General_MainMetrics'|translate }}</h2>
<ul class="overlayMetrics">
diff --git a/plugins/Overlay/templates/startOverlaySession.twig b/plugins/Overlay/templates/startOverlaySession.twig
new file mode 100644
index 0000000000..b1db8ce5b2
--- /dev/null
+++ b/plugins/Overlay/templates/startOverlaySession.twig
@@ -0,0 +1,50 @@
+<html><head><title></title></head><body>
+<script type="text/javascript">
+ function handleProtocol(url) {
+ if ({% if isHttps %}true{% else %}false{% endif %}) {
+ return url.replace(/http:\/\//i, "https://");
+ } else {
+ return url.replace(/https:\/\//i, "http://");
+ }
+ }
+
+ function removeUrlPrefix(url) {
+ return url.replace(/http(s)?:\/\/(www\.)?/i, "");
+ }
+
+ if (window.location.hash) {
+ var match = false;
+
+ var urlToRedirect = window.location.hash.substr(1);
+ var urlToRedirectWithoutPrefix = removeUrlPrefix(urlToRedirect);
+
+ var knownUrls = {{ knownUrls|raw }};
+ for (var i = 0; i < knownUrls.length; i++) {
+ var testUrl = removeUrlPrefix(knownUrls[i]);
+ if (urlToRedirectWithoutPrefix.substr(0, testUrl.length) == testUrl) {
+ match = true;
+ if (navigator.appName == "Microsoft Internet Explorer") {
+ // internet explorer loses the referrer if we use window.location.href=X
+ var referLink = document.createElement("a");
+ referLink.href = handleProtocol(urlToRedirect);
+ document.body.appendChild(referLink);
+ referLink.click();
+ } else {
+ window.location.href = handleProtocol(urlToRedirect);
+ }
+ break;
+ }
+ }
+
+ if (!match) {
+ var idSite = window.location.href.match(/idSite=([0-9]+)/i)[1];
+ window.location.href = "index.php?module=Overlay&action=showErrorWrongDomain"
+ + "&idSite=" + idSite
+ + "&url=" + encodeURIComponent(urlToRedirect);
+ }
+ }
+ else {
+ window.location.href = handleProtocol("{{ mainUrl|e('js') }}");
+ };
+</script>
+</body></html> \ No newline at end of file
diff --git a/plugins/SegmentEditor/SegmentEditor.php b/plugins/SegmentEditor/SegmentEditor.php
index 9107935e73..a46be068bc 100644
--- a/plugins/SegmentEditor/SegmentEditor.php
+++ b/plugins/SegmentEditor/SegmentEditor.php
@@ -28,6 +28,7 @@ class SegmentEditor extends \Piwik\Plugin
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'Template.nextToCalendar' => 'getSegmentEditorHtml',
+ 'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
);
}
@@ -85,4 +86,9 @@ class SegmentEditor extends \Piwik\Plugin
{
return Config::getInstance()->General['allow_adding_segments_for_all_websites'] == 1;
}
+
+ public function getClientSideTranslationKeys(&$translationKeys)
+ {
+ $translationKeys[] = 'SegmentEditor_CustomSegment';
+ }
}
diff --git a/plugins/SegmentEditor/SegmentFormatter.php b/plugins/SegmentEditor/SegmentFormatter.php
new file mode 100644
index 0000000000..54dcc20c99
--- /dev/null
+++ b/plugins/SegmentEditor/SegmentFormatter.php
@@ -0,0 +1,142 @@
+<?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\SegmentEditor;
+
+use Exception;
+use Piwik\Config;
+use Piwik\Db;
+use Piwik\Piwik;
+use Piwik\Segment;
+use Piwik\Segment\SegmentExpression;
+
+/**
+ */
+class SegmentFormatter
+{
+ /**
+ * @var SegmentList
+ */
+ private $segmentList;
+
+ private $matchesMetric = array(
+ SegmentExpression::MATCH_EQUAL => 'General_OperationEquals',
+ SegmentExpression::MATCH_NOT_EQUAL => 'General_OperationNotEquals',
+ SegmentExpression::MATCH_LESS_OR_EQUAL => 'General_OperationAtMost',
+ SegmentExpression::MATCH_GREATER_OR_EQUAL => 'General_OperationAtLeast',
+ SegmentExpression::MATCH_LESS => 'General_OperationLessThan',
+ SegmentExpression::MATCH_GREATER => 'General_OperationGreaterThan',
+ );
+
+ private $matchesDimension = array(
+ SegmentExpression::MATCH_EQUAL => 'General_OperationIs',
+ SegmentExpression::MATCH_NOT_EQUAL => 'General_OperationIsNot',
+ SegmentExpression::MATCH_CONTAINS => 'General_OperationContains',
+ SegmentExpression::MATCH_DOES_NOT_CONTAIN => 'General_OperationDoesNotContain',
+ SegmentExpression::MATCH_STARTS_WITH => 'General_OperationStartsWith',
+ SegmentExpression::MATCH_ENDS_WITH => 'General_OperationEndsWith'
+ );
+
+ private $operators = array(
+ SegmentExpression::BOOL_OPERATOR_AND => 'General_And',
+ SegmentExpression::BOOL_OPERATOR_OR => 'General_Or',
+ SegmentExpression::BOOL_OPERATOR_END => '',
+ );
+
+ public function __construct(SegmentList $segmentList)
+ {
+ $this->segmentList = $segmentList;
+ }
+
+ public function getHumanReadable($segmentString, $idSite)
+ {
+ if (empty($segmentString)) {
+ return Piwik::translate('SegmentEditor_DefaultAllVisits');
+ }
+
+ $segment = new SegmentExpression($segmentString);
+ $expressions = $segment->parseSubExpressions();
+
+ $readable = '';
+ foreach ($expressions as $expression) {
+ $operator = $expression[SegmentExpression::INDEX_BOOL_OPERATOR];
+ $operand = $expression[SegmentExpression::INDEX_OPERAND];
+ $name = $operand[SegmentExpression::INDEX_OPERAND_NAME];
+
+ $segment = $this->segmentList->findSegment($name, $idSite);
+
+ if (empty($segment)) {
+ throw new Exception(sprintf("The segment '%s' does not exist.", $name));
+ }
+
+ $readable .= $segment['name'] . ' ';
+ $readable .= $this->getTranslationForComparison($operand, $segment['type']) . ' ';
+ $readable .= $this->getFormattedValue($operand);
+ $readable .= $this->getTranslationForBoolOperator($operator) . ' ';
+ }
+
+ $readable = trim($readable);
+
+ return $readable;
+ }
+
+ private function getTranslationForComparison($operand, $segmentType)
+ {
+ $operator = $operand[SegmentExpression::INDEX_OPERAND_OPERATOR];
+
+ $translation = $operator;
+
+ if ($operator === SegmentExpression::MATCH_IS_NULL_OR_EMPTY) {
+ return Piwik::translate('SegmentEditor_SegmentOperatorIsNullOrEmpty');
+ }
+
+ if ($operator === SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY) {
+ return Piwik::translate('SegmentEditor_SegmentOperatorIsNotNullNorEmpty');
+ }
+
+ if ($segmentType === 'dimension' && !empty($this->matchesDimension[$operator])) {
+ $translation = Piwik::translate($this->matchesDimension[$operator]);
+ }
+ if ($segmentType === 'metric' && !empty($this->matchesMetric[$operator])) {
+ $translation = Piwik::translate($this->matchesMetric[$operator]);
+ }
+
+ return strtolower($translation);
+ }
+
+ private function getFormattedValue($operand)
+ {
+ $operator = $operand[SegmentExpression::INDEX_OPERAND_OPERATOR];
+
+ if ($operator === SegmentExpression::MATCH_IS_NULL_OR_EMPTY
+ || $operator === SegmentExpression::MATCH_IS_NOT_NULL_NOR_EMPTY) {
+ return '';
+ }
+
+ $value = $operand[SegmentExpression::INDEX_OPERAND_VALUE];
+
+ if (empty($value)) {
+ $value = '';
+ }
+
+ return '"' . $value . '" ';
+ }
+
+ private function getTranslationForBoolOperator($operator)
+ {
+ $translation = '';
+
+ if (!empty($this->operators[$operator])) {
+ $translation = Piwik::translate($this->operators[$operator]);
+ } elseif (!empty($operator)) {
+ $translation = $operator;
+ }
+
+ return $translation;
+ }
+}
diff --git a/plugins/SegmentEditor/SegmentList.php b/plugins/SegmentEditor/SegmentList.php
new file mode 100644
index 0000000000..e7094d4793
--- /dev/null
+++ b/plugins/SegmentEditor/SegmentList.php
@@ -0,0 +1,32 @@
+<?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\SegmentEditor;
+
+use Piwik\API\Request;
+use Piwik\Config;
+use Piwik\Db;
+
+/**
+ */
+class SegmentList
+{
+ public function findSegment($segmentName, $idSite)
+ {
+ $segments = Request::processRequest('API.getSegmentsMetadata', array(
+ 'idSites' => array($idSite),
+ ));
+
+ foreach ($segments as $segment) {
+ if ($segment['segment'] == $segmentName && !empty($segmentName)) {
+ return $segment;
+ }
+ }
+ }
+
+}
diff --git a/plugins/SegmentEditor/SegmentSelectorControl.php b/plugins/SegmentEditor/SegmentSelectorControl.php
index 9dc2659660..b01018dbcf 100644
--- a/plugins/SegmentEditor/SegmentSelectorControl.php
+++ b/plugins/SegmentEditor/SegmentSelectorControl.php
@@ -8,8 +8,10 @@
*/
namespace Piwik\Plugins\SegmentEditor;
+use Piwik\API\Request;
use Piwik\Common;
use Piwik\Config;
+use Piwik\Container\StaticContainer;
use Piwik\Piwik;
use Piwik\Plugins\API\API as APIMetadata;
use Piwik\View\UIControl;
@@ -37,6 +39,9 @@ class SegmentSelectorControl extends UIControl
$this->selectedSegment = Common::getRequestVar('segment', false, 'string');
+ $formatter = StaticContainer::get('Piwik\Plugins\SegmentEditor\SegmentFormatter');
+ $this->segmentDescription = $formatter->getHumanReadable(Request::getRawSegmentFromRequest(), $this->idSite);
+
$this->isAddingSegmentsForAllWebsitesEnabled = SegmentEditor::isAddingSegmentsForAllWebsitesEnabled();
$segments = APIMetadata::getInstance()->getSegmentsMetadata($this->idSite);
diff --git a/plugins/SegmentEditor/javascripts/Segmentation.js b/plugins/SegmentEditor/javascripts/Segmentation.js
index ec1e8f23b8..2ffd7dac57 100644
--- a/plugins/SegmentEditor/javascripts/Segmentation.js
+++ b/plugins/SegmentEditor/javascripts/Segmentation.js
@@ -85,7 +85,7 @@ Segmentation = (function($) {
var name = $(foundItems).first().find("span.segname").text();
title.text(name);
} else {
- title.text("Custom Segment");
+ title.text(_pk_translate('SegmentEditor_CustomSegment'));
}
segmentationTitle.html(title);
}
diff --git a/plugins/SegmentEditor/lang/en.json b/plugins/SegmentEditor/lang/en.json
index 933ac73884..a9ba4382e0 100644
--- a/plugins/SegmentEditor/lang/en.json
+++ b/plugins/SegmentEditor/lang/en.json
@@ -6,7 +6,7 @@
"AreYouSureDeleteSegment": "Are you sure you want to delete this segment?",
"AutoArchivePreProcessed": "segmented reports are pre-processed (faster, requires cron)",
"AutoArchiveRealTime": "segmented reports are processed in real time",
- "ChooseASegment": "Choose a segment",
+ "ChooseASegment": "Choose a segment, currently selected segment: %s",
"DataAvailableAtLaterDate": "Your segmented analytics reports will be available later. We apologize for the inconvenience.",
"DefaultAllVisits": "All visits",
"DragDropCondition": "Drag & Drop condition",
@@ -27,6 +27,9 @@
"YouMustBeLoggedInToCreateSegments": "You must be logged in to create and edit custom visitor segments.",
"YouDontHaveAccessToCreateSegments": "You don't have the required access level to create and edit segments.",
"AddingSegmentForAllWebsitesDisabled": "Adding segments for all websites has been disabled.",
- "SegmentXIsAUnionOf": "%s is a union of these segments:"
+ "SegmentXIsAUnionOf": "%s is a union of these segments:",
+ "CustomSegment": "Custom Segment",
+ "SegmentOperatorIsNullOrEmpty": "is null or empty",
+ "SegmentOperatorIsNotNullNorEmpty": "is not null nor empty"
}
} \ No newline at end of file
diff --git a/plugins/SegmentEditor/templates/_segmentSelector.twig b/plugins/SegmentEditor/templates/_segmentSelector.twig
index f2d53b60c7..c14921a046 100644
--- a/plugins/SegmentEditor/templates/_segmentSelector.twig
+++ b/plugins/SegmentEditor/templates/_segmentSelector.twig
@@ -1,5 +1,5 @@
<div class="SegmentEditor" style="display:none;">
- <div class="segmentationContainer listHtml" title="{{ 'SegmentEditor_ChooseASegment'|translate|e('html_attr') }}">
+ <div class="segmentationContainer listHtml" title="{{ 'SegmentEditor_ChooseASegment'|translate(segmentDescription)|e('html_attr') }}">
<a class="title"><span class="icon icon-segment"></span><span class="segmentationTitle"></span></a>
<div class="dropdown dropdown-body">
<div class="segmentFilterContainer">
diff --git a/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php b/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php
new file mode 100644
index 0000000000..bc0f206784
--- /dev/null
+++ b/plugins/SegmentEditor/tests/Integration/SegmentFormatterTest.php
@@ -0,0 +1,110 @@
+<?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\SegmentEditor\tests\Integration;
+
+use Piwik\Plugins\SegmentEditor\SegmentFormatter;
+use Piwik\Plugins\SegmentEditor\SegmentList;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\Mock\FakeAccess;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Translate;
+use Exception;
+
+/**
+ * @group SegmentFormatterTest
+ * @group SegmentFormatter
+ * @group SegmentEditor
+ * @group Plugins
+ */
+class SegmentFormatterTest extends IntegrationTestCase
+{
+ /**
+ * @var SegmentFormatter
+ */
+ private $formatter;
+
+ private $idSite;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->idSite = Fixture::createWebsite('2012-01-01 00:00:00');
+ $this->formatter = new SegmentFormatter(new SegmentList());
+
+ Translate::loadAllTranslations();
+ }
+
+ public function tearDown()
+ {
+ Translate::reset();
+ }
+
+ public function test_getHumanReadable_noSegmentGiven_ShouldReturnDefaultSegment()
+ {
+ $readable = $this->formatter->getHumanReadable($segment = '', $this->idSite);
+ $this->assertSame('All visits', $readable);
+ }
+
+ public function test_getHumanReadable_ShouldTranslateAMetric()
+ {
+ $readable = $this->formatter->getHumanReadable($segment = 'visitCount>5', $this->idSite);
+ $this->assertSame('Number of visits greater than "5"', $readable);
+
+ $readable = $this->formatter->getHumanReadable($segment = 'visitCount==5', $this->idSite);
+ $this->assertSame('Number of visits equals "5"', $readable);
+ }
+
+ public function test_getHumanReadable_ShouldTranslateADimension()
+ {
+ $readable = $this->formatter->getHumanReadable($segment = 'resolution=@1024', $this->idSite);
+ $this->assertSame('Resolution contains "1024"', $readable);
+
+ $readable = $this->formatter->getHumanReadable($segment = 'resolution==1024x768', $this->idSite);
+ $this->assertSame('Resolution is "1024x768"', $readable);
+ }
+
+ public function test_getHumanReadable_ShouldCombineMultipleSegmentDefinitionsWithBooleanOperator()
+ {
+ $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=1.0;browserEngine=$Trident', $this->idSite);
+ $this->assertSame('Browser version is not "1.0" and Browser engine ends with "Trident"', $readable);
+
+ $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=1.0,browserEngine=$Trident', $this->idSite);
+ $this->assertSame('Browser version is not "1.0" or Browser engine ends with "Trident"', $readable);
+ }
+
+ public function test_getHumanReadable_ShouldHandleAMissingValue()
+ {
+ $readable = $this->formatter->getHumanReadable($segment = 'browserVersion==', $this->idSite);
+ $this->assertSame('Browser version is null or empty', $readable);
+
+ $readable = $this->formatter->getHumanReadable($segment = 'browserVersion!=', $this->idSite);
+ $this->assertSame('Browser version is not null nor empty', $readable);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The segment 'noTexisTinG' does not exist
+ */
+ public function test_getHumanReadable_ShouldThrowAnException_IfTheGivenSegmentNameDoesNotExist()
+ {
+ $this->formatter->getHumanReadable($segment = 'noTexisTinG==1.0', $this->idSite);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage The segment 'pageUrl=!1.0' is not valid.
+ */
+ public function test_getHumanReadable_ShouldThrowAnException_IfSegmentCannotBeParsedBecauseOfInvalidFormat()
+ {
+ $invalidOperator = '=!';
+ $this->formatter->getHumanReadable($segment = 'pageUrl' . $invalidOperator . '1.0', $this->idSite);
+ }
+
+}
diff --git a/plugins/SegmentEditor/tests/Integration/SegmentListTest.php b/plugins/SegmentEditor/tests/Integration/SegmentListTest.php
new file mode 100644
index 0000000000..3a90bb95ee
--- /dev/null
+++ b/plugins/SegmentEditor/tests/Integration/SegmentListTest.php
@@ -0,0 +1,74 @@
+<?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\SegmentEditor\tests\Integration;
+
+use Piwik\Plugins\SegmentEditor\SegmentList;
+use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\Mock\FakeAccess;
+use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Exception;
+
+/**
+ * @group SegmentListTest
+ * @group SegmentList
+ * @group SegmentEditor
+ * @group Plugins
+ */
+class SegmentListTest extends IntegrationTestCase
+{
+ /**
+ * @var SegmentList
+ */
+ private $list;
+
+ private $idSite;
+
+ public function setUp()
+ {
+ parent::setUp();
+
+ $this->idSite = Fixture::createWebsite('2012-01-01 00:00:00');
+ $this->list = new SegmentList();
+ }
+
+ public function test_findSegment_shouldFindSegmentByName_IfNameExists()
+ {
+ $segmentName = 'pageUrl';
+
+ $segment = $this->list->findSegment($segmentName, $this->idSite);
+ $this->assertInternalType('array', $segment);
+ $this->assertSame($segmentName, $segment['segment']);
+ }
+
+ public function test_findSegment_shouldNotFindSegmentByName_IfNameDoesNotExist()
+ {
+ $segment = $this->list->findSegment('aNyNotExisTinGSegmEnt', $this->idSite);
+ $this->assertNull($segment);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage checkUserHasViewAccess
+ */
+ public function test_findSegment_ShouldThrowException_IfNotEnoughPermission()
+ {
+ FakeAccess::clearAccess($superUser = false, array(1));
+
+ $segment = $this->list->findSegment('pageUrl', 999);
+ $this->assertNull($segment);
+ }
+
+ public function provideContainerConfig()
+ {
+ return array(
+ 'Piwik\Access' => new FakeAccess()
+ );
+ }
+
+}
diff --git a/plugins/TestRunner/Commands/TestsRunUI.php b/plugins/TestRunner/Commands/TestsRunUI.php
index 4ca7285c1d..3a9a06c080 100644
--- a/plugins/TestRunner/Commands/TestsRunUI.php
+++ b/plugins/TestRunner/Commands/TestsRunUI.php
@@ -26,7 +26,7 @@ class TestsRunUI extends ConsoleCommand
\nRun one spec:
\n./console tests:run-ui UIIntegrationTest
");
- $this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by comma, for instance UIIntegrationTest ', array());
+ $this->addArgument('specs', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Run only a specific test spec. Separate multiple specs by a space, for instance UIIntegrationTest ', array());
$this->addOption("persist-fixture-data", null, InputOption::VALUE_NONE, "Persist test data in a database and do not execute tear down.");
$this->addOption('keep-symlinks', null, InputOption::VALUE_NONE, "Keep recursive directory symlinks so test pages can be viewed in a browser.");
$this->addOption('print-logs', null, InputOption::VALUE_NONE, "Print webpage logs even if tests succeed.");
diff --git a/plugins/TestRunner/Commands/TestsSetupFixture.php b/plugins/TestRunner/Commands/TestsSetupFixture.php
index baea8f70ff..565a81122a 100644
--- a/plugins/TestRunner/Commands/TestsSetupFixture.php
+++ b/plugins/TestRunner/Commands/TestsSetupFixture.php
@@ -192,10 +192,7 @@ class TestsSetupFixture extends ConsoleCommand
);
foreach ($optionsToOverride as $configOption => $value) {
if ($value) {
- $configOverride = $testingEnvironment->configOverride;
- $configOverride['database_tests'][$configOption] = $configOverride['database'][$configOption] = $value;
- $testingEnvironment->configOverride = $configOverride;
-
+ $testingEnvironment->overrideConfig('database_tests', $configOption, $value);
Config::getInstance()->database[$configOption] = $value;
}
}
diff --git a/plugins/Transitions/javascripts/transitions.js b/plugins/Transitions/javascripts/transitions.js
index cb98e3d045..a11f6860c6 100644
--- a/plugins/Transitions/javascripts/transitions.js
+++ b/plugins/Transitions/javascripts/transitions.js
@@ -30,6 +30,10 @@ DataTable_RowActions_Transitions.isPageTitleReport = function (module, action) {
return module == 'Actions' && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch');
};
+DataTable_RowActions_Transitions.isActionCustomDimensionReport = function (params) {
+ return params.module == 'CustomDimensions' && params.action == 'getCustomDimension' && params.scopeOfDimension && params.scopeOfDimension === 'action';
+};
+
DataTable_RowActions_Transitions.prototype.trigger = function (tr, e, subTableLabel) {
var link = tr.find('> td:first > a').attr('href');
link = $('<textarea>').html(link).val(); // remove html entities
@@ -40,6 +44,27 @@ DataTable_RowActions_Transitions.prototype.trigger = function (tr, e, subTableLa
this.openPopover('url:' + link);
} else if (DataTable_RowActions_Transitions.isPageTitleReport(module, action)) {
DataTable_RowAction.prototype.trigger.apply(this, [tr, e, subTableLabel]);
+ } else if (DataTable_RowActions_Transitions.isActionCustomDimensionReport(this.dataTable.param)) {
+
+ var label = this.getLabelFromTr(tr);
+ if (label && label.substr(0, 1) === '@') {
+ label = label.substr(1);
+ }
+
+ var subtable = tr.closest('table');
+ if (subtable.is('.subDataTable')) {
+ var prev = subtable.closest('tr').prev();
+ var segment = prev.attr('data-segment-filter');
+ if (segment) {
+ label = unescape(label);
+ if (this.transitions === null) {
+ this.transitions = new Piwik_Transitions('url', label, this, segment);
+ } else {
+ this.transitions.reset('url', label, segment);
+ }
+ this.transitions.showPopover();
+ }
+ }
} else {
alert('Transitions can\'t be used on this report.');
}
@@ -95,7 +120,8 @@ DataTable_RowActions_Registry.register({
isAvailableOnReport: function (dataTableParams) {
return (
DataTable_RowActions_Transitions.isPageUrlReport(dataTableParams.module, dataTableParams.action) ||
- DataTable_RowActions_Transitions.isPageTitleReport(dataTableParams.module, dataTableParams.action)
+ DataTable_RowActions_Transitions.isPageTitleReport(dataTableParams.module, dataTableParams.action) ||
+ DataTable_RowActions_Transitions.isActionCustomDimensionReport(dataTableParams)
);
},
@@ -109,6 +135,11 @@ DataTable_RowActions_Registry.register({
// not on page url without link (i.e. "Page URL not defined")
return false;
}
+ if (DataTable_RowActions_Transitions.isActionCustomDimensionReport(dataTableParams)
+ && !tr.parents('table').first().hasClass('subDataTable')) {
+ // only show it in subtables of custom dimensions
+ return false;
+ }
return true;
}
@@ -118,8 +149,8 @@ DataTable_RowActions_Registry.register({
// TRANSITIONS IMPLEMENTATION
//
-function Piwik_Transitions(actionType, actionName, rowAction) {
- this.reset(actionType, actionName);
+function Piwik_Transitions(actionType, actionName, rowAction, segment) {
+ this.reset(actionType, actionName, segment);
this.rowAction = rowAction;
this.ajax = new Piwik_Transitions_Ajax();
@@ -129,9 +160,10 @@ function Piwik_Transitions(actionType, actionName, rowAction) {
this.rightGroups = ['followingPages', 'followingSiteSearches', 'downloads', 'outlinks'];
}
-Piwik_Transitions.prototype.reset = function (actionType, actionName) {
+Piwik_Transitions.prototype.reset = function (actionType, actionName, segment) {
this.actionType = actionType;
this.actionName = actionName;
+ this.segment = segment;
this.popover = null;
this.canvas = null;
@@ -179,7 +211,7 @@ Piwik_Transitions.prototype.showPopover = function () {
}
// load the data
- self.model.loadData(self.actionType, self.actionName, function () {
+ self.model.loadData(self.actionType, self.actionName, self.segment, function () {
if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
// html not there yet
callbackForHtml = bothLoaded;
@@ -1249,7 +1281,7 @@ Piwik_Transitions_Model.prototype.htmlLoaded = function () {
};
};
-Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, callback) {
+Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, segment, callback) {
var self = this;
this.pageviews = 0;
@@ -1287,11 +1319,16 @@ Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, c
this.date = '';
- this.ajax.callApi('Transitions.getTransitionsForAction', {
- actionType: actionType,
- actionName: actionName,
- expanded: 1
- },
+ var params = {
+ actionType: actionType,
+ actionName: actionName,
+ expanded: 1
+ };
+ if (segment) {
+ params.segment = segment;
+ }
+
+ this.ajax.callApi('Transitions.getTransitionsForAction', params,
function (report) {
self.date = report.date;
diff --git a/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php b/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php
index 8ba755fabd..a32129c93b 100644
--- a/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php
+++ b/tests/PHPUnit/Fixtures/SomeVisitsCustomVariablesCampaignsNotHeuristics.php
@@ -36,10 +36,9 @@ class SomeVisitsCustomVariablesCampaignsNotHeuristics extends Fixture
private function setPiwikEnvironmentOverrides()
{
- $configOverride = $this->getTestEnvironment()->configOverride;
- $configOverride['Tracker']['create_new_visit_when_website_referrer_changes'] = 1;
- $this->getTestEnvironment()->configOverride = $configOverride;
- $this->getTestEnvironment()->save();
+ $env = $this->getTestEnvironment();
+ $env->overrideConfig('Tracker', 'create_new_visit_when_website_referrer_changes', 1);
+ $env->save();
}
private function setUpWebsitesAndGoals()
diff --git a/tests/PHPUnit/Fixtures/UITestFixture.php b/tests/PHPUnit/Fixtures/UITestFixture.php
index 1e9543eb04..a1d2d316d0 100644
--- a/tests/PHPUnit/Fixtures/UITestFixture.php
+++ b/tests/PHPUnit/Fixtures/UITestFixture.php
@@ -26,6 +26,7 @@ use Piwik\WidgetsList;
use Piwik\Tests\Framework\OverrideLogin;
use Piwik\Tests\Framework\TestCase\SystemTestCase;
use Piwik\Plugins\VisitsSummary\API as VisitsSummaryAPI;
+use Piwik\Config as PiwikConfig;
/**
* Fixture for UI tests.
@@ -46,7 +47,9 @@ class UITestFixture extends SqlDump
parent::setUp();
+ self::resetPluginsInstalledConfig();
self::updateDatabase();
+ self::installAndActivatePlugins($this->getTestEnvironment());
// make sure site has an early enough creation date (for period selector tests)
Db::get()->update(Common::prefixTable("site"),
diff --git a/tests/PHPUnit/Framework/Fixture.php b/tests/PHPUnit/Framework/Fixture.php
index 29b4625465..f421ba5658 100644
--- a/tests/PHPUnit/Framework/Fixture.php
+++ b/tests/PHPUnit/Framework/Fixture.php
@@ -181,7 +181,7 @@ class Fixture extends \PHPUnit_Framework_Assert
return $id;
}
- return Config::getInstance()->database_tests['dbname'];
+ return self::getConfig()->database_tests['dbname'];
}
public function performSetUp($setupEnvironmentOnly = false)
@@ -213,7 +213,7 @@ class Fixture extends \PHPUnit_Framework_Assert
$this->createEnvironmentInstance();
if ($this->dbName === false) { // must be after test config is created
- $this->dbName = Config::getInstance()->database['dbname'];
+ $this->dbName = self::getConfig()->database['dbname'];
}
try {
@@ -230,14 +230,14 @@ class Fixture extends \PHPUnit_Framework_Assert
Tracker::disconnectCachedDbConnection();
// reconnect once we're sure the database exists
- Config::getInstance()->database['dbname'] = $this->dbName;
+ self::getConfig()->database['dbname'] = $this->dbName;
Db::createDatabaseObject();
Db::get()->query("SET wait_timeout=28800;");
DbHelper::createTables();
- Manager::getInstance()->unloadPlugins();
+ self::getPluginManager()->unloadPlugins();
} catch (Exception $e) {
static::fail("TEST INITIALIZATION FAILED: " . $e->getMessage() . "\n" . $e->getTraceAsString());
@@ -254,11 +254,12 @@ class Fixture extends \PHPUnit_Framework_Assert
Cache::deleteTrackerCache();
- static::loadAllPlugins($this->getTestEnvironment(), $this->testCaseClass, $this->extraPluginsToLoad);
+ self::resetPluginsInstalledConfig();
+ $testEnvironment = $this->getTestEnvironment();
+ static::loadAllPlugins($testEnvironment, $this->testCaseClass, $this->extraPluginsToLoad);
self::updateDatabase();
-
- self::installAndActivatePlugins();
+ self::installAndActivatePlugins($testEnvironment);
$_GET = $_REQUEST = array();
$_SERVER['HTTP_REFERER'] = '';
@@ -364,10 +365,26 @@ class Fixture extends \PHPUnit_Framework_Assert
$_GET = $_REQUEST = array();
Translate::reset();
- Config::getInstance()->Plugins; // make sure Plugins exists in a config object for next tests that use Plugin\Manager
+ self::getConfig()->Plugins; // make sure Plugins exists in a config object for next tests that use Plugin\Manager
// since Plugin\Manager uses getFromGlobalConfig which doesn't init the config object
}
+ protected static function resetPluginsInstalledConfig()
+ {
+ $config = self::getConfig();
+ $installed = $config->PluginsInstalled;
+ $installed['PluginsInstalled'] = array();
+ $config->PluginsInstalled = $installed;
+ }
+
+ protected static function rememberCurrentlyInstalledPluginsAcrossRequests(TestingEnvironmentVariables $testEnvironment)
+ {
+ $plugins = self::getPluginManager()->getInstalledPluginsName();
+
+ $testEnvironment->overrideConfig('PluginsInstalled', 'PluginsInstalled', $plugins);
+ $testEnvironment->save();
+ }
+
/**
* @param \Piwik\Tests\Framework\TestingEnvironmentVariables|null $testEnvironment Ignored.
* @param bool|false $testCaseClass Ignored.
@@ -376,12 +393,12 @@ class Fixture extends \PHPUnit_Framework_Assert
public static function loadAllPlugins(TestingEnvironmentVariables $testEnvironment = null, $testCaseClass = false, $extraPluginsToLoad = array())
{
DbHelper::createTables();
- Plugin\Manager::getInstance()->loadActivatedPlugins();
+ self::getPluginManager()->loadActivatedPlugins();
}
- public static function installAndActivatePlugins()
+ public static function installAndActivatePlugins(TestingEnvironmentVariables $testEnvironment)
{
- $pluginsManager = Manager::getInstance();
+ $pluginsManager = self::getPluginManager();
// Install plugins
$messages = $pluginsManager->installLoadedPlugins();
@@ -398,19 +415,35 @@ class Fixture extends \PHPUnit_Framework_Assert
}
$pluginsManager->loadPluginTranslations();
+
+ self::rememberCurrentlyInstalledPluginsAcrossRequests($testEnvironment);
+ }
+
+ private static function getPluginManager()
+ {
+ return Manager::getInstance();
+ }
+
+ private static function getConfig()
+ {
+ return Config::getInstance();
}
public static function unloadAllPlugins()
{
try {
- $manager = Manager::getInstance();
+ $manager = self::getPluginManager();
$plugins = $manager->getLoadedPlugins();
foreach ($plugins as $plugin) {
$plugin->uninstall();
}
- Manager::getInstance()->unloadPlugins();
+
+ $manager->unloadPlugins();
} catch (Exception $e) {
}
+
+ self::resetPluginsInstalledConfig();
+ self::rememberCurrentlyInstalledPluginsAcrossRequests(new TestingEnvironmentVariables());
}
/**
@@ -460,6 +493,7 @@ class Fixture extends \PHPUnit_Framework_Assert
// Clear the memory Website cache
Site::clearCache();
+ Cache::deleteCacheWebsiteAttributes($idSite);
return $idSite;
}
@@ -471,9 +505,10 @@ class Fixture extends \PHPUnit_Framework_Assert
*/
public static function getRootUrl()
{
- $piwikUrl = Config::getInstance()->tests['http_host'];
- $piwikUri = Config::getInstance()->tests['request_uri'];
- $piwikPort = Config::getInstance()->tests['port'];
+ $config = self::getConfig();
+ $piwikUrl = $config->tests['http_host'];
+ $piwikUri = $config->tests['request_uri'];
+ $piwikPort = $config->tests['port'];
if($piwikUri == '@REQUEST_URI@') {
throw new Exception("Piwik is mis-configured. Remove (or fix) the 'request_uri' entry below [tests] section in your config.ini.php. ");
@@ -870,7 +905,7 @@ class Fixture extends \PHPUnit_Framework_Assert
*/
public static function connectWithoutDatabase()
{
- $dbConfig = Config::getInstance()->database;
+ $dbConfig = self::getConfig()->database;
$oldDbName = $dbConfig['dbname'];
$dbConfig['dbname'] = null;
@@ -888,7 +923,7 @@ class Fixture extends \PHPUnit_Framework_Assert
public function dropDatabase($dbName = null)
{
- $dbName = $dbName ?: $this->dbName ?: Config::getInstance()->database_tests['dbname'];
+ $dbName = $dbName ?: $this->dbName ?: self::getConfig()->database_tests['dbname'];
$this->log("Dropping database '$dbName'...");
diff --git a/tests/PHPUnit/Framework/TestingEnvironmentVariables.php b/tests/PHPUnit/Framework/TestingEnvironmentVariables.php
index 4b33a7fe07..60942d2510 100644
--- a/tests/PHPUnit/Framework/TestingEnvironmentVariables.php
+++ b/tests/PHPUnit/Framework/TestingEnvironmentVariables.php
@@ -39,6 +39,45 @@ class TestingEnvironmentVariables
return isset($this->behaviorOverrideProperties[$name]);
}
+ /**
+ * Overrides a config entry.
+ *
+ * You can use this method either to set one specific config value `overrideConfig(group, name, value)`
+ * or you can set a whole group of values `overrideConfig(group, valueObject)`.
+ *
+ * @param string $group Eg 'General', 'log', or any other config group name
+ * @param string|array $name The name of the config within the group you want to overwrite. If you want to overwrite
+ * the whole group just leave `$value` empty and instead provide an array of key/value pairs
+ * here.
+ * @param string|int|array|null $value The value you want to set for the given config.
+ * @throws \Exception if no name is set
+ */
+ public function overrideConfig($group, $name, $value = null)
+ {
+ if (empty($name) && !is_array($name)) {
+ throw new \Exception('No name set that needs to be overwritten');
+ }
+
+ $config = $this->configOverride;
+
+ if (empty($config)) {
+ $config = array();
+ }
+
+ if (!isset($value) && is_array($name)) {
+ $config[$group] = $name;
+ $this->configOverride = $config;
+ return;
+ }
+
+ if (!isset($config[$group])) {
+ $config[$group] = array();
+ }
+
+ $config[$group][$name] = $value;
+ $this->configOverride = $config;
+ }
+
public function save()
{
$includePath = __DIR__ . '/../../..';
diff --git a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php
index 74dbb05d73..12a60f3821 100644
--- a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php
+++ b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php
@@ -13,6 +13,7 @@ use Piwik\Plugins\VisitFrequency\API as VisitFrequencyApi;
use Piwik\Tests\Framework\TestCase\SystemTestCase;
use Piwik\Tests\Fixtures\SqlDump;
use Piwik\Tests\Framework\Fixture;
+use Piwik\Tests\Framework\TestingEnvironmentVariables;
/**
* Tests that Piwik 2.0 works w/ data from Piwik 1.12.
@@ -32,6 +33,7 @@ class BackwardsCompatibility1XTest extends SystemTestCase
// note: not sure why I have to manually install plugin
\Piwik\Plugin\Manager::getInstance()->loadPlugin('CustomAlerts')->install();
+ \Piwik\Plugin\Manager::getInstance()->loadPlugin('CustomDimensions')->install();
$result = Fixture::updateDatabase();
if ($result === false) {
diff --git a/tests/PHPUnit/System/TrackerTest.php b/tests/PHPUnit/System/TrackerTest.php
index 461b0a2385..417b40f72a 100644
--- a/tests/PHPUnit/System/TrackerTest.php
+++ b/tests/PHPUnit/System/TrackerTest.php
@@ -180,9 +180,7 @@ class TrackerTest extends IntegrationTestCase
public function test_scheduledTasks_CanBeRunThroughTracker_WithOutputIncluded_IfDebugQueryParamUsed()
{
$environment = $this->setScheduledTasksToRunInTracker();
- $config = $environment->configOverride;
- $config['log']['log_writers'] = array('screen');
- $environment->configOverride = $config;
+ $environment->overrideConfig('log', 'log_writers', array('screen'));
$environment->save();
$urlToTest = $this->getSimpleTrackingUrl() . '&debug=1';
@@ -298,7 +296,8 @@ class TrackerTest extends IntegrationTestCase
$testingEnvironment = new \Piwik\Tests\Framework\TestingEnvironmentVariables();
$testingEnvironment->testCaseClass = 'Piwik\Tests\System\TrackerTest';
$testingEnvironment->addScheduledTask = true;
- $testingEnvironment->configOverride = array('Tracker' => array('scheduled_tasks_min_interval' => 1, 'debug_on_demand' => 1));
+ $testingEnvironment->overrideConfig('Tracker', array('scheduled_tasks_min_interval' => 1, 'debug_on_demand' => 1));
+ $testingEnvironment->overrideConfig('log', array());
$testingEnvironment->save();
return $testingEnvironment;
diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml
new file mode 100644
index 0000000000..7a397597a0
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableExtractionDimensions.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <value>url</value>
+ <name>Page URL</name>
+ </row>
+ <row>
+ <value>urlparam</value>
+ <name>Page URL Parameter</name>
+ </row>
+ <row>
+ <value>action_name</value>
+ <name>Page Title</name>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml
new file mode 100644
index 0000000000..6df7142c16
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getAvailableScopes.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <name>visit</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+ <row>
+ <name>action</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml
new file mode 100644
index 0000000000..c234bed59e
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_ImportLogs__CustomDimensions.getConfiguredCustomDimensions.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result /> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml
new file mode 100644
index 0000000000..7a397597a0
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableExtractionDimensions.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <value>url</value>
+ <name>Page URL</name>
+ </row>
+ <row>
+ <value>urlparam</value>
+ <name>Page URL Parameter</name>
+ </row>
+ <row>
+ <value>action_name</value>
+ <name>Page Title</name>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml
new file mode 100644
index 0000000000..6df7142c16
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getAvailableScopes.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <name>visit</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+ <row>
+ <name>action</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml
new file mode 100644
index 0000000000..c234bed59e
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits__CustomDimensions.getConfiguredCustomDimensions.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result /> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml
index 4a3961acd5..52aca7f701 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_hideColumns___API.getProcessedReport_day.xml
@@ -45,7 +45,7 @@
<reportMetadata>
<row>
- <idsubdatatable>5117</idsubdatatable>
+ <idsubdatatable>5119</idsubdatatable>
</row>
</reportMetadata>
<reportTotal>
diff --git a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml
index a6a946e829..c5f4f6b6a5 100644
--- a/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_OneVisitorTwoVisits_showColumns___API.getProcessedReport_day.xml
@@ -54,7 +54,7 @@
<reportMetadata>
<row>
- <idsubdatatable>5121</idsubdatatable>
+ <idsubdatatable>5123</idsubdatatable>
</row>
</reportMetadata>
<reportTotal>
diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml
new file mode 100644
index 0000000000..7a397597a0
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableExtractionDimensions.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <value>url</value>
+ <name>Page URL</name>
+ </row>
+ <row>
+ <value>urlparam</value>
+ <name>Page URL Parameter</name>
+ </row>
+ <row>
+ <value>action_name</value>
+ <name>Page Title</name>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml
new file mode 100644
index 0000000000..6df7142c16
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getAvailableScopes.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <name>visit</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+ <row>
+ <name>action</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml
new file mode 100644
index 0000000000..c234bed59e
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_noVisit_PeriodIsLast__CustomDimensions.getConfiguredCustomDimensions.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result /> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml
new file mode 100644
index 0000000000..7a397597a0
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableExtractionDimensions.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <value>url</value>
+ <name>Page URL</name>
+ </row>
+ <row>
+ <value>urlparam</value>
+ <name>Page URL Parameter</name>
+ </row>
+ <row>
+ <value>action_name</value>
+ <name>Page Title</name>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml
new file mode 100644
index 0000000000..6df7142c16
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getAvailableScopes.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <name>visit</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+ <row>
+ <name>action</name>
+ <numSlotsAvailable>5</numSlotsAvailable>
+ <numSlotsUsed>0</numSlotsUsed>
+ <numSlotsLeft>5</numSlotsLeft>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml
new file mode 100644
index 0000000000..c234bed59e
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_noVisit__CustomDimensions.getConfiguredCustomDimensions.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result /> \ No newline at end of file
diff --git a/tests/UI/expected-ui-screenshots b/tests/UI/expected-ui-screenshots
-Subproject 9fffafc0764f3b6bb3c3b84c28db1a4925b6da5
+Subproject c4db80d7f41dcb7c43795675e948f861d5581fe
diff --git a/tests/UI/specs/Dashboard_spec.js b/tests/UI/specs/Dashboard_spec.js
index f17e9f4258..57626c4d1f 100644
--- a/tests/UI/specs/Dashboard_spec.js
+++ b/tests/UI/specs/Dashboard_spec.js
@@ -38,11 +38,7 @@ describe("Dashboard", function () {
var setup = function (done) {
// make sure live widget doesn't refresh constantly for UI tests
- testEnvironment.configOverride = {
- General: {
- 'live_widget_refresh_after_seconds': 1000000
- }
- };
+ testEnvironment.overrideConfig('General', 'live_widget_refresh_after_seconds', 1000000);
testEnvironment.save();
// save empty layout for dashboard ID = 5
diff --git a/tests/UI/specs/Morpheus_spec.js b/tests/UI/specs/Morpheus_spec.js
index e2dd4322e5..0a57d26540 100644
--- a/tests/UI/specs/Morpheus_spec.js
+++ b/tests/UI/specs/Morpheus_spec.js
@@ -12,11 +12,7 @@ describe("Morpheus", function () {
before(function () {
// Enable development mode
- testEnvironment.configOverride = {
- Development: {
- enabled: true
- }
- };
+ testEnvironment.overrideConfig('Development', 'enabled', true);
testEnvironment.save();
});
diff --git a/tests/UI/specs/Overlay_spec.js b/tests/UI/specs/Overlay_spec.js
index 2a63870081..d4117627e3 100644
--- a/tests/UI/specs/Overlay_spec.js
+++ b/tests/UI/specs/Overlay_spec.js
@@ -12,6 +12,7 @@ describe("Overlay", function () {
this.timeout(0);
var url = null;
+ var urlWithSegment;
function removeOptOutIframe(page) {
page.evaluate(function () {
@@ -20,8 +21,12 @@ describe("Overlay", function () {
}
before(function (done) {
- url = "?module=Overlay&period=year&date=today&idSite=3#l=" + encodeURIComponent(testEnvironment.overlayUrl).replace(/[%]/g, "$");
-
+ var baseUrl = '?module=Overlay&period=year&date=today&idSite=3';
+ var hash = '#l=' + encodeURIComponent(testEnvironment.overlayUrl).replace(/[%]/g, "$");
+
+ url = baseUrl + hash;
+ urlWithSegment = baseUrl + '&segment=' + encodeURIComponent('visitIp==20.56.34.67') + hash;
+
testEnvironment.callApi("SitesManager.addSiteAliasUrls", {idSite: 3, urls: [config.piwikUrl]}, done);
});
@@ -122,4 +127,12 @@ describe("Overlay", function () {
removeOptOutIframe(page);
}, done);
});
+
+ it("should load an overlay with segment", function (done) {
+ expect.screenshot("loaded_with_segment").to.be.capture(function (page) {
+ page.load(urlWithSegment);
+
+ removeOptOutIframe(page);
+ }, done);
+ });
}); \ No newline at end of file
diff --git a/tests/UI/specs/Theme_spec.js b/tests/UI/specs/Theme_spec.js
index 81c162b0c5..a7fffd2f30 100644
--- a/tests/UI/specs/Theme_spec.js
+++ b/tests/UI/specs/Theme_spec.js
@@ -20,18 +20,14 @@ describe("Theme", function () {
testEnvironment.pluginsToLoad = ['ExampleTheme'];
// Enable development mode to be able to see the UI demo page
- testEnvironment.configOverride = {
- Development: {
- enabled: true
- }
- };
-
+ testEnvironment.overrideConfig('Development', 'enabled', true);
testEnvironment.save();
clearAssets();
});
after(function () {
+
clearAssets();
});
diff --git a/tests/UI/specs/UIIntegration_spec.js b/tests/UI/specs/UIIntegration_spec.js
index 4e866037ab..888391e81c 100644
--- a/tests/UI/specs/UIIntegration_spec.js
+++ b/tests/UI/specs/UIIntegration_spec.js
@@ -30,7 +30,9 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik?
});
beforeEach(function () {
- delete testEnvironment.configOverride;
+ if (testEnvironment.configOverride.database) {
+ delete testEnvironment.configOverride.database;
+ }
testEnvironment.testUseMockAuth = 1;
testEnvironment.save();
});
@@ -528,15 +530,14 @@ describe("UIIntegrationTest", function () { // TODO: Rename to Piwik?
// DB error message
it('should fail correctly when db information in config is incorrect', function (done) {
- testEnvironment.configOverride = {
- database: {
- host: config.phpServer.REMOTE_ADDR,
- username: 'slkdfjsdlkfj',
- password: 'slkdfjsldkfj',
- dbname: 'abcdefg',
- tables_prefix: 'gfedcba'
- }
- };
+
+ testEnvironment.overrideConfig('database', {
+ host: config.phpServer.REMOTE_ADDR,
+ username: 'slkdfjsdlkfj',
+ password: 'slkdfjsldkfj',
+ dbname: 'abcdefg',
+ tables_prefix: 'gfedcba'
+ });
testEnvironment.save();
expect.screenshot('db_connect_error').to.be.capture(function (page) {
diff --git a/tests/javascript/index.php b/tests/javascript/index.php
index 0cfd028493..71a125eb0c 100644
--- a/tests/javascript/index.php
+++ b/tests/javascript/index.php
@@ -1928,7 +1928,7 @@ function PiwikTest() {
});
test("API methods", function() {
- expect(66);
+ expect(69);
equal( typeof Piwik.addPlugin, 'function', 'addPlugin' );
equal( typeof Piwik.getTracker, 'function', 'getTracker' );
@@ -1957,6 +1957,9 @@ function PiwikTest() {
equal( typeof tracker.setCustomData, 'function', 'setCustomData' );
equal( typeof tracker.getCustomData, 'function', 'getCustomData' );
equal( typeof tracker.setCustomRequestProcessing, 'function', 'setCustomRequestProcessing' );
+ equal( typeof tracker.setCustomDimension, 'function', 'setCustomDimension' );
+ equal( typeof tracker.getCustomDimension, 'function', 'getCustomDimension' );
+ equal( typeof tracker.deleteCustomDimension, 'function', 'deleteCustomDimension' );
equal( typeof tracker.setCustomVariable, 'function', 'setCustomVariable' );
equal( typeof tracker.getCustomVariable, 'function', 'getCustomVariable' );
equal( typeof tracker.deleteCustomVariable, 'function', 'deleteCustomVariable' );
@@ -2725,7 +2728,7 @@ if ($mysql) {
});
test("tracking", function() {
- expect(102);
+ expect(114);
// Prevent Opera and HtmlUnit from performing the default action (i.e., load the href URL)
var stopEvent = function (evt) {
@@ -2781,6 +2784,21 @@ if ($mysql) {
deepEqual( tracker.getCustomVariable(5), ["new name", ""], "getting a custom variable with no value" );
tracker.deleteCustomVariable(5);
+ equal(tracker.getCustomDimension(94), null, "if no custom dimension for this index is specified should return null");
+ equal(tracker.getCustomDimension(-1), null, "if custom dimension index is invalid should return null");
+ equal(tracker.getCustomDimension('not valid'), null, "if custom dimension index is invalid should return null");
+ tracker.setCustomDimension(1, 5);
+ equal(tracker.getCustomDimension(1), "5", "set custom dimension should convert any value to a string" );
+ tracker.setCustomDimension(1, "my custom value");
+ equal(tracker.getCustomDimension(1), "my custom value", "should get stored custom dimension value" );
+ tracker.setCustomDimension(2, undefined);
+ equal(tracker.getCustomDimension(2), "", "setCustomDimension should convert undefined to an empty string" );
+
+ tracker.setCustomDimension(3, 'my third value');
+ equal(tracker.getCustomDimension(3), "my third value", "deleteCustomDimension verify a value is set for this dimension" );
+ tracker.deleteCustomDimension(3);
+ equal(tracker.getCustomDimension(3), null, "deleteCustomDimension verify value was removed" );
+
tracker.setDocumentTitle("PiwikTest");
var referrerUrl = "http://referrer.example.com/page/sub?query=test&test2=test3";
@@ -2789,7 +2807,10 @@ if ($mysql) {
referrerTimestamp = Math.round(new Date().getTime() / 1000);
tracker.trackPageView();
- tracker.trackPageView("CustomTitleTest");
+ equal(tracker.getCustomDimension(1), "my custom value", "custom dimensions should not be cleared after a tracked pageview");
+ equal(tracker.getCustomDimension(2), "", "custom dimensions should not be cleared after a tracked pageview");
+
+ tracker.trackPageView("CustomTitleTest", {dimension2: 'my new value', dimension5: 'another dimension'});
var customUrlShouldNotChangeCampaign = "http://localhost.localdomain/?utm_campaign=NONONONONONONO&utm_term=PLEASE NO!";
tracker.setCustomUrl(customUrl);
@@ -3054,6 +3075,12 @@ if ($mysql) {
// 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");
+ // Test CustomDimension (persistent ones across requests)
+ ok( /dimension1=my%20custom%20value&dimension2=&/.test(results), "test custom dimensions are set");
+
+ // send along a page view and ony valid for this pageview (dimension 2 overwrites another one)
+ ok( /dimension2=my%20new%20value&dimension5=another%20dimension&dimension1=my%20custom%20value&data=%7B%22token/.test( results ), "trackPageView(customTitle, customData)" );
+
// Test campaign parameters set
ok( /&_rcn=YEAH&_rck=RIGHT!/.test( results), "Test campaign parameters found");
ok( /&_ref=http%3A%2F%2Freferrer.example.com%2Fpage%2Fsub%3Fquery%3Dtest%26test2%3Dtest3/.test( results), "Test cookie Ref URL found ");
diff --git a/tests/lib/screenshot-testing/support/chai-extras.js b/tests/lib/screenshot-testing/support/chai-extras.js
index a43c8a07e3..29c2d4f47b 100644
--- a/tests/lib/screenshot-testing/support/chai-extras.js
+++ b/tests/lib/screenshot-testing/support/chai-extras.js
@@ -181,6 +181,7 @@ function capture(screenName, compareAgainst, selector, pageSetupFn, comparisonTh
child.on("exit", function (code) {
if (testFailure) {
testFailure = 'Processed screenshot does not match expected for ' + screenshotFileName + ' ' + testFailure;
+ testFailure += 'TestEnvironment was ' + JSON.stringify(testEnvironment);
}
if (code == 0 && !testFailure) {
diff --git a/tests/lib/screenshot-testing/support/page-renderer.js b/tests/lib/screenshot-testing/support/page-renderer.js
index 6c23fd0243..81fa28e238 100644
--- a/tests/lib/screenshot-testing/support/page-renderer.js
+++ b/tests/lib/screenshot-testing/support/page-renderer.js
@@ -338,7 +338,7 @@ PageRenderer.prototype.capture = function (outputPath, callback, selector) {
self.abort();
callback(new Error("Screenshot load timeout. Details:\n" + timeoutDetails));
- }, 120 * 1000);
+ }, 180 * 1000);
if (this.webpage === null) {
this._recreateWebPage();
diff --git a/tests/lib/screenshot-testing/support/test-environment.js b/tests/lib/screenshot-testing/support/test-environment.js
index 700493f933..01914c8c24 100644
--- a/tests/lib/screenshot-testing/support/test-environment.js
+++ b/tests/lib/screenshot-testing/support/test-environment.js
@@ -25,6 +25,7 @@ TestingEnvironment.prototype.reload = function () {
this['useOverrideJs'] = true;
this['loadRealTranslations'] = true; // UI tests should test w/ real translations, not translation keys
this['testUseMockAuth'] = true;
+ this['configOverride'] = {};
if (fs.exists(testingEnvironmentOverridePath)) {
var data = JSON.parse(fs.read(testingEnvironmentOverridePath));
@@ -34,6 +35,33 @@ TestingEnvironment.prototype.reload = function () {
}
};
+/**
+ * Overrides a config entry.
+ *
+ * You can use this method either to set one specific config value `overrideConfig(group, name, value)`
+ * or you can set a whole group of values `overrideConfig(group, valueObject)`.
+ */
+TestingEnvironment.prototype.overrideConfig = function (group, name, value) {
+ if (!name) {
+ return;
+ }
+
+ if (!this['configOverride']) {
+ this['configOverride'] = {};
+ }
+
+ if ((typeof value) === 'undefined') {
+ this['configOverride'][group] = name;
+ return;
+ }
+
+ if (!this['configOverride'][group]) {
+ this['configOverride'][group] = {};
+ }
+
+ this['configOverride'][group][name] = value;
+};
+
TestingEnvironment.prototype.save = function () {
var copy = {};
for (var key in this) {