add icons for Character groups
242
www/analytics/plugins/CoreHome/Controller.php
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\CoreHome;
|
||||
|
||||
use Exception;
|
||||
use Piwik\API\Request;
|
||||
use Piwik\Common;
|
||||
use Piwik\Date;
|
||||
use Piwik\FrontController;
|
||||
use Piwik\Menu\MenuMain;
|
||||
use Piwik\Notification\Manager as NotificationManager;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\CoreHome\DataTableRowAction\MultiRowEvolution;
|
||||
use Piwik\Plugins\CoreHome\DataTableRowAction\RowEvolution;
|
||||
use Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient;
|
||||
use Piwik\Plugins\Dashboard\DashboardManagerControl;
|
||||
use Piwik\Plugins\UsersManager\API;
|
||||
use Piwik\Site;
|
||||
use Piwik\UpdateCheck;
|
||||
use Piwik\Url;
|
||||
use Piwik\View;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class Controller extends \Piwik\Plugin\Controller
|
||||
{
|
||||
function getDefaultAction()
|
||||
{
|
||||
return 'redirectToCoreHomeIndex';
|
||||
}
|
||||
|
||||
function redirectToCoreHomeIndex()
|
||||
{
|
||||
$defaultReport = API::getInstance()->getUserPreference(Piwik::getCurrentUserLogin(), API::PREFERENCE_DEFAULT_REPORT);
|
||||
$module = 'CoreHome';
|
||||
$action = 'index';
|
||||
|
||||
// User preference: default report to load is the All Websites dashboard
|
||||
if ($defaultReport == 'MultiSites'
|
||||
&& \Piwik\Plugin\Manager::getInstance()->isPluginActivated('MultiSites')
|
||||
) {
|
||||
$module = 'MultiSites';
|
||||
}
|
||||
if ($defaultReport == Piwik::getLoginPluginName()) {
|
||||
$module = Piwik::getLoginPluginName();
|
||||
}
|
||||
$idSite = Common::getRequestVar('idSite', false, 'int');
|
||||
parent::redirectToIndex($module, $action, $idSite);
|
||||
}
|
||||
|
||||
public function showInContext()
|
||||
{
|
||||
$controllerName = Common::getRequestVar('moduleToLoad');
|
||||
$actionName = Common::getRequestVar('actionToLoad', 'index');
|
||||
if ($actionName == 'showInContext') {
|
||||
throw new Exception("Preventing infinite recursion...");
|
||||
}
|
||||
$view = $this->getDefaultIndexView();
|
||||
$view->content = FrontController::getInstance()->fetchDispatch($controllerName, $actionName);
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
public function markNotificationAsRead()
|
||||
{
|
||||
$notificationId = Common::getRequestVar('notificationId');
|
||||
NotificationManager::cancel($notificationId);
|
||||
}
|
||||
|
||||
protected function getDefaultIndexView()
|
||||
{
|
||||
$view = new View('@CoreHome/getDefaultIndexView');
|
||||
$this->setGeneralVariablesView($view);
|
||||
$view->menu = MenuMain::getInstance()->getMenu();
|
||||
$view->dashboardSettingsControl = new DashboardManagerControl();
|
||||
$view->content = '';
|
||||
return $view;
|
||||
}
|
||||
|
||||
protected function setDateTodayIfWebsiteCreatedToday()
|
||||
{
|
||||
$date = Common::getRequestVar('date', false);
|
||||
if ($date == 'today'
|
||||
|| Common::getRequestVar('period', false) == 'range'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
$websiteId = Common::getRequestVar('idSite', false, 'int');
|
||||
if ($websiteId) {
|
||||
$website = new Site($websiteId);
|
||||
$datetimeCreationDate = $website->getCreationDate()->getDatetime();
|
||||
$creationDateLocalTimezone = Date::factory($datetimeCreationDate, $website->getTimezone())->toString('Y-m-d');
|
||||
$todayLocalTimezone = Date::factory('now', $website->getTimezone())->toString('Y-m-d');
|
||||
if ($creationDateLocalTimezone == $todayLocalTimezone) {
|
||||
Piwik::redirectToModule('CoreHome', 'index',
|
||||
array('date' => 'today',
|
||||
'idSite' => $websiteId,
|
||||
'period' => Common::getRequestVar('period'))
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->setDateTodayIfWebsiteCreatedToday();
|
||||
$view = $this->getDefaultIndexView();
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// ROW EVOLUTION
|
||||
// The following methods render the popover that shows the
|
||||
// evolution of a singe or multiple rows in a data table
|
||||
// --------------------------------------------------------
|
||||
|
||||
/** Render the entire row evolution popover for a single row */
|
||||
public function getRowEvolutionPopover()
|
||||
{
|
||||
$rowEvolution = $this->makeRowEvolution($isMulti = false);
|
||||
$view = new View('@CoreHome/getRowEvolutionPopover');
|
||||
return $rowEvolution->renderPopover($this, $view);
|
||||
}
|
||||
|
||||
/** Render the entire row evolution popover for multiple rows */
|
||||
public function getMultiRowEvolutionPopover()
|
||||
{
|
||||
$rowEvolution = $this->makeRowEvolution($isMulti = true);
|
||||
$view = new View('@CoreHome/getMultiRowEvolutionPopover');
|
||||
return $rowEvolution->renderPopover($this, $view);
|
||||
}
|
||||
|
||||
/** Generic method to get an evolution graph or a sparkline for the row evolution popover */
|
||||
public function getRowEvolutionGraph($fetch = false, $rowEvolution = null)
|
||||
{
|
||||
if (empty($rowEvolution)) {
|
||||
$label = Common::getRequestVar('label', '', 'string');
|
||||
$isMultiRowEvolution = strpos($label, ',') !== false;
|
||||
|
||||
$rowEvolution = $this->makeRowEvolution($isMultiRowEvolution, $graphType = 'graphEvolution');
|
||||
$rowEvolution->useAvailableMetrics();
|
||||
}
|
||||
|
||||
$view = $rowEvolution->getRowEvolutionGraph();
|
||||
return $this->renderView($view);
|
||||
}
|
||||
|
||||
/** Utility function. Creates a RowEvolution instance. */
|
||||
private function makeRowEvolution($isMultiRowEvolution, $graphType = null)
|
||||
{
|
||||
if ($isMultiRowEvolution) {
|
||||
return new MultiRowEvolution($this->idSite, $this->date, $graphType);
|
||||
} else {
|
||||
return new RowEvolution($this->idSite, $this->date, $graphType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a check for updates and re-renders the header message.
|
||||
*
|
||||
* This will check piwik.org at most once per 10s.
|
||||
*/
|
||||
public function checkForUpdates()
|
||||
{
|
||||
Piwik::checkUserHasSomeAdminAccess();
|
||||
$this->checkTokenInUrl();
|
||||
|
||||
// perform check (but only once every 10s)
|
||||
UpdateCheck::check($force = false, UpdateCheck::UI_CLICK_CHECK_INTERVAL);
|
||||
|
||||
MarketplaceApiClient::clearAllCacheEntries();
|
||||
|
||||
$view = new View('@CoreHome/checkForUpdates');
|
||||
$this->setGeneralVariablesView($view);
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders and echo's the in-app donate form w/ slider.
|
||||
*/
|
||||
public function getDonateForm()
|
||||
{
|
||||
$view = new View('@CoreHome/getDonateForm');
|
||||
if (Common::getRequestVar('widget', false)
|
||||
&& Piwik::hasUserSuperUserAccess()
|
||||
) {
|
||||
$view->footerMessage = Piwik::translate('CoreHome_OnlyForSuperUserAccess');
|
||||
}
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders and echo's HTML that displays the Piwik promo video.
|
||||
*/
|
||||
public function getPromoVideo()
|
||||
{
|
||||
$view = new View('@CoreHome/getPromoVideo');
|
||||
$view->shareText = Piwik::translate('CoreHome_SharePiwikShort');
|
||||
$view->shareTextLong = Piwik::translate('CoreHome_SharePiwikLong');
|
||||
$view->promoVideoUrl = 'http://www.youtube.com/watch?v=OslfF_EH81g';
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the user to a paypal so they can donate to Piwik.
|
||||
*/
|
||||
public function redirectToPaypal()
|
||||
{
|
||||
$parameters = Request::getRequestArrayFromString($request = null);
|
||||
foreach ($parameters as $name => $param) {
|
||||
if ($name == 'idSite'
|
||||
|| $name == 'module'
|
||||
|| $name == 'action'
|
||||
) {
|
||||
unset($parameters[$name]);
|
||||
}
|
||||
}
|
||||
|
||||
$url = "https://www.paypal.com/cgi-bin/webscr?" . Url::getQueryStringFromParameters($parameters);
|
||||
|
||||
header("Location: $url");
|
||||
exit;
|
||||
}
|
||||
|
||||
public function getSiteSelector()
|
||||
{
|
||||
return "<div piwik-siteselector class=\"sites_autocomplete\" switch-site-on-select=\"false\"></div>";
|
||||
}
|
||||
|
||||
public function getPeriodSelector()
|
||||
{
|
||||
$view = new View("@CoreHome/_periodSelect");
|
||||
$this->setGeneralVariablesView($view);
|
||||
return $view->render();
|
||||
}
|
||||
}
|
||||
201
www/analytics/plugins/CoreHome/CoreHome.php
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\CoreHome;
|
||||
|
||||
use Piwik\WidgetsList;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class CoreHome extends \Piwik\Plugin
|
||||
{
|
||||
/**
|
||||
* @see Piwik\Plugin::getListHooksRegistered
|
||||
*/
|
||||
public function getListHooksRegistered()
|
||||
{
|
||||
return array(
|
||||
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
|
||||
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
|
||||
'WidgetsList.addWidgets' => 'addWidgets',
|
||||
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the donate form widget.
|
||||
*/
|
||||
public function addWidgets()
|
||||
{
|
||||
WidgetsList::add('Example Widgets', 'CoreHome_SupportPiwik', 'CoreHome', 'getDonateForm');
|
||||
WidgetsList::add('Example Widgets', 'Installation_Welcome', 'CoreHome', 'getPromoVideo');
|
||||
}
|
||||
|
||||
public function getStylesheetFiles(&$stylesheets)
|
||||
{
|
||||
$stylesheets[] = "libs/jquery/themes/base/jquery-ui.css";
|
||||
$stylesheets[] = "libs/jquery/stylesheets/jquery.jscrollpane.css";
|
||||
$stylesheets[] = "libs/jquery/stylesheets/scroll.less";
|
||||
$stylesheets[] = "plugins/Zeitgeist/stylesheets/base.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/coreHome.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/menu.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/dataTable.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/cloud.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/jquery.ui.autocomplete.css";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/jqplotColors.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/sparklineColors.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/promo.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/color_manager.css";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/sparklineColors.less";
|
||||
$stylesheets[] = "plugins/CoreHome/stylesheets/notification.less";
|
||||
$stylesheets[] = "plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.less";
|
||||
}
|
||||
|
||||
public function getJsFiles(&$jsFiles)
|
||||
{
|
||||
$jsFiles[] = "libs/jquery/jquery.js";
|
||||
$jsFiles[] = "libs/jquery/jquery-ui.js";
|
||||
$jsFiles[] = "libs/jquery/jquery.browser.js";
|
||||
$jsFiles[] = "libs/jquery/jquery.truncate.js";
|
||||
$jsFiles[] = "libs/jquery/jquery.scrollTo.js";
|
||||
$jsFiles[] = "libs/jquery/jquery.history.js";
|
||||
$jsFiles[] = "libs/jquery/jquery.jscrollpane.js";
|
||||
$jsFiles[] = "libs/jquery/jquery.mousewheel.js";
|
||||
$jsFiles[] = "libs/jquery/mwheelIntent.js";
|
||||
$jsFiles[] = "libs/javascript/sprintf.js";
|
||||
$jsFiles[] = "libs/angularjs/angular.min.js";
|
||||
$jsFiles[] = "libs/angularjs/angular-sanitize.min.js";
|
||||
$jsFiles[] = "libs/angularjs/angular-animate.min.js";
|
||||
$jsFiles[] = "plugins/Zeitgeist/javascripts/piwikHelper.js";
|
||||
$jsFiles[] = "plugins/Zeitgeist/javascripts/ajaxHelper.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/require.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/uiControl.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/dataTable.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/dataTable_rowactions.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/popover.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/broadcast.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/menu.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/menu_init.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/calendar.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/sparkline.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/corehome.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/top_controls.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/donate.js";
|
||||
$jsFiles[] = "libs/jqplot/jqplot-custom.min.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/promo.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/color_manager.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/notification.js";
|
||||
$jsFiles[] = "plugins/CoreHome/javascripts/notification_parser.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/piwikAppConfig.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/service.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/services/piwik-api.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/filter.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/translate.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/startfrom.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/filters/evolution.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/directive.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/ignore-click.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/onenter.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/focusif.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/common/directives/dialog.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/piwikApp.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/anchorLinkFix.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-model.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-controller.js";
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/siteselector/siteselector-directive.js";
|
||||
|
||||
$jsFiles[] = "plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline-directive.js";
|
||||
}
|
||||
|
||||
public function getClientSideTranslationKeys(&$translationKeys)
|
||||
{
|
||||
$translationKeys[] = 'General_InvalidDateRange';
|
||||
$translationKeys[] = 'General_Loading';
|
||||
$translationKeys[] = 'General_Show';
|
||||
$translationKeys[] = 'General_Hide';
|
||||
$translationKeys[] = 'General_YearShort';
|
||||
$translationKeys[] = 'General_MultiSitesSummary';
|
||||
$translationKeys[] = 'CoreHome_YouAreUsingTheLatestVersion';
|
||||
$translationKeys[] = 'CoreHome_IncludeRowsWithLowPopulation';
|
||||
$translationKeys[] = 'CoreHome_ExcludeRowsWithLowPopulation';
|
||||
$translationKeys[] = 'CoreHome_DataTableIncludeAggregateRows';
|
||||
$translationKeys[] = 'CoreHome_DataTableExcludeAggregateRows';
|
||||
$translationKeys[] = 'CoreHome_Default';
|
||||
$translationKeys[] = 'CoreHome_PageOf';
|
||||
$translationKeys[] = 'CoreHome_FlattenDataTable';
|
||||
$translationKeys[] = 'CoreHome_UnFlattenDataTable';
|
||||
$translationKeys[] = 'CoreHome_ExternalHelp';
|
||||
$translationKeys[] = 'SitesManager_NotFound';
|
||||
$translationKeys[] = 'Annotations_ViewAndAddAnnotations';
|
||||
$translationKeys[] = 'General_RowEvolutionRowActionTooltipTitle';
|
||||
$translationKeys[] = 'General_RowEvolutionRowActionTooltip';
|
||||
$translationKeys[] = 'Annotations_IconDesc';
|
||||
$translationKeys[] = 'Annotations_IconDescHideNotes';
|
||||
$translationKeys[] = 'Annotations_HideAnnotationsFor';
|
||||
$translationKeys[] = 'General_LoadingPopover';
|
||||
$translationKeys[] = 'General_LoadingPopoverFor';
|
||||
$translationKeys[] = 'General_ShortMonth_1';
|
||||
$translationKeys[] = 'General_ShortMonth_2';
|
||||
$translationKeys[] = 'General_ShortMonth_3';
|
||||
$translationKeys[] = 'General_ShortMonth_4';
|
||||
$translationKeys[] = 'General_ShortMonth_5';
|
||||
$translationKeys[] = 'General_ShortMonth_6';
|
||||
$translationKeys[] = 'General_ShortMonth_7';
|
||||
$translationKeys[] = 'General_ShortMonth_8';
|
||||
$translationKeys[] = 'General_ShortMonth_9';
|
||||
$translationKeys[] = 'General_ShortMonth_10';
|
||||
$translationKeys[] = 'General_ShortMonth_11';
|
||||
$translationKeys[] = 'General_ShortMonth_12';
|
||||
$translationKeys[] = 'General_LongMonth_1';
|
||||
$translationKeys[] = 'General_LongMonth_2';
|
||||
$translationKeys[] = 'General_LongMonth_3';
|
||||
$translationKeys[] = 'General_LongMonth_4';
|
||||
$translationKeys[] = 'General_LongMonth_5';
|
||||
$translationKeys[] = 'General_LongMonth_6';
|
||||
$translationKeys[] = 'General_LongMonth_7';
|
||||
$translationKeys[] = 'General_LongMonth_8';
|
||||
$translationKeys[] = 'General_LongMonth_9';
|
||||
$translationKeys[] = 'General_LongMonth_10';
|
||||
$translationKeys[] = 'General_LongMonth_11';
|
||||
$translationKeys[] = 'General_LongMonth_12';
|
||||
$translationKeys[] = 'General_ShortDay_1';
|
||||
$translationKeys[] = 'General_ShortDay_2';
|
||||
$translationKeys[] = 'General_ShortDay_3';
|
||||
$translationKeys[] = 'General_ShortDay_4';
|
||||
$translationKeys[] = 'General_ShortDay_5';
|
||||
$translationKeys[] = 'General_ShortDay_6';
|
||||
$translationKeys[] = 'General_ShortDay_7';
|
||||
$translationKeys[] = 'General_LongDay_1';
|
||||
$translationKeys[] = 'General_LongDay_2';
|
||||
$translationKeys[] = 'General_LongDay_3';
|
||||
$translationKeys[] = 'General_LongDay_4';
|
||||
$translationKeys[] = 'General_LongDay_5';
|
||||
$translationKeys[] = 'General_LongDay_6';
|
||||
$translationKeys[] = 'General_LongDay_7';
|
||||
$translationKeys[] = 'General_DayMo';
|
||||
$translationKeys[] = 'General_DayTu';
|
||||
$translationKeys[] = 'General_DayWe';
|
||||
$translationKeys[] = 'General_DayTh';
|
||||
$translationKeys[] = 'General_DayFr';
|
||||
$translationKeys[] = 'General_DaySa';
|
||||
$translationKeys[] = 'General_DaySu';
|
||||
$translationKeys[] = 'General_Search';
|
||||
$translationKeys[] = 'General_MoreDetails';
|
||||
$translationKeys[] = 'General_Help';
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\CoreHome\DataTableRowAction;
|
||||
|
||||
use Piwik\Common;
|
||||
use Piwik\Piwik;
|
||||
|
||||
/**
|
||||
* MULTI ROW EVOLUTION
|
||||
* The class handles the popover that shows the evolution of a multiple rows in a data table
|
||||
*/
|
||||
class MultiRowEvolution extends RowEvolution
|
||||
{
|
||||
/** The requested metric */
|
||||
protected $metric;
|
||||
|
||||
/** Show all metrics in the evolution graph when the popover opens */
|
||||
protected $initiallyShowAllMetrics = true;
|
||||
|
||||
/** The metrics available in the metrics select */
|
||||
protected $metricsForSelect;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
* @param int $idSite
|
||||
* @param \Piwik\Date $date ($this->date from controller)
|
||||
*/
|
||||
public function __construct($idSite, $date)
|
||||
{
|
||||
$this->metric = Common::getRequestVar('column', '', 'string');
|
||||
parent::__construct($idSite, $date);
|
||||
}
|
||||
|
||||
protected function loadEvolutionReport($column = false)
|
||||
{
|
||||
// set the "column" parameter for the API.getRowEvolution call
|
||||
parent::loadEvolutionReport($this->metric);
|
||||
}
|
||||
|
||||
protected function extractEvolutionReport($report)
|
||||
{
|
||||
$this->metric = $report['column'];
|
||||
$this->dataTable = $report['reportData'];
|
||||
$this->availableMetrics = $report['metadata']['metrics'];
|
||||
$this->metricsForSelect = $report['metadata']['columns'];
|
||||
$this->dimension = $report['metadata']['dimension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the popover
|
||||
* @param \Piwik\Plugins\CoreHome\Controller $controller
|
||||
* @param View (the popover_rowevolution template)
|
||||
*/
|
||||
public function renderPopover($controller, $view)
|
||||
{
|
||||
// add data for metric select box
|
||||
$view->availableMetrics = $this->metricsForSelect;
|
||||
$view->selectedMetric = $this->metric;
|
||||
|
||||
$view->availableRecordsText = $this->dimension . ': '
|
||||
. Piwik::translate('RowEvolution_ComparingRecords', array(count($this->availableMetrics)));
|
||||
|
||||
return parent::renderPopover($controller, $view);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,342 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*
|
||||
*/
|
||||
namespace Piwik\Plugins\CoreHome\DataTableRowAction;
|
||||
|
||||
use Exception;
|
||||
use Piwik\API\Request;
|
||||
use Piwik\API\ResponseBuilder;
|
||||
use Piwik\Common;
|
||||
use Piwik\DataTable;
|
||||
use Piwik\Date;
|
||||
use Piwik\Metrics;
|
||||
use Piwik\Piwik;
|
||||
use Piwik\Plugins\CoreVisualizations\Visualizations\JqplotGraph\Evolution as EvolutionViz;
|
||||
use Piwik\Url;
|
||||
use Piwik\ViewDataTable\Factory;
|
||||
|
||||
/**
|
||||
* ROW EVOLUTION
|
||||
* The class handles the popover that shows the evolution of a singe row in a data table
|
||||
*/
|
||||
class RowEvolution
|
||||
{
|
||||
|
||||
/** The current site id */
|
||||
protected $idSite;
|
||||
|
||||
/** The api method to get the data. Format: Plugin.apiAction */
|
||||
protected $apiMethod;
|
||||
|
||||
/** The label of the requested row */
|
||||
protected $label;
|
||||
|
||||
/** The requested period */
|
||||
protected $period;
|
||||
|
||||
/** The requested date */
|
||||
protected $date;
|
||||
|
||||
/** The request segment */
|
||||
protected $segment;
|
||||
|
||||
/** The metrics that are available for the requested report and period */
|
||||
protected $availableMetrics;
|
||||
|
||||
/** The name of the dimension of the current report */
|
||||
protected $dimension;
|
||||
|
||||
/**
|
||||
* The data
|
||||
* @var \Piwik\DataTable
|
||||
*/
|
||||
protected $dataTable;
|
||||
|
||||
/** The label of the current record */
|
||||
protected $rowLabel;
|
||||
|
||||
/** The icon of the current record */
|
||||
protected $rowIcon;
|
||||
|
||||
/** The type of graph that has been requested last */
|
||||
protected $graphType;
|
||||
|
||||
/** The metrics for the graph that has been requested last */
|
||||
protected $graphMetrics;
|
||||
|
||||
/** Whether or not to show all metrics in the evolution graph when to popover opens */
|
||||
protected $initiallyShowAllMetrics = false;
|
||||
|
||||
/**
|
||||
* The constructor
|
||||
* Initialize some local variables from the request
|
||||
* @param int $idSite
|
||||
* @param Date $date ($this->date from controller)
|
||||
* @param null|string $graphType
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct($idSite, $date, $graphType = null)
|
||||
{
|
||||
$this->apiMethod = Common::getRequestVar('apiMethod', '', 'string');
|
||||
if (empty($this->apiMethod)) throw new Exception("Parameter apiMethod not set.");
|
||||
|
||||
$this->label = ResponseBuilder::getLabelFromRequest($_GET);
|
||||
$this->label = $this->label[0];
|
||||
|
||||
if ($this->label === '') throw new Exception("Parameter label not set.");
|
||||
|
||||
$this->period = Common::getRequestVar('period', '', 'string');
|
||||
if (empty($this->period)) throw new Exception("Parameter period not set.");
|
||||
|
||||
$this->idSite = $idSite;
|
||||
$this->graphType = $graphType;
|
||||
|
||||
if ($this->period != 'range') {
|
||||
// handle day, week, month and year: display last X periods
|
||||
$end = $date->toString();
|
||||
list($this->date, $lastN) = EvolutionViz::getDateRangeAndLastN($this->period, $end);
|
||||
}
|
||||
$this->segment = \Piwik\API\Request::getRawSegmentFromRequest();
|
||||
|
||||
$this->loadEvolutionReport();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the popover
|
||||
* @param \Piwik\Plugins\CoreHome\Controller $controller
|
||||
* @param View (the popover_rowevolution template)
|
||||
*/
|
||||
public function renderPopover($controller, $view)
|
||||
{
|
||||
// render main evolution graph
|
||||
$this->graphType = 'graphEvolution';
|
||||
$this->graphMetrics = $this->availableMetrics;
|
||||
$view->graph = $controller->getRowEvolutionGraph($fetch = true, $rowEvolution = $this);
|
||||
|
||||
// render metrics overview
|
||||
$view->metrics = $this->getMetricsToggles();
|
||||
|
||||
// available metrics text
|
||||
$metricsText = Piwik::translate('RowEvolution_AvailableMetrics');
|
||||
$popoverTitle = '';
|
||||
if ($this->rowLabel) {
|
||||
$icon = $this->rowIcon ? '<img src="' . $this->rowIcon . '" alt="">' : '';
|
||||
$metricsText = sprintf(Piwik::translate('RowEvolution_MetricsFor'), $this->dimension . ': ' . $icon . ' ' . $this->rowLabel);
|
||||
$popoverTitle = $icon . ' ' . $this->rowLabel;
|
||||
}
|
||||
|
||||
$view->availableMetricsText = $metricsText;
|
||||
$view->popoverTitle = $popoverTitle;
|
||||
|
||||
return $view->render();
|
||||
}
|
||||
|
||||
protected function loadEvolutionReport($column = false)
|
||||
{
|
||||
list($apiModule, $apiAction) = explode('.', $this->apiMethod);
|
||||
|
||||
$parameters = array(
|
||||
'method' => 'API.getRowEvolution',
|
||||
'label' => $this->label,
|
||||
'apiModule' => $apiModule,
|
||||
'apiAction' => $apiAction,
|
||||
'idSite' => $this->idSite,
|
||||
'period' => $this->period,
|
||||
'date' => $this->date,
|
||||
'format' => 'original',
|
||||
'serialize' => '0'
|
||||
);
|
||||
if (!empty($this->segment)) {
|
||||
$parameters['segment'] = $this->segment;
|
||||
}
|
||||
|
||||
if ($column !== false) {
|
||||
$parameters['column'] = $column;
|
||||
}
|
||||
|
||||
$url = Url::getQueryStringFromParameters($parameters);
|
||||
|
||||
$request = new Request($url);
|
||||
$report = $request->process();
|
||||
|
||||
$this->extractEvolutionReport($report);
|
||||
}
|
||||
|
||||
protected function extractEvolutionReport($report)
|
||||
{
|
||||
$this->dataTable = $report['reportData'];
|
||||
$this->rowLabel = $this->extractPrettyLabel($report);
|
||||
$this->rowIcon = !empty($report['logo']) ? $report['logo'] : false;
|
||||
$this->availableMetrics = $report['metadata']['metrics'];
|
||||
$this->dimension = $report['metadata']['dimension'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method to get an evolution graph or a sparkline for the row evolution popover.
|
||||
* Do as much as possible from outside the controller.
|
||||
* @param string|bool $graphType
|
||||
* @param array|bool $metrics
|
||||
* @return Factory
|
||||
*/
|
||||
public function getRowEvolutionGraph($graphType = false, $metrics = false)
|
||||
{
|
||||
// set up the view data table
|
||||
$view = Factory::build($graphType ? : $this->graphType, $this->apiMethod,
|
||||
$controllerAction = 'CoreHome.getRowEvolutionGraph', $forceDefault = true);
|
||||
$view->setDataTable($this->dataTable);
|
||||
|
||||
if (!empty($this->graphMetrics)) { // In row Evolution popover, this is empty
|
||||
$view->config->columns_to_display = array_keys($metrics ? : $this->graphMetrics);
|
||||
}
|
||||
|
||||
$view->config->show_goals = false;
|
||||
$view->config->show_all_views_icons = false;
|
||||
$view->config->show_active_view_icon = false;
|
||||
$view->config->show_related_reports = false;
|
||||
$view->config->show_series_picker = false;
|
||||
$view->config->show_footer_message = false;
|
||||
|
||||
foreach ($this->availableMetrics as $metric => $metadata) {
|
||||
$view->config->translations[$metric] = $metadata['name'];
|
||||
}
|
||||
|
||||
$view->config->external_series_toggle = 'RowEvolutionSeriesToggle';
|
||||
$view->config->external_series_toggle_show_all = $this->initiallyShowAllMetrics;
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare metrics toggles with spark lines
|
||||
* @return array
|
||||
*/
|
||||
protected function getMetricsToggles()
|
||||
{
|
||||
$i = 0;
|
||||
$metrics = array();
|
||||
foreach ($this->availableMetrics as $metric => $metricData) {
|
||||
$unit = Metrics::getUnit($metric, $this->idSite);
|
||||
$change = isset($metricData['change']) ? $metricData['change'] : false;
|
||||
|
||||
list($first, $last) = $this->getFirstAndLastDataPointsForMetric($metric);
|
||||
$details = Piwik::translate('RowEvolution_MetricBetweenText', array($first, $last));
|
||||
|
||||
if ($change !== false) {
|
||||
$lowerIsBetter = Metrics::isLowerValueBetter($metric);
|
||||
if (substr($change, 0, 1) == '+') {
|
||||
$changeClass = $lowerIsBetter ? 'bad' : 'good';
|
||||
$changeImage = $lowerIsBetter ? 'arrow_up_red' : 'arrow_up';
|
||||
} else if (substr($change, 0, 1) == '-') {
|
||||
$changeClass = $lowerIsBetter ? 'good' : 'bad';
|
||||
$changeImage = $lowerIsBetter ? 'arrow_down_green' : 'arrow_down';
|
||||
} else {
|
||||
$changeClass = 'neutral';
|
||||
$changeImage = false;
|
||||
}
|
||||
|
||||
$change = '<span class="' . $changeClass . '">'
|
||||
. ($changeImage ? '<img src="plugins/MultiSites/images/' . $changeImage . '.png" /> ' : '')
|
||||
. $change . '</span>';
|
||||
|
||||
$details .= ', ' . Piwik::translate('RowEvolution_MetricChangeText', $change);
|
||||
}
|
||||
|
||||
// set metric min/max text (used as tooltip for details)
|
||||
$max = isset($metricData['max']) ? $metricData['max'] : 0;
|
||||
$min = isset($metricData['min']) ? $metricData['min'] : 0;
|
||||
$min .= $unit;
|
||||
$max .= $unit;
|
||||
$minmax = Piwik::translate('RowEvolution_MetricMinMax', array($metricData['name'], $min, $max));
|
||||
|
||||
$newMetric = array(
|
||||
'label' => $metricData['name'],
|
||||
'details' => $details,
|
||||
'minmax' => $minmax,
|
||||
'sparkline' => $this->getSparkline($metric),
|
||||
);
|
||||
// Multi Rows, each metric can be for a particular row and display an icon
|
||||
if (!empty($metricData['logo'])) {
|
||||
$newMetric['logo'] = $metricData['logo'];
|
||||
}
|
||||
$metrics[] = $newMetric;
|
||||
$i++;
|
||||
}
|
||||
|
||||
return $metrics;
|
||||
}
|
||||
|
||||
/** Get the img tag for a sparkline showing a single metric */
|
||||
protected function getSparkline($metric)
|
||||
{
|
||||
// sparkline is always echoed, so we need to buffer the output
|
||||
$view = $this->getRowEvolutionGraph($graphType = 'sparkline', $metrics = array($metric => $metric));
|
||||
|
||||
ob_start();
|
||||
$view->render();
|
||||
$spark = ob_get_contents();
|
||||
ob_end_clean();
|
||||
|
||||
// undo header change by sparkline renderer
|
||||
header('Content-type: text/html');
|
||||
|
||||
// base64 encode the image and put it in an img tag
|
||||
$spark = base64_encode($spark);
|
||||
return '<img src="data:image/png;base64,' . $spark . '" />';
|
||||
}
|
||||
|
||||
/** Use the available metrics for the metrics of the last requested graph. */
|
||||
public function useAvailableMetrics()
|
||||
{
|
||||
$this->graphMetrics = $this->availableMetrics;
|
||||
}
|
||||
|
||||
private function getFirstAndLastDataPointsForMetric($metric)
|
||||
{
|
||||
$first = 0;
|
||||
$firstTable = $this->dataTable->getFirstRow();
|
||||
if (!empty($firstTable)) {
|
||||
$row = $firstTable->getFirstRow();
|
||||
if (!empty($row)) {
|
||||
$first = floatval($row->getColumn($metric));
|
||||
}
|
||||
}
|
||||
|
||||
$last = 0;
|
||||
$lastTable = $this->dataTable->getLastRow();
|
||||
if (!empty($lastTable)) {
|
||||
$row = $lastTable->getFirstRow();
|
||||
if (!empty($row)) {
|
||||
$last = floatval($row->getColumn($metric));
|
||||
}
|
||||
}
|
||||
|
||||
return array($first, $last);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $report
|
||||
* @return string
|
||||
*/
|
||||
protected function extractPrettyLabel($report)
|
||||
{
|
||||
// By default, use the specified label
|
||||
$rowLabel = Common::sanitizeInputValue($report['label']);
|
||||
$rowLabel = str_replace('/', '<wbr>/', str_replace('&', '<wbr>&', $rowLabel ));
|
||||
|
||||
// If the dataTable specifies a label_html, use this instead
|
||||
/** @var $dataTableMap \Piwik\DataTable\Map */
|
||||
$dataTableMap = $report['reportData'];
|
||||
$labelPretty = $dataTableMap->getColumn('label_html');
|
||||
$labelPretty = array_filter($labelPretty, 'strlen');
|
||||
$labelPretty = current($labelPretty);
|
||||
if(!empty($labelPretty)) {
|
||||
return $labelPretty;
|
||||
}
|
||||
return $rowLabel;
|
||||
}
|
||||
}
|
||||
108
www/analytics/plugins/CoreHome/angularjs/anchorLinkFix.js
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* See http://dev.piwik.org/trac/ticket/4795 "linking to #hash tag does not work after merging AngularJS"
|
||||
*/
|
||||
(function () {
|
||||
|
||||
function scrollToAnchorNode($node)
|
||||
{
|
||||
$.scrollTo($node, 20);
|
||||
}
|
||||
|
||||
function preventDefaultIfEventExists(event)
|
||||
{
|
||||
if (event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function scrollToAnchorIfPossible(hash, event)
|
||||
{
|
||||
if (!hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (-1 !== hash.indexOf('&')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $node = $('#' + hash);
|
||||
|
||||
if ($node && $node.length) {
|
||||
scrollToAnchorNode($node);
|
||||
preventDefaultIfEventExists(event);
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $('a[name='+ hash + ']');
|
||||
|
||||
if ($node && $node.length) {
|
||||
scrollToAnchorNode($node);
|
||||
preventDefaultIfEventExists(event);
|
||||
}
|
||||
}
|
||||
|
||||
function isLinkWithinSamePage(location, newUrl)
|
||||
{
|
||||
if (location && location.origin && -1 === newUrl.indexOf(location.origin)) {
|
||||
// link to different domain
|
||||
return false;
|
||||
}
|
||||
|
||||
if (location && location.pathname && -1 === newUrl.indexOf(location.pathname)) {
|
||||
// link to different path
|
||||
return false;
|
||||
}
|
||||
|
||||
if (location && location.search && -1 === newUrl.indexOf(location.search)) {
|
||||
// link with different search
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleScrollToAnchorIfPresentOnPageLoad()
|
||||
{
|
||||
if (location.hash.substr(0, 2) == '#/') {
|
||||
var hash = location.hash.substr(2);
|
||||
scrollToAnchorIfPossible(hash, null);
|
||||
}
|
||||
}
|
||||
|
||||
function handleScrollToAnchorAfterPageLoad()
|
||||
{
|
||||
angular.module('piwikApp').run(['$rootScope', function ($rootScope) {
|
||||
|
||||
$rootScope.$on('$locationChangeStart', function (event, newUrl, oldUrl, $location) {
|
||||
|
||||
if (!newUrl) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hashPos = newUrl.indexOf('#/');
|
||||
if (-1 === hashPos) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLinkWithinSamePage(this.location, newUrl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var hash = newUrl.substr(hashPos + 2);
|
||||
|
||||
scrollToAnchorIfPossible(hash, event);
|
||||
});
|
||||
}]);
|
||||
}
|
||||
|
||||
handleScrollToAnchorAfterPageLoad();
|
||||
$(handleScrollToAnchorIfPresentOnPageLoad);
|
||||
|
||||
})();
|
||||
42
www/analytics/plugins/CoreHome/angularjs/common/directives/autocomplete-matched.js
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* If the given text or resolved expression matches any text within the element, the matching text will be wrapped
|
||||
* with a class.
|
||||
*
|
||||
* Example:
|
||||
* <div piwik-autocomplete-matched="'text'">My text</div> ==> <div>My <span class="autocompleteMatched">text</span></div>
|
||||
*
|
||||
* <div piwik-autocomplete-matched="searchTerm">{{ name }}</div>
|
||||
* <input type="text" ng-model="searchTerm">
|
||||
*/
|
||||
angular.module('piwikApp.directive').directive('piwikAutocompleteMatched', function() {
|
||||
return function(scope, element, attrs) {
|
||||
var searchTerm;
|
||||
|
||||
scope.$watch(attrs.piwikAutocompleteMatched, function(value) {
|
||||
searchTerm = value;
|
||||
updateText();
|
||||
});
|
||||
|
||||
function updateText () {
|
||||
if (!searchTerm || !element) {
|
||||
return;
|
||||
}
|
||||
|
||||
var content = element.html();
|
||||
var startTerm = content.toLowerCase().indexOf(searchTerm.toLowerCase());
|
||||
|
||||
if (-1 !== startTerm) {
|
||||
var word = content.substr(startTerm, searchTerm.length);
|
||||
content = content.replace(word, '<span class="autocompleteMatched">' + word + '</span>');
|
||||
element.html(content);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
43
www/analytics/plugins/CoreHome/angularjs/common/directives/autocomplete-matched_spec.js
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
describe('piwikAutocompleteMatchedDirective', function() {
|
||||
var $compile;
|
||||
var $rootScope;
|
||||
|
||||
beforeEach(module('piwikApp.directive'));
|
||||
beforeEach(inject(function(_$compile_, _$rootScope_){
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
}));
|
||||
|
||||
function assertRenderedContentIs(query, expectedResult) {
|
||||
var template = '<div piwik-autocomplete-matched="\'' + query + '\'">My Content</div>';
|
||||
var element = $compile(template)($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element.html()).to.eql(expectedResult);
|
||||
}
|
||||
|
||||
describe('#piwikAutocompleteMatched()', function() {
|
||||
|
||||
it('should not change anything if query does not match the text', function() {
|
||||
assertRenderedContentIs('Whatever', 'My Content');
|
||||
});
|
||||
|
||||
it('should wrap the matching part and find case insensitive', function() {
|
||||
assertRenderedContentIs('y cont', 'M<span class="autocompleteMatched">y Cont</span>ent');
|
||||
});
|
||||
|
||||
it('should be able to wrap the whole content', function() {
|
||||
assertRenderedContentIs('my content', '<span class="autocompleteMatched">My Content</span>');
|
||||
});
|
||||
|
||||
it('should find matching content case sensitive', function() {
|
||||
assertRenderedContentIs('My Co', '<span class="autocompleteMatched">My Co</span>ntent');
|
||||
});
|
||||
});
|
||||
});
|
||||
41
www/analytics/plugins/CoreHome/angularjs/common/directives/dialog.js
vendored
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* <div piwik-dialog="showDialog">...</div>
|
||||
* Will show dialog once showDialog evaluates to true.
|
||||
*
|
||||
* <div piwik-dialog="showDialog" yes="executeMyFunction();">
|
||||
* ... <input type="button" role="yes" value="button">
|
||||
* </div>
|
||||
* Will execute the "executeMyFunction" function in the current scope once the yes button is pressed.
|
||||
*/
|
||||
angular.module('piwikApp.directive').directive('piwikDialog', function(piwik) {
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
|
||||
element.css('display', 'none');
|
||||
|
||||
element.on( "dialogclose", function() {
|
||||
scope.$eval(attrs.piwikDialog+'=false');
|
||||
});
|
||||
|
||||
scope.$watch(attrs.piwikDialog, function(newValue, oldValue) {
|
||||
if (newValue) {
|
||||
piwik.helper.modalConfirm(element, {yes: function() {
|
||||
if (attrs.yes) {
|
||||
scope.$eval(attrs.yes);
|
||||
}
|
||||
}});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
8
www/analytics/plugins/CoreHome/angularjs/common/directives/directive.js
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.directive', []);
|
||||
40
www/analytics/plugins/CoreHome/angularjs/common/directives/focus-anywhere-but-here.js
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* The given expression will be executed when the user presses either escape or presses something outside
|
||||
* of this element
|
||||
*
|
||||
* Example:
|
||||
* <div piwik-focus-anywhere-but-here="closeDialog()">my dialog</div>
|
||||
*/
|
||||
angular.module('piwikApp.directive').directive('piwikFocusAnywhereButHere', function($document){
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attr, ctrl) {
|
||||
|
||||
function onClickOutsideElement (event) {
|
||||
if (element.has(event.target).length === 0) {
|
||||
scope.$apply(attr.piwikFocusAnywhereButHere);
|
||||
}
|
||||
}
|
||||
|
||||
function onEscapeHandler (event) {
|
||||
if (event.which === 27) {
|
||||
scope.$apply(attr.piwikFocusAnywhereButHere);
|
||||
}
|
||||
}
|
||||
|
||||
$document.on('keyup', onEscapeHandler);
|
||||
$document.on('mouseup', onClickOutsideElement);
|
||||
scope.$on('$destroy', function() {
|
||||
$document.off('mouseup', onClickOutsideElement);
|
||||
$document.off('keyup', onEscapeHandler);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
27
www/analytics/plugins/CoreHome/angularjs/common/directives/focusif.js
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* If the given expression evaluates to true the element will be focussed
|
||||
*
|
||||
* Example:
|
||||
* <input type="text" piwik-focus-if="view.editName">
|
||||
*/
|
||||
angular.module('piwikApp.directive').directive('piwikFocusIf', function($timeout) {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, element, attrs) {
|
||||
scope.$watch(attrs.piwikFocusIf, function(newValue, oldValue) {
|
||||
if (newValue) {
|
||||
$timeout(function () {
|
||||
element[0].focus();
|
||||
}, 5);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
21
www/analytics/plugins/CoreHome/angularjs/common/directives/ignore-click.js
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Prevents the default behavior of the click. For instance useful if a link should only work in case the user
|
||||
* does a "right click open in new window".
|
||||
*
|
||||
* Example
|
||||
* <a piwik-ignore-click ng-click="doSomething()" href="/">my link</a>
|
||||
*/
|
||||
angular.module('piwikApp.directive').directive('piwikIgnoreClick', function() {
|
||||
return function(scope, element, attrs) {
|
||||
$(element).click(function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
};
|
||||
});
|
||||
27
www/analytics/plugins/CoreHome/angularjs/common/directives/onenter.js
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allows you to define any expression to be executed in case the user presses enter
|
||||
*
|
||||
* Example
|
||||
* <div piwik-onenter="save()">
|
||||
* <div piwik-onenter="showList=false">
|
||||
*/
|
||||
angular.module('piwikApp.directive').directive('piwikOnenter', function() {
|
||||
return function(scope, element, attrs) {
|
||||
element.bind("keydown keypress", function(event) {
|
||||
if(event.which === 13) {
|
||||
scope.$apply(function(){
|
||||
scope.$eval(attrs.piwikOnenter, {'event': event});
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
44
www/analytics/plugins/CoreHome/angularjs/common/filters/evolution.js
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.filter').filter('evolution', function() {
|
||||
|
||||
function calculateEvolution(currentValue, pastValue)
|
||||
{
|
||||
pastValue = parseInt(pastValue, 10);
|
||||
currentValue = parseInt(currentValue, 10) - pastValue;
|
||||
|
||||
if (currentValue === 0 || isNaN(currentValue)) {
|
||||
evolution = 0;
|
||||
} else if (pastValue === 0 || isNaN(pastValue)) {
|
||||
evolution = 100;
|
||||
} else {
|
||||
evolution = (currentValue / pastValue) * 100;
|
||||
}
|
||||
|
||||
return evolution;
|
||||
}
|
||||
|
||||
function formatEvolution(evolution)
|
||||
{
|
||||
evolution = Math.round(evolution);
|
||||
|
||||
if (evolution > 0) {
|
||||
evolution = '+' + evolution;
|
||||
}
|
||||
|
||||
evolution += '%';
|
||||
|
||||
return evolution;
|
||||
}
|
||||
|
||||
return function(currentValue, pastValue) {
|
||||
var evolution = calculateEvolution(currentValue, pastValue);
|
||||
|
||||
return formatEvolution(evolution);
|
||||
};
|
||||
});
|
||||
7
www/analytics/plugins/CoreHome/angularjs/common/filters/filter.js
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
angular.module('piwikApp.filter', []);
|
||||
13
www/analytics/plugins/CoreHome/angularjs/common/filters/startfrom.js
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.filter').filter('startFrom', function() {
|
||||
return function(input, start) {
|
||||
start = +start; //parse to int
|
||||
return input.slice(start);
|
||||
};
|
||||
});
|
||||
40
www/analytics/plugins/CoreHome/angularjs/common/filters/startfrom_spec.js
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
describe('startFromFilter', function() {
|
||||
var startFrom;
|
||||
|
||||
beforeEach(module('piwikApp.filter'));
|
||||
beforeEach(inject(function($injector) {
|
||||
var $filter = $injector.get('$filter');
|
||||
startFrom = $filter('startFrom');
|
||||
}));
|
||||
|
||||
describe('#startFrom()', function() {
|
||||
|
||||
it('should return all entries if index is zero', function() {
|
||||
|
||||
var result = startFrom([1,2,3], 0);
|
||||
|
||||
expect(result).to.eql([1,2,3]);
|
||||
});
|
||||
|
||||
it('should return only partial entries if filter is higher than zero', function() {
|
||||
|
||||
var result = startFrom([1,2,3], 2);
|
||||
|
||||
expect(result).to.eql([3]);
|
||||
});
|
||||
|
||||
it('should return no entries if start is higher than input length', function() {
|
||||
|
||||
var result = startFrom([1,2,3], 11);
|
||||
|
||||
expect(result).to.eql([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
19
www/analytics/plugins/CoreHome/angularjs/common/filters/translate.js
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.filter').filter('translate', function() {
|
||||
|
||||
return function(key, value1, value2, value3) {
|
||||
var values = [];
|
||||
if (arguments && arguments.length > 1) {
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
values.push(arguments[index]);
|
||||
}
|
||||
}
|
||||
return _pk_translate(key, values);
|
||||
};
|
||||
});
|
||||
186
www/analytics/plugins/CoreHome/angularjs/common/services/piwik-api.js
vendored
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.service').factory('piwikApi', function ($http, $q, $rootScope, piwik, $window) {
|
||||
|
||||
var url = 'index.php';
|
||||
var format = 'json';
|
||||
var getParams = {};
|
||||
var postParams = {};
|
||||
var requestHandle = null;
|
||||
|
||||
var piwikApi = {};
|
||||
|
||||
/**
|
||||
* Adds params to the request.
|
||||
* If params are given more then once, the latest given value is used for the request
|
||||
*
|
||||
* @param {object} params
|
||||
* @return {void}
|
||||
*/
|
||||
function addParams (params) {
|
||||
if (typeof params == 'string') {
|
||||
params = piwik.broadcast.getValuesFromUrl(params);
|
||||
}
|
||||
|
||||
for (var key in params) {
|
||||
getParams[key] = params[key];
|
||||
}
|
||||
}
|
||||
|
||||
function reset () {
|
||||
getParams = {};
|
||||
postParams = {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the request
|
||||
* @return $promise
|
||||
*/
|
||||
function send () {
|
||||
|
||||
var deferred = $q.defer();
|
||||
var requestHandle = deferred;
|
||||
|
||||
var onError = function (message) {
|
||||
deferred.reject(message);
|
||||
requestHandle = null;
|
||||
};
|
||||
|
||||
var onSuccess = function (response) {
|
||||
if (response && response.result == 'error') {
|
||||
|
||||
if (response.message) {
|
||||
onError(response.message);
|
||||
|
||||
var UI = require('piwik/UI');
|
||||
var notification = new UI.Notification();
|
||||
notification.show(response.message, {
|
||||
context: 'error',
|
||||
type: 'toast',
|
||||
id: 'ajaxHelper'
|
||||
});
|
||||
notification.scrollToNotification();
|
||||
} else {
|
||||
onError(null);
|
||||
}
|
||||
|
||||
} else {
|
||||
deferred.resolve(response);
|
||||
}
|
||||
requestHandle = null;
|
||||
};
|
||||
|
||||
var headers = {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
// ie 8,9,10 caches ajax requests, prevent this
|
||||
'cache-control': 'no-cache'
|
||||
};
|
||||
|
||||
var ajaxCall = {
|
||||
method: 'POST',
|
||||
url: url,
|
||||
responseType: format,
|
||||
params: _mixinDefaultGetParams(getParams),
|
||||
data: $.param(getPostParams(postParams)),
|
||||
timeout: deferred.promise,
|
||||
headers: headers
|
||||
};
|
||||
|
||||
$http(ajaxCall).success(onSuccess).error(onError);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the parameters to send as POST
|
||||
*
|
||||
* @param {object} params parameter object
|
||||
* @return {object}
|
||||
* @private
|
||||
*/
|
||||
function getPostParams () {
|
||||
return {
|
||||
token_auth: piwik.token_auth
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin the default parameters to send as GET
|
||||
*
|
||||
* @param {object} getParamsToMixin parameter object
|
||||
* @return {object}
|
||||
* @private
|
||||
*/
|
||||
function _mixinDefaultGetParams (getParamsToMixin) {
|
||||
|
||||
var defaultParams = {
|
||||
idSite: piwik.idSite || piwik.broadcast.getValueFromUrl('idSite'),
|
||||
period: piwik.period || piwik.broadcast.getValueFromUrl('period'),
|
||||
segment: piwik.broadcast.getValueFromHash('segment', $window.location.href.split('#')[1])
|
||||
};
|
||||
|
||||
// never append token_auth to url
|
||||
if (getParamsToMixin.token_auth) {
|
||||
getParamsToMixin.token_auth = null;
|
||||
delete getParamsToMixin.token_auth;
|
||||
}
|
||||
|
||||
for (var key in defaultParams) {
|
||||
if (!getParamsToMixin[key] && !postParams[key] && defaultParams[key]) {
|
||||
getParamsToMixin[key] = defaultParams[key];
|
||||
}
|
||||
}
|
||||
|
||||
// handle default date & period if not already set
|
||||
if (!getParamsToMixin.date && !postParams.date) {
|
||||
getParamsToMixin.date = piwik.currentDateString || piwik.broadcast.getValueFromUrl('date');
|
||||
if (getParamsToMixin.period == 'range' && piwik.currentDateString) {
|
||||
getParamsToMixin.date = piwik.startDateString + ',' + getParamsToMixin.date;
|
||||
}
|
||||
}
|
||||
|
||||
return getParamsToMixin;
|
||||
}
|
||||
|
||||
piwikApi.abort = function () {
|
||||
reset();
|
||||
|
||||
if (requestHandle) {
|
||||
requestHandle.resolve();
|
||||
requestHandle = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Perform a reading API request.
|
||||
* @param getParams
|
||||
*/
|
||||
piwikApi.fetch = function (getParams) {
|
||||
|
||||
getParams.module = 'API';
|
||||
getParams.format = 'JSON';
|
||||
|
||||
addParams(getParams, 'GET');
|
||||
|
||||
var promise = send();
|
||||
|
||||
reset();
|
||||
|
||||
return promise;
|
||||
};
|
||||
|
||||
piwikApi.post = function (getParams, _postParams_) {
|
||||
if (_postParams_) {
|
||||
postParams = _postParams_;
|
||||
}
|
||||
|
||||
return this.fetch(getParams);
|
||||
};
|
||||
|
||||
return piwikApi;
|
||||
});
|
||||
13
www/analytics/plugins/CoreHome/angularjs/common/services/piwik.js
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.service').service('piwik', function () {
|
||||
|
||||
piwik.helper = piwikHelper;
|
||||
piwik.broadcast = broadcast;
|
||||
return piwik;
|
||||
});
|
||||
37
www/analytics/plugins/CoreHome/angularjs/common/services/piwik_spec.js
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
describe('piwikService', function() {
|
||||
var piwikService;
|
||||
|
||||
beforeEach(module('piwikApp.service'));
|
||||
beforeEach(inject(function($injector) {
|
||||
piwikService = $injector.get('piwik');
|
||||
}));
|
||||
|
||||
describe('#piwikService', function() {
|
||||
|
||||
it('should be the same as piwik global var', function() {
|
||||
piwik.should.equal(piwikService);
|
||||
});
|
||||
|
||||
it('should mixin broadcast', function() {
|
||||
expect(piwikService.broadcast).to.be.an('object');
|
||||
});
|
||||
|
||||
it('should mixin piwikHelper', function() {
|
||||
expect(piwikService.helper).to.be.an('object');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#piwik_url', function() {
|
||||
|
||||
it('should contain the piwik url', function() {
|
||||
expect(piwikService.piwik_url).to.eql('http://localhost/');
|
||||
});
|
||||
});
|
||||
});
|
||||
8
www/analytics/plugins/CoreHome/angularjs/common/services/service.js
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp.service', []);
|
||||
66
www/analytics/plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline-directive.js
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
*
|
||||
* <h2 piwik-enriched-headline>All Websites Dashboard</h2>
|
||||
* -> uses "All Websites Dashboard" as featurename
|
||||
*
|
||||
* <h2 piwik-enriched-headline feature-name="All Websites Dashboard">All Websites Dashboard (Total: 309 Visits)</h2>
|
||||
* -> custom featurename
|
||||
*
|
||||
* <h2 piwik-enriched-headline help-url="http://piwik.org/guide">All Websites Dashboard</h2>
|
||||
* -> shows help icon and links to external url
|
||||
*
|
||||
* <h2 piwik-enriched-headline>All Websites Dashboard
|
||||
* <div class="inlineHelp>My <strong>inline help</strong></div>
|
||||
* </h2>
|
||||
* -> shows help icon to display inline help on click. Note: You can combine inlinehelp and help-url
|
||||
*/
|
||||
angular.module('piwikApp').directive('piwikEnrichedHeadline', function($document, piwik, $filter){
|
||||
var defaults = {
|
||||
helpUrl: ''
|
||||
};
|
||||
|
||||
return {
|
||||
transclude: true,
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
helpUrl: '@',
|
||||
featureName: '@'
|
||||
},
|
||||
templateUrl: 'plugins/CoreHome/angularjs/enrichedheadline/enrichedheadline.html?cb=' + piwik.cacheBuster,
|
||||
compile: function (element, attrs) {
|
||||
|
||||
for (var index in defaults) {
|
||||
if (!attrs[index]) { attrs[index] = defaults[index]; }
|
||||
}
|
||||
|
||||
return function (scope, element, attrs) {
|
||||
|
||||
var helpNode = $('[ng-transclude] .inlineHelp', element);
|
||||
|
||||
if ((!helpNode || !helpNode.length) && element.next()) {
|
||||
// hack for reports :(
|
||||
helpNode = element.next().find('.reportDocumentation');
|
||||
}
|
||||
|
||||
if (helpNode && helpNode.length) {
|
||||
if ($.trim(helpNode.text())) {
|
||||
scope.inlineHelp = $.trim(helpNode.html());
|
||||
}
|
||||
helpNode.remove();
|
||||
}
|
||||
|
||||
if (!attrs.featureName) {
|
||||
attrs.featureName = $.trim(element.text());
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<div class="enrichedHeadline"
|
||||
ng-mouseenter="view.showIcons=true" ng-mouseleave="view.showIcons=false">
|
||||
<span ng-transclude></span>
|
||||
|
||||
<span ng-show="view.showIcons">
|
||||
<a ng-if="helpUrl && !inlineHelp"
|
||||
target="_blank"
|
||||
href="{{ helpUrl }}"
|
||||
title="{{ 'CoreHome_ExternalHelp'|translate }}"
|
||||
class="helpIcon"></a>
|
||||
|
||||
<a ng-if="inlineHelp"
|
||||
title="{{ 'General_Help'|translate }}"
|
||||
ng-click="view.showInlineHelp=!view.showInlineHelp"
|
||||
class="helpIcon"></a>
|
||||
|
||||
<div class="ratingIcons"
|
||||
piwik-rate-feature
|
||||
title="{{ featureName }}"></div>
|
||||
</span>
|
||||
|
||||
<div class="inlineHelp" ng-show="view.showIcons && view.showInlineHelp">
|
||||
<div ng-bind-html="inlineHelp"></div>
|
||||
<a ng-if="helpUrl"
|
||||
target="_blank"
|
||||
href="{{ helpUrl }}"
|
||||
class="readMore">{{ 'General_MoreDetails'|translate }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
.inlineHelp {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.enrichedHeadline {
|
||||
min-height: 22px;
|
||||
|
||||
.inlineHelp {
|
||||
display:block;
|
||||
background: #F7F7F7;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
border: 1px solid #E4E5E4;
|
||||
margin: 10px 0 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
max-width: 500px;
|
||||
|
||||
.readMore {
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.ratingIcons {
|
||||
display:inline-block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.helpIcon:hover {
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.helpIcon {
|
||||
cursor: pointer;
|
||||
display:inline-block;
|
||||
margin: 0px 0px -1px 4px;
|
||||
width: 16px;
|
||||
opacity: 0.3;
|
||||
height: 16px;
|
||||
background: url(plugins/CoreHome/angularjs/enrichedheadline/help.png) no-repeat;
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 350 B |
16
www/analytics/plugins/CoreHome/angularjs/piwikApp.js
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp', [
|
||||
'ngSanitize',
|
||||
'ngAnimate',
|
||||
'piwikApp.config',
|
||||
'piwikApp.service',
|
||||
'piwikApp.directive',
|
||||
'piwikApp.filter'
|
||||
]);
|
||||
angular.module('app', []);
|
||||
9
www/analytics/plugins/CoreHome/angularjs/piwikAppConfig.js
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
angular.module('piwikApp.config', []);
|
||||
|
||||
(function () {
|
||||
var piwikAppConfig = angular.module('piwikApp.config');
|
||||
// we probably want this later as a separate config file, till then it serves as a "bridge"
|
||||
for (var index in piwik.config) {
|
||||
piwikAppConfig.constant(index.toUpperCase(), piwik.config[index]);
|
||||
}
|
||||
})();
|
||||
49
www/analytics/plugins/CoreHome/angularjs/siteselector/siteselector-controller.js
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp').controller('SiteSelectorController', function($scope, siteSelectorModel, piwik, AUTOCOMPLETE_MIN_SITES){
|
||||
|
||||
$scope.model = siteSelectorModel;
|
||||
|
||||
$scope.autocompleteMinSites = AUTOCOMPLETE_MIN_SITES;
|
||||
$scope.selectedSite = {id: '', name: ''};
|
||||
$scope.activeSiteId = piwik.idSite;
|
||||
|
||||
$scope.switchSite = function (site) {
|
||||
$scope.selectedSite.id = site.idsite;
|
||||
|
||||
if (site.name === $scope.allSitesText) {
|
||||
$scope.selectedSite.name = $scope.allSitesText;
|
||||
} else {
|
||||
$scope.selectedSite.name = site.name.replace(/[\u0000-\u2666]/g, function(c) {
|
||||
return '&#'+c.charCodeAt(0)+';';
|
||||
});
|
||||
}
|
||||
|
||||
if (!$scope.switchSiteOnSelect || $scope.activeSiteId == site.idsite) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (site.idsite == 'all') {
|
||||
piwik.broadcast.propagateNewPage('module=MultiSites&action=index');
|
||||
} else {
|
||||
piwik.broadcast.propagateNewPage('segment=&idSite=' + site.idsite, false);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.getUrlAllSites = function () {
|
||||
var newParameters = 'module=MultiSites&action=index';
|
||||
return piwik.helper.getCurrentQueryStringWithParametersModified(newParameters);
|
||||
};
|
||||
$scope.getUrlForSiteId = function (idSite) {
|
||||
var idSiteParam = 'idSite=' + idSite;
|
||||
var newParameters = 'segment=&' + idSiteParam;
|
||||
var hash = piwik.broadcast.isHashExists() ? piwik.broadcast.getHashFromUrl() : "";
|
||||
return piwik.helper.getCurrentQueryStringWithParametersModified(newParameters) +
|
||||
'#' + piwik.helper.getQueryStringWithParametersModified(hash.substring(1), newParameters);
|
||||
};
|
||||
});
|
||||
80
www/analytics/plugins/CoreHome/angularjs/siteselector/siteselector-directive.js
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* Usage:
|
||||
* <div piwik-siteselector>
|
||||
*
|
||||
* More advanced example
|
||||
* <div piwik-siteselector
|
||||
* show-selected-site="true" show-all-sites-item="true" switch-site-on-select="true"
|
||||
* all-sites-location="top|bottom" all-sites-text="test" show-selected-site="true"
|
||||
* show-all-sites-item="true">
|
||||
*
|
||||
* Within a form
|
||||
* <div piwik-siteselector input-name="siteId">
|
||||
*
|
||||
* Events:
|
||||
* Triggers a `change` event on any change
|
||||
* <div piwik-siteselector id="mySelector">
|
||||
* $('#mySelector').on('change', function (event) { event.id/event.name })
|
||||
*/
|
||||
angular.module('piwikApp').directive('piwikSiteselector', function($document, piwik, $filter){
|
||||
var defaults = {
|
||||
name: '',
|
||||
siteid: piwik.idSite,
|
||||
sitename: piwik.siteName,
|
||||
allSitesLocation: 'bottom',
|
||||
allSitesText: $filter('translate')('General_MultiSitesSummary'),
|
||||
showSelectedSite: 'false',
|
||||
showAllSitesItem: 'true',
|
||||
switchSiteOnSelect: 'true'
|
||||
};
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
showSelectedSite: '=',
|
||||
showAllSitesItem: '=',
|
||||
switchSiteOnSelect: '=',
|
||||
inputName: '@name',
|
||||
allSitesText: '@',
|
||||
allSitesLocation: '@'
|
||||
},
|
||||
templateUrl: 'plugins/CoreHome/angularjs/siteselector/siteselector.html?cb=' + piwik.cacheBuster,
|
||||
controller: 'SiteSelectorController',
|
||||
compile: function (element, attrs) {
|
||||
|
||||
for (var index in defaults) {
|
||||
if (!attrs[index]) { attrs[index] = defaults[index]; }
|
||||
}
|
||||
|
||||
return function (scope, element, attrs) {
|
||||
|
||||
// selectedSite.id|.name + model is hard-coded but actually the directive should not know about this
|
||||
scope.selectedSite.id = attrs.siteid;
|
||||
scope.selectedSite.name = attrs.sitename;
|
||||
|
||||
if (!attrs.siteid || !attrs.sitename) {
|
||||
scope.model.loadInitialSites();
|
||||
}
|
||||
|
||||
scope.$watch('selectedSite.id', function (newValue, oldValue, scope) {
|
||||
if (newValue != oldValue) {
|
||||
element.attr('siteid', newValue);
|
||||
element.trigger('change', scope.selectedSite);
|
||||
}
|
||||
});
|
||||
|
||||
/** use observe to monitor attribute changes
|
||||
attrs.$observe('maxsitenamewidth', function(val) {
|
||||
// for instance trigger a function or whatever
|
||||
}) */
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
75
www/analytics/plugins/CoreHome/angularjs/siteselector/siteselector-model.js
vendored
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
angular.module('piwikApp').factory('siteSelectorModel', function (piwikApi, $filter) {
|
||||
|
||||
var model = {};
|
||||
model.sites = [];
|
||||
model.hasMultipleWebsites = false;
|
||||
model.isLoading = false;
|
||||
model.firstSiteName = '';
|
||||
|
||||
var initialSites = null;
|
||||
|
||||
model.updateWebsitesList = function (sites) {
|
||||
|
||||
if (!sites || !sites.length) {
|
||||
model.sites = [];
|
||||
return [];
|
||||
}
|
||||
|
||||
angular.forEach(sites, function (site) {
|
||||
if (site.group) site.name = '[' + site.group + '] ' + site.name;
|
||||
});
|
||||
|
||||
model.sites = $filter('orderBy')(sites, '+name');
|
||||
|
||||
if (!model.firstSiteName) {
|
||||
model.firstSiteName = model.sites[0].name;
|
||||
}
|
||||
|
||||
model.hasMultipleWebsites = model.hasMultipleWebsites || sites.length > 1;
|
||||
|
||||
return model.sites;
|
||||
};
|
||||
|
||||
model.searchSite = function (term) {
|
||||
|
||||
if (!term) {
|
||||
model.loadInitialSites();
|
||||
return;
|
||||
}
|
||||
|
||||
if (model.isLoading) {
|
||||
piwikApi.abort();
|
||||
}
|
||||
|
||||
model.isLoading = true;
|
||||
|
||||
return piwikApi.fetch({
|
||||
method: 'SitesManager.getPatternMatchSites',
|
||||
pattern: term
|
||||
}).then(function (response) {
|
||||
return model.updateWebsitesList(response);
|
||||
})['finally'](function () { // .finally() is not IE8 compatible see https://github.com/angular/angular.js/commit/f078762d48d0d5d9796dcdf2cb0241198677582c
|
||||
model.isLoading = false;
|
||||
});
|
||||
};
|
||||
|
||||
model.loadInitialSites = function () {
|
||||
if (initialSites) {
|
||||
model.sites = initialSites;
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchSite('%').then(function (websites) {
|
||||
initialSites = websites;
|
||||
});
|
||||
};
|
||||
|
||||
return model;
|
||||
});
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<div piwik-focus-anywhere-but-here="view.showSitesList=false" class="custom_select"
|
||||
ng-class="{'sites_autocomplete--dropdown': (model.hasMultipleWebsites || showAllSitesItem || !model.sites.length)}">
|
||||
|
||||
<script type="text/ng-template" id="siteselector_allsiteslink.html">
|
||||
<div ng-click="switchSite({idsite: 'all', name: allSitesText});view.showSitesList=false;"
|
||||
class="custom_select_all">
|
||||
<a href="{{ getUrlAllSites() }}"
|
||||
piwik-ignore-click
|
||||
ng-bind-html="allSitesText"></a>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<input ng-if="inputName" type="hidden" name="{{ inputName }}" ng-value="selectedSite.id"/>
|
||||
|
||||
<a ng-click="view.showSitesList=!view.showSitesList; view.showSitesList && model.loadInitialSites()"
|
||||
href="javascript:void(0)"
|
||||
class="custom_select_main_link"
|
||||
ng-class="{'loading': model.isLoading}">
|
||||
<span ng-bind-html="selectedSite.name || model.firstSiteName">?</span>
|
||||
</a>
|
||||
|
||||
<div ng-show="view.showSitesList" class="custom_select_block">
|
||||
<div ng-if="allSitesLocation=='top' && showAllSitesItem"
|
||||
ng-include="'siteselector_allsiteslink.html'"></div>
|
||||
|
||||
<div class="custom_select_container">
|
||||
<ul class="custom_select_ul_list" ng-click="view.showSitesList=false;">
|
||||
<li ng-click="switchSite(site)"
|
||||
ng-repeat="site in model.sites"
|
||||
ng-hide="!showSelectedSite && activeSiteId==site.idsite">
|
||||
<a piwik-ignore-click href="{{ getUrlForSiteId(site.idsite) }}"
|
||||
piwik-autocomplete-matched="view.searchTerm">{{ site.name }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul ng-show="!model.sites.length && view.searchTerm" class="ui-autocomplete ui-front ui-menu ui-widget ui-widget-content ui-corner-all siteSelect">
|
||||
<li class="ui-menu-item">
|
||||
<a class="ui-corner-all" tabindex="-1">{{ ('SitesManager_NotFound' | translate) + ' ' + view.searchTerm }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div ng-if="allSitesLocation=='bottom' && showAllSitesItem"
|
||||
ng-include="'siteselector_allsiteslink.html'"></div>
|
||||
|
||||
<div class="custom_select_search" ng-show="autocompleteMinSites <= model.sites.length || view.searchTerm">
|
||||
<input type="text"
|
||||
ng-click="view.searchTerm=''"
|
||||
ng-model="view.searchTerm"
|
||||
ng-change="model.searchSite(view.searchTerm)"
|
||||
class="websiteSearch inp"/>
|
||||
<input type="submit"
|
||||
ng-click="model.searchSite(view.searchTerm)"
|
||||
value="{{ 'General_Search' | translate }}" class="but"/>
|
||||
<img title="Clear"
|
||||
ng-show="view.searchTerm"
|
||||
ng-click="view.searchTerm=''; model.loadInitialSites()"
|
||||
class="reset"
|
||||
src="plugins/CoreHome/images/reset_search.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
|
||||
|
||||
/*sites_autocomplete*/
|
||||
.sites_autocomplete {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
display: inline-block;
|
||||
height: 30px; /* Hack to not push the dashboard widget below */
|
||||
}
|
||||
|
||||
.sites_selector_in_dashboard {
|
||||
margin-top:10px;
|
||||
}
|
||||
.top_bar_sites_selector {
|
||||
float: right
|
||||
}
|
||||
|
||||
.top_bar_sites_selector > label {
|
||||
display: inline-block;
|
||||
padding: 7px 0 6px 0;
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.top_bar_sites_selector > .sites_autocomplete {
|
||||
position: static;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.autocompleteMatched {
|
||||
color: #5256BE;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select {
|
||||
float: left;
|
||||
position: relative;
|
||||
z-index: 19;
|
||||
background: #fff url(plugins/Zeitgeist/images/sites_selection.png) repeat-x 0 0;
|
||||
border: 1px solid #d4d4d4;
|
||||
color: #255792;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
min-width: 165px;
|
||||
padding: 5px 6px 4px;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_main_link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
background: none;
|
||||
cursor: default;
|
||||
height:1.4em;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_ul_list li a,
|
||||
.sites_autocomplete .custom_select_all a,
|
||||
.sites_autocomplete .custom_select_main_link > span {
|
||||
display: inline-block;
|
||||
max-width: 140px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0 20px 0 4px;
|
||||
}
|
||||
|
||||
.sites_autocomplete--dropdown .custom_select_main_link:not(.loading):before {
|
||||
content: " \25BC";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
font-size: 0.8em;
|
||||
margin-top: 0.2em;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.sites_autocomplete--dropdown .custom_select_main_link {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_main_link.loading {
|
||||
background: url(plugins/Zeitgeist/images/loading-blue.gif) no-repeat right 3px;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_ul_list,
|
||||
.sites_autocomplete ul.ui-autocomplete {
|
||||
position: relative;
|
||||
list-style: none;
|
||||
line-height: 18px;
|
||||
padding: 0 0 15px 0;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_ul_list li a,
|
||||
.sites_autocomplete .custom_select_all a {
|
||||
line-height: 18px;
|
||||
height: auto;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_ul_list li a:hover,
|
||||
.sites_autocomplete .custom_select_all a:hover {
|
||||
background: #ebeae6;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_all a {
|
||||
text-decoration: none;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_search {
|
||||
margin: 0 0 0 4px;
|
||||
height: 26px;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
background: url(plugins/Zeitgeist/images/search_bg.png) no-repeat 0 0;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_search .inp {
|
||||
vertical-align: top;
|
||||
width: 114px;
|
||||
padding: 2px 6px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
font-size: 10px;
|
||||
color: #454545;
|
||||
|
||||
}
|
||||
.sites_autocomplete {
|
||||
width: 165px;
|
||||
}
|
||||
|
||||
.sites_autocomplete .custom_select_search .but {
|
||||
vertical-align: top;
|
||||
font-size: 10px;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
width: 21px;
|
||||
height: 17px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sites_selector_container>.sites_autocomplete {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.custom_selector_container .ui-menu-item,
|
||||
.custom_selector_container .ui-menu-item a {
|
||||
float:none;position:static
|
||||
}
|
||||
|
||||
.custom_select_search .reset {
|
||||
position: relative; top: 4px; left: -44px; cursor: pointer;
|
||||
}
|
||||
|
||||
.custom_select_block {
|
||||
overflow: hidden;
|
||||
max-width: inherit;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.custom_select_block_show {
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
max-width:inherit;
|
||||
}
|
||||
|
||||
.sites_selector_container {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.siteSelect a {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
}
|
||||
BIN
www/analytics/plugins/CoreHome/images/bg_header.jpg
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
www/analytics/plugins/CoreHome/images/bullet1.gif
Normal file
|
After Width: | Height: | Size: 52 B |
BIN
www/analytics/plugins/CoreHome/images/bullet2.gif
Normal file
|
After Width: | Height: | Size: 52 B |
BIN
www/analytics/plugins/CoreHome/images/favicon.ico
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
www/analytics/plugins/CoreHome/images/googleplay.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
www/analytics/plugins/CoreHome/images/more.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
www/analytics/plugins/CoreHome/images/more_date.gif
Normal file
|
After Width: | Height: | Size: 56 B |
BIN
www/analytics/plugins/CoreHome/images/more_period.gif
Normal file
|
After Width: | Height: | Size: 53 B |
BIN
www/analytics/plugins/CoreHome/images/promo_splash.png
Executable file
|
After Width: | Height: | Size: 12 KiB |
BIN
www/analytics/plugins/CoreHome/images/reset_search.png
Normal file
|
After Width: | Height: | Size: 1,021 B |
BIN
www/analytics/plugins/CoreHome/images/search.png
Normal file
|
After Width: | Height: | Size: 136 B |
644
www/analytics/plugins/CoreHome/javascripts/broadcast.js
Normal file
|
|
@ -0,0 +1,644 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* broadcast object is to help maintain a hash for link clicks and ajax calls
|
||||
* so we can have back button and refresh button working.
|
||||
*
|
||||
* @type {object}
|
||||
*/
|
||||
var broadcast = {
|
||||
|
||||
/**
|
||||
* Initialisation state
|
||||
* @type {Boolean}
|
||||
*/
|
||||
_isInit: false,
|
||||
|
||||
/**
|
||||
* Last known hash url without popover parameter
|
||||
*/
|
||||
currentHashUrl: false,
|
||||
|
||||
/**
|
||||
* Last known popover parameter
|
||||
*/
|
||||
currentPopoverParameter: false,
|
||||
|
||||
/**
|
||||
* Callbacks for popover parameter change
|
||||
*/
|
||||
popoverHandlers: [],
|
||||
|
||||
/**
|
||||
* Force reload once
|
||||
*/
|
||||
forceReload: false,
|
||||
|
||||
/**
|
||||
* Suppress content update on hash changing
|
||||
*/
|
||||
updateHashOnly: false,
|
||||
|
||||
/**
|
||||
* Initializes broadcast object
|
||||
* @return {void}
|
||||
*/
|
||||
init: function (noLoadingMessage) {
|
||||
if (broadcast._isInit) {
|
||||
return;
|
||||
}
|
||||
broadcast._isInit = true;
|
||||
|
||||
// Initialize history plugin.
|
||||
// The callback is called at once by present location.hash
|
||||
$.history.init(broadcast.pageload, {unescape: true});
|
||||
|
||||
if(noLoadingMessage != true) {
|
||||
piwikHelper.showAjaxLoading();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* ========== PageLoad function =================
|
||||
* This function is called when:
|
||||
* 1. after calling $.history.init();
|
||||
* 2. after calling $.history.load(); //look at broadcast.changeParameter();
|
||||
* 3. after pushing "Go Back" button of a browser
|
||||
*
|
||||
* * Note: the method is manipulated in Overlay/javascripts/Piwik_Overlay.js - keep this in mind when making changes.
|
||||
*
|
||||
* @param {string} hash to load page with
|
||||
* @return {void}
|
||||
*/
|
||||
pageload: function (hash) {
|
||||
broadcast.init();
|
||||
|
||||
// Unbind any previously attached resize handlers
|
||||
$(window).off('resize');
|
||||
|
||||
// do not update content if it should be suppressed
|
||||
if (broadcast.updateHashOnly) {
|
||||
broadcast.updateHashOnly = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// hash doesn't contain the first # character.
|
||||
if (hash && 0 === (''+hash).indexOf('/')) {
|
||||
hash = (''+hash).substr(1);
|
||||
}
|
||||
|
||||
if (hash) {
|
||||
|
||||
if (/^popover=/.test(hash)) {
|
||||
var hashParts = [
|
||||
'',
|
||||
hash.replace(/^popover=/, '')
|
||||
];
|
||||
} else {
|
||||
var hashParts = hash.split('&popover=');
|
||||
}
|
||||
var hashUrl = hashParts[0];
|
||||
var popoverParam = '';
|
||||
if (hashParts.length > 1) {
|
||||
popoverParam = hashParts[1];
|
||||
// in case the $ was encoded (e.g. when using copy&paste on urls in some browsers)
|
||||
popoverParam = decodeURIComponent(popoverParam);
|
||||
// revert special encoding from broadcast.propagateNewPopoverParameter()
|
||||
popoverParam = popoverParam.replace(/\$/g, '%');
|
||||
popoverParam = decodeURIComponent(popoverParam);
|
||||
}
|
||||
|
||||
var pageUrlUpdated = (popoverParam == '' ||
|
||||
(broadcast.currentHashUrl !== false && broadcast.currentHashUrl != hashUrl));
|
||||
|
||||
var popoverParamUpdated = (popoverParam != '' && hashUrl == broadcast.currentHashUrl);
|
||||
|
||||
if (broadcast.currentHashUrl === false) {
|
||||
// new page load
|
||||
pageUrlUpdated = true;
|
||||
popoverParamUpdated = (popoverParam != '');
|
||||
}
|
||||
|
||||
if (pageUrlUpdated || broadcast.forceReload) {
|
||||
Piwik_Popover.close();
|
||||
|
||||
if (hashUrl != broadcast.currentHashUrl || broadcast.forceReload) {
|
||||
// restore ajax loaded state
|
||||
broadcast.loadAjaxContent(hashUrl);
|
||||
|
||||
// make sure the "Widgets & Dashboard" is deleted on reload
|
||||
$('.top_controls .dashboard-manager').hide();
|
||||
$('#dashboardWidgetsArea').dashboard('destroy');
|
||||
|
||||
// remove unused controls
|
||||
require('piwik/UI').UIControl.cleanupUnusedControls();
|
||||
}
|
||||
}
|
||||
|
||||
broadcast.forceReload = false;
|
||||
broadcast.currentHashUrl = hashUrl;
|
||||
broadcast.currentPopoverParameter = popoverParam;
|
||||
|
||||
if (popoverParamUpdated && popoverParam == '') {
|
||||
Piwik_Popover.close();
|
||||
} else if (popoverParamUpdated) {
|
||||
var popoverParamParts = popoverParam.split(':');
|
||||
var handlerName = popoverParamParts[0];
|
||||
popoverParamParts.shift();
|
||||
var param = popoverParamParts.join(':');
|
||||
if (typeof broadcast.popoverHandlers[handlerName] != 'undefined') {
|
||||
broadcast.popoverHandlers[handlerName](param);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// start page
|
||||
Piwik_Popover.close();
|
||||
|
||||
$('.pageWrap #content:not(.admin)').empty();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* propagateAjax -- update hash values then make ajax calls.
|
||||
* example :
|
||||
* 1) <a href="javascript:broadcast.propagateAjax('module=Referrers&action=getKeywords')">View keywords report</a>
|
||||
* 2) Main menu li also goes through this function.
|
||||
*
|
||||
* Will propagate your new value into the current hash string and make ajax calls.
|
||||
*
|
||||
* NOTE: this method will only make ajax call and replacing main content.
|
||||
*
|
||||
* @param {string} ajaxUrl querystring with parameters to be updated
|
||||
* @param {boolean} [disableHistory] the hash change won't be available in the browser history
|
||||
* @return {void}
|
||||
*/
|
||||
propagateAjax: function (ajaxUrl, disableHistory) {
|
||||
broadcast.init();
|
||||
|
||||
// abort all existing ajax requests
|
||||
globalAjaxQueue.abort();
|
||||
|
||||
// available in global scope
|
||||
var currentHashStr = broadcast.getHash();
|
||||
|
||||
ajaxUrl = ajaxUrl.replace(/^\?|&#/, '');
|
||||
|
||||
var params_vals = ajaxUrl.split("&");
|
||||
for (var i = 0; i < params_vals.length; i++) {
|
||||
currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr);
|
||||
}
|
||||
|
||||
// if the module is not 'Goals', we specifically unset the 'idGoal' parameter
|
||||
// this is to ensure that the URLs are clean (and that clicks on graphs work as expected - they are broken with the extra parameter)
|
||||
var action = broadcast.getParamValue('action', currentHashStr);
|
||||
if (action != 'goalReport' && action != 'ecommerceReport') {
|
||||
currentHashStr = broadcast.updateParamValue('idGoal=', currentHashStr);
|
||||
}
|
||||
// unset idDashboard if use doesn't display a dashboard
|
||||
var module = broadcast.getParamValue('module', currentHashStr);
|
||||
if (module != 'Dashboard') {
|
||||
currentHashStr = broadcast.updateParamValue('idDashboard=', 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
|
||||
window.location.replace(newLocation);
|
||||
}
|
||||
else {
|
||||
// Let history know about this new Hash and load it.
|
||||
broadcast.forceReload = true;
|
||||
$.history.load(currentHashStr);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* propagateNewPage() -- update url value and load new page,
|
||||
* Example:
|
||||
* 1) We want to update idSite to both search query and hash then reload the page,
|
||||
* 2) update period to both search query and hash then reload page.
|
||||
*
|
||||
* ** If you'd like to make ajax call with new values then use propagateAjax ** *
|
||||
*
|
||||
* Expecting:
|
||||
* str = "param1=newVal1¶m2=newVal2";
|
||||
*
|
||||
* NOTE: This method will refresh the page with new values.
|
||||
*
|
||||
* @param {string} str url with parameters to be updated
|
||||
* @param {boolean} [showAjaxLoading] whether to show the ajax loading gif or not.
|
||||
* @return {void}
|
||||
*/
|
||||
propagateNewPage: function (str, showAjaxLoading) {
|
||||
// abort all existing ajax requests
|
||||
globalAjaxQueue.abort();
|
||||
|
||||
if (typeof showAjaxLoading === 'undefined' || showAjaxLoading) {
|
||||
piwikHelper.showAjaxLoading();
|
||||
}
|
||||
|
||||
var params_vals = str.split("&");
|
||||
|
||||
// available in global scope
|
||||
var currentSearchStr = window.location.search;
|
||||
var currentHashStr = broadcast.getHashFromUrl();
|
||||
var oldUrl = currentSearchStr + currentHashStr;
|
||||
|
||||
for (var i = 0; i < params_vals.length; i++) {
|
||||
// update both the current search query and hash string
|
||||
currentSearchStr = broadcast.updateParamValue(params_vals[i], currentSearchStr);
|
||||
|
||||
if (currentHashStr.length != 0) {
|
||||
currentHashStr = broadcast.updateParamValue(params_vals[i], currentHashStr);
|
||||
}
|
||||
}
|
||||
|
||||
// Now load the new page.
|
||||
var newUrl = currentSearchStr + currentHashStr;
|
||||
|
||||
if (oldUrl == newUrl) {
|
||||
window.location.reload();
|
||||
} else {
|
||||
this.forceReload = true;
|
||||
window.location.href = newUrl;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/*************************************************
|
||||
*
|
||||
* Broadcast Supporter Methods:
|
||||
*
|
||||
*************************************************/
|
||||
|
||||
/**
|
||||
* updateParamValue(newParamValue,urlStr) -- Helping propagate functions to update value to url string.
|
||||
* eg. I want to update date value to search query or hash query
|
||||
*
|
||||
* Expecting:
|
||||
* urlStr : A Hash or search query string. e.g: module=whatever&action=index=date=yesterday
|
||||
* newParamValue : A param value pair: e.g: date=2009-05-02
|
||||
*
|
||||
* Return module=whatever&action=index&date=2009-05-02
|
||||
*
|
||||
* @param {string} newParamValue param to be updated
|
||||
* @param {string} urlStr url to be updated
|
||||
* @return {string} urlStr with updated param
|
||||
*/
|
||||
updateParamValue: function (newParamValue, urlStr) {
|
||||
var p_v = newParamValue.split("=");
|
||||
|
||||
var paramName = p_v[0];
|
||||
var valFromUrl = broadcast.getParamValue(paramName, urlStr);
|
||||
// if set 'idGoal=' then we remove the parameter from the URL automatically (rather than passing an empty value)
|
||||
var paramValue = p_v[1];
|
||||
if (paramValue == '') {
|
||||
newParamValue = '';
|
||||
}
|
||||
var getQuotedRegex = function(str) {
|
||||
return (str+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
|
||||
};
|
||||
|
||||
if (valFromUrl != '') {
|
||||
// replacing current param=value to newParamValue;
|
||||
valFromUrl = getQuotedRegex(valFromUrl);
|
||||
var regToBeReplace = new RegExp(paramName + '=' + valFromUrl, 'ig');
|
||||
if (newParamValue == '') {
|
||||
// if new value is empty remove leading &, aswell
|
||||
regToBeReplace = new RegExp('[\&]?' + paramName + '=' + valFromUrl, 'ig');
|
||||
}
|
||||
urlStr = urlStr.replace(regToBeReplace, newParamValue);
|
||||
} else if (newParamValue != '') {
|
||||
urlStr += (urlStr == '') ? newParamValue : '&' + newParamValue;
|
||||
}
|
||||
|
||||
return urlStr;
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads a popover by adding a 'popover' query parameter to the current URL and
|
||||
* indirectly executing the popover handler.
|
||||
*
|
||||
* This function should be called to open popovers that can be opened by URL alone.
|
||||
* That is, if you want users to be able to copy-paste the URL displayed when a popover
|
||||
* is open into a new browser window/tab and have the same popover open, you should
|
||||
* call this function.
|
||||
*
|
||||
* In order for this function to open a popover, there must be a popover handler
|
||||
* associated with handlerName. To associate one, call broadcast.addPopoverHandler.
|
||||
*
|
||||
* @param {String} handlerName The name of the popover handler.
|
||||
* @param {String} value The String value that should be passed to the popover
|
||||
* handler.
|
||||
*/
|
||||
propagateNewPopoverParameter: function (handlerName, value) {
|
||||
// init broadcast if not already done (it is required to make popovers work in widgetize mode)
|
||||
broadcast.init(true);
|
||||
|
||||
var hash = broadcast.getHashFromUrl(window.location.href);
|
||||
|
||||
var popover = '';
|
||||
if (handlerName) {
|
||||
popover = handlerName + ':' + value;
|
||||
|
||||
// between jquery.history and different browser bugs, it's impossible to ensure
|
||||
// that the parameter is en- and decoded the same number of times. in order to
|
||||
// make sure it doesn't change, we have to manipulate the url encoding a bit.
|
||||
popover = encodeURIComponent(popover);
|
||||
popover = popover.replace(/%/g, '\$');
|
||||
}
|
||||
|
||||
if ('' == value || 'undefined' == typeof value) {
|
||||
var newHash = hash.replace(/(&?popover=.*)/, '');
|
||||
} else if (broadcast.getParamValue('popover', hash)) {
|
||||
var newHash = broadcast.updateParamValue('popover='+popover, hash);
|
||||
} else if (hash && hash != '#') {
|
||||
var newHash = hash + '&popover=' + popover
|
||||
} else {
|
||||
var newHash = '#popover='+popover;
|
||||
}
|
||||
|
||||
// never use an empty hash, as that might reload the page
|
||||
if ('' == newHash) {
|
||||
newHash = '#';
|
||||
}
|
||||
|
||||
broadcast.forceReload = false;
|
||||
$.history.load(newHash);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a handler for the 'popover' query parameter.
|
||||
*
|
||||
* @see broadcast#propagateNewPopoverParameter
|
||||
*
|
||||
* @param {String} handlerName The handler name, eg, 'visitorProfile'. Should identify
|
||||
* the popover that the callback will open up.
|
||||
* @param {Function} callback This function should open the popover. It should take
|
||||
* one string parameter.
|
||||
*/
|
||||
addPopoverHandler: function (handlerName, callback) {
|
||||
broadcast.popoverHandlers[handlerName] = callback;
|
||||
},
|
||||
|
||||
/**
|
||||
* Loads the given url with ajax and replaces the content
|
||||
*
|
||||
* Note: the method is replaced in Overlay/javascripts/Piwik_Overlay.js - keep this in mind when making changes.
|
||||
*
|
||||
* @param {string} urlAjax url to load
|
||||
* @return {Boolean}
|
||||
*/
|
||||
loadAjaxContent: function (urlAjax) {
|
||||
if (typeof piwikMenu !== 'undefined') {
|
||||
piwikMenu.activateMenu(
|
||||
broadcast.getParamValue('module', urlAjax),
|
||||
broadcast.getParamValue('action', urlAjax),
|
||||
broadcast.getParamValue('idGoal', urlAjax) || broadcast.getParamValue('idDashboard', urlAjax)
|
||||
);
|
||||
}
|
||||
|
||||
piwikHelper.hideAjaxError('loadingError');
|
||||
piwikHelper.showAjaxLoading();
|
||||
$('#content').empty();
|
||||
$("object").remove();
|
||||
|
||||
urlAjax = urlAjax.match(/^\?/) ? urlAjax : "?" + urlAjax;
|
||||
broadcast.lastUrlRequested = urlAjax;
|
||||
function sectionLoaded(content) {
|
||||
// if content is whole HTML document, do not show it, otherwise recursive page load could occur
|
||||
var htmlDocType = '<!DOCTYPE';
|
||||
if (content.substring(0, htmlDocType.length) == htmlDocType) {
|
||||
// if the content has an error message, display it
|
||||
if ($(content).filter('title').text() == 'Piwik › Error') {
|
||||
content = $(content).filter('#contentsimple');
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (urlAjax == broadcast.lastUrlRequested) {
|
||||
$('#content').html(content).show();
|
||||
$(broadcast).trigger('locationChangeSuccess', {element: $('#content'), content: content});
|
||||
piwikHelper.hideAjaxLoading();
|
||||
broadcast.lastUrlRequested = null;
|
||||
|
||||
piwikHelper.compileAngularComponents('#content');
|
||||
}
|
||||
|
||||
initTopControls();
|
||||
}
|
||||
|
||||
var ajax = new ajaxHelper();
|
||||
ajax.setUrl(urlAjax);
|
||||
ajax.setErrorCallback(broadcast.customAjaxHandleError);
|
||||
ajax.setCallback(sectionLoaded);
|
||||
ajax.setFormat('html');
|
||||
ajax.send();
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Method to handle ajax errors
|
||||
* @param {XMLHttpRequest} deferred
|
||||
* @param {string} status
|
||||
* @return {void}
|
||||
*/
|
||||
customAjaxHandleError: function (deferred, status) {
|
||||
broadcast.lastUrlRequested = null;
|
||||
|
||||
// do not display error message if request was aborted
|
||||
if(status == 'abort') {
|
||||
return;
|
||||
}
|
||||
$('#loadingError').show();
|
||||
setTimeout( function(){
|
||||
$('#loadingError').fadeOut('slow');
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return hash string if hash exists on address bar.
|
||||
* else return false;
|
||||
*
|
||||
* @return {string|boolean} current hash or false if it is empty
|
||||
*/
|
||||
isHashExists: function () {
|
||||
var hashStr = broadcast.getHashFromUrl();
|
||||
|
||||
if (hashStr != "") {
|
||||
return hashStr;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Hash from given url or from current location.
|
||||
* return empty string if no hash present.
|
||||
*
|
||||
* @param {string} [url] url to get hash from (defaults to current location)
|
||||
* @return {string} the hash part of the given url
|
||||
*/
|
||||
getHashFromUrl: function (url) {
|
||||
var hashStr = "";
|
||||
// If url provided, give back the hash from url, else get hash from current address.
|
||||
if (url && url.match('#')) {
|
||||
hashStr = url.substring(url.indexOf("#"), url.length);
|
||||
}
|
||||
else {
|
||||
locationSplit = location.href.split('#');
|
||||
if(typeof locationSplit[1] != 'undefined') {
|
||||
hashStr = '#' + locationSplit[1];
|
||||
}
|
||||
}
|
||||
|
||||
return hashStr;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get search query from given url or from current location.
|
||||
* return empty string if no search query present.
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {string} the query part of the given url
|
||||
*/
|
||||
getSearchFromUrl: function (url) {
|
||||
var searchStr = "";
|
||||
// If url provided, give back the query string from url, else get query string from current address.
|
||||
if (url && url.match(/\?/)) {
|
||||
searchStr = url.substring(url.indexOf("?"), url.length);
|
||||
} else {
|
||||
searchStr = location.search;
|
||||
}
|
||||
|
||||
return searchStr;
|
||||
},
|
||||
|
||||
/**
|
||||
* Extracts from a query strings, the request array
|
||||
* @param queryString
|
||||
* @returns {object}
|
||||
*/
|
||||
extractKeyValuePairsFromQueryString: function (queryString) {
|
||||
var pairs = queryString.split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i != pairs.length; ++i) {
|
||||
// attn: split with regex has bugs in several browsers such as IE 8
|
||||
// so we need to split, use the first part as key and rejoin the rest
|
||||
var pair = pairs[i].split('=');
|
||||
var key = pair.shift();
|
||||
result[key] = pair.join('=');
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns all key-value pairs in query string of url.
|
||||
*
|
||||
* @param {string} url url to check. if undefined, null or empty, current url is used.
|
||||
* @return {object} key value pair describing query string parameters
|
||||
*/
|
||||
getValuesFromUrl: function (url) {
|
||||
var searchString = this._removeHashFromUrl(url).split('?')[1] || '';
|
||||
return this.extractKeyValuePairsFromQueryString(searchString);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* help to get param value for any given url string with provided param name
|
||||
* if no url is provided, it will get param from current address.
|
||||
* return:
|
||||
* Empty String if param is not found.
|
||||
*
|
||||
* @param {string} param parameter to search for
|
||||
* @param {string} [url] url to check, defaults to current location
|
||||
* @return {string} value of the given param within the given url
|
||||
*/
|
||||
getValueFromUrl: function (param, url) {
|
||||
var searchString = this._removeHashFromUrl(url);
|
||||
return broadcast.getParamValue(param, searchString);
|
||||
},
|
||||
|
||||
/**
|
||||
* NOTE: you should probably be using broadcast.getValueFromUrl instead!
|
||||
*
|
||||
* @param {string} param parameter to search for
|
||||
* @param {string} [url] url to check
|
||||
* @return {string} value of the given param within the hash part of the given url
|
||||
*/
|
||||
getValueFromHash: function (param, url) {
|
||||
var hashStr = broadcast.getHashFromUrl(url);
|
||||
if (hashStr.substr(0, 1) == '#') {
|
||||
hashStr = hashStr.substr(1);
|
||||
}
|
||||
hashStr = hashStr.split('#')[0];
|
||||
|
||||
return broadcast.getParamValue(param, hashStr);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* return value for the requested param, will return the first match.
|
||||
* out side of this class should use getValueFromHash() or getValueFromUrl() instead.
|
||||
* return:
|
||||
* Empty String if param is not found.
|
||||
*
|
||||
* @param {string} param parameter to search for
|
||||
* @param {string} url url to check
|
||||
* @return {string} value of the given param within the given url
|
||||
*/
|
||||
getParamValue: function (param, url) {
|
||||
var lookFor = param + '=';
|
||||
var startStr = url.indexOf(lookFor);
|
||||
|
||||
if (startStr >= 0) {
|
||||
var endStr = url.indexOf("&", startStr);
|
||||
if (endStr == -1) {
|
||||
endStr = url.length;
|
||||
}
|
||||
var value = url.substring(startStr + param.length + 1, endStr);
|
||||
|
||||
// we sanitize values to add a protection layer against XSS
|
||||
// &segment= value is not sanitized, since segments are designed to accept any user input
|
||||
if(param != 'segment') {
|
||||
value = value.replace(/[^_%~\*\+\-\<\>!@\$\.()=,;0-9a-zA-Z]/gi, '');
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the hash without the starting #
|
||||
* @return {string} hash part of the current url
|
||||
*/
|
||||
getHash: function () {
|
||||
return broadcast.getHashFromUrl().replace(/^#/, '').split('#')[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the hash portion of a URL and returns the rest.
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {string} url w/o hash
|
||||
*/
|
||||
_removeHashFromUrl: function (url) {
|
||||
var searchString = '';
|
||||
if (url) {
|
||||
var urlParts = url.split('#');
|
||||
searchString = urlParts[0];
|
||||
} else {
|
||||
searchString = location.search;
|
||||
}
|
||||
return searchString;
|
||||
}
|
||||
};
|
||||
545
www/analytics/plugins/CoreHome/javascripts/calendar.js
Normal file
|
|
@ -0,0 +1,545 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
Date.prototype.getWeek = function () {
|
||||
var onejan = new Date(this.getFullYear(), 0, 1), // needed for getDay()
|
||||
|
||||
// use UTC times since getTime() can differ based on user's timezone
|
||||
onejan_utc = Date.UTC(this.getFullYear(), 0, 1),
|
||||
this_utc = Date.UTC(this.getFullYear(), this.getMonth(), this.getDate()),
|
||||
|
||||
daysSinceYearStart = (this_utc - onejan_utc) / 86400000; // constant is millisecs in one day
|
||||
|
||||
return Math.ceil((daysSinceYearStart + onejan.getDay()) / 7);
|
||||
};
|
||||
|
||||
var currentYear, currentMonth, currentDay, currentDate, currentWeek;
|
||||
|
||||
function setCurrentDate(dateStr) {
|
||||
var splitDate = dateStr.split("-");
|
||||
currentYear = splitDate[0];
|
||||
currentMonth = splitDate[1] - 1;
|
||||
currentDay = splitDate[2];
|
||||
currentDate = new Date(currentYear, currentMonth, currentDay);
|
||||
currentWeek = currentDate.getWeek();
|
||||
}
|
||||
|
||||
if(!piwik.currentDateString) {
|
||||
// eg. Login form
|
||||
return;
|
||||
}
|
||||
setCurrentDate(piwik.currentDateString);
|
||||
|
||||
var todayDate = new Date;
|
||||
var todayMonth = todayDate.getMonth();
|
||||
var todayYear = todayDate.getFullYear();
|
||||
var todayDay = todayDate.getDate();
|
||||
|
||||
// min/max date for picker
|
||||
var piwikMinDate = new Date(piwik.minDateYear, piwik.minDateMonth - 1, piwik.minDateDay),
|
||||
piwikMaxDate = new Date(piwik.maxDateYear, piwik.maxDateMonth - 1, piwik.maxDateDay);
|
||||
|
||||
// we start w/ the current period
|
||||
var selectedPeriod = piwik.period;
|
||||
|
||||
function isDateInCurrentPeriod(date) {
|
||||
// if the selected period isn't the current period, don't highlight any dates
|
||||
if (selectedPeriod != piwik.period) {
|
||||
return [true, ''];
|
||||
}
|
||||
|
||||
var valid = false;
|
||||
|
||||
var dateMonth = date.getMonth();
|
||||
var dateYear = date.getFullYear();
|
||||
var dateDay = date.getDate();
|
||||
|
||||
// we don't color dates in the future
|
||||
if (dateMonth == todayMonth
|
||||
&& dateYear == todayYear
|
||||
&& dateDay > todayDay
|
||||
) {
|
||||
return [true, ''];
|
||||
}
|
||||
|
||||
// we don't color dates before the minimum date
|
||||
if (dateYear < piwik.minDateYear
|
||||
|| ( dateYear == piwik.minDateYear
|
||||
&&
|
||||
(
|
||||
(dateMonth == piwik.minDateMonth - 1
|
||||
&& dateDay < piwik.minDateDay)
|
||||
|| (dateMonth < piwik.minDateMonth - 1)
|
||||
)
|
||||
)
|
||||
) {
|
||||
return [true, ''];
|
||||
}
|
||||
|
||||
// we color all day of the month for the same year for the month period
|
||||
if (piwik.period == "month"
|
||||
&& dateMonth == currentMonth
|
||||
&& dateYear == currentYear
|
||||
) {
|
||||
valid = true;
|
||||
}
|
||||
// we color all day of the year for the year period
|
||||
else if (piwik.period == "year"
|
||||
&& dateYear == currentYear
|
||||
) {
|
||||
valid = true;
|
||||
}
|
||||
else if (piwik.period == "week"
|
||||
&& date.getWeek() == currentWeek
|
||||
&& dateYear == currentYear
|
||||
) {
|
||||
valid = true;
|
||||
}
|
||||
else if (piwik.period == "day"
|
||||
&& dateDay == currentDay
|
||||
&& dateMonth == currentMonth
|
||||
&& dateYear == currentYear
|
||||
) {
|
||||
valid = true;
|
||||
}
|
||||
|
||||
if (valid) {
|
||||
return [true, 'ui-datepicker-current-period'];
|
||||
}
|
||||
|
||||
return [true, ''];
|
||||
}
|
||||
|
||||
piwik.getBaseDatePickerOptions = function (defaultDate) {
|
||||
return {
|
||||
showOtherMonths: false,
|
||||
dateFormat: 'yy-mm-dd',
|
||||
firstDay: 1,
|
||||
minDate: piwikMinDate,
|
||||
maxDate: piwikMaxDate,
|
||||
prevText: "",
|
||||
nextText: "",
|
||||
currentText: "",
|
||||
defaultDate: defaultDate,
|
||||
changeMonth: true,
|
||||
changeYear: true,
|
||||
stepMonths: 1,
|
||||
// jquery-ui-i18n 1.7.2 lacks some translations, so we use our own
|
||||
dayNamesMin: [
|
||||
_pk_translate('General_DaySu'),
|
||||
_pk_translate('General_DayMo'),
|
||||
_pk_translate('General_DayTu'),
|
||||
_pk_translate('General_DayWe'),
|
||||
_pk_translate('General_DayTh'),
|
||||
_pk_translate('General_DayFr'),
|
||||
_pk_translate('General_DaySa')],
|
||||
dayNamesShort: [
|
||||
_pk_translate('General_ShortDay_7'), // start with sunday
|
||||
_pk_translate('General_ShortDay_1'),
|
||||
_pk_translate('General_ShortDay_2'),
|
||||
_pk_translate('General_ShortDay_3'),
|
||||
_pk_translate('General_ShortDay_4'),
|
||||
_pk_translate('General_ShortDay_5'),
|
||||
_pk_translate('General_ShortDay_6')],
|
||||
dayNames: [
|
||||
_pk_translate('General_LongDay_7'), // start with sunday
|
||||
_pk_translate('General_LongDay_1'),
|
||||
_pk_translate('General_LongDay_2'),
|
||||
_pk_translate('General_LongDay_3'),
|
||||
_pk_translate('General_LongDay_4'),
|
||||
_pk_translate('General_LongDay_5'),
|
||||
_pk_translate('General_LongDay_6')],
|
||||
monthNamesShort: [
|
||||
_pk_translate('General_ShortMonth_1'),
|
||||
_pk_translate('General_ShortMonth_2'),
|
||||
_pk_translate('General_ShortMonth_3'),
|
||||
_pk_translate('General_ShortMonth_4'),
|
||||
_pk_translate('General_ShortMonth_5'),
|
||||
_pk_translate('General_ShortMonth_6'),
|
||||
_pk_translate('General_ShortMonth_7'),
|
||||
_pk_translate('General_ShortMonth_8'),
|
||||
_pk_translate('General_ShortMonth_9'),
|
||||
_pk_translate('General_ShortMonth_10'),
|
||||
_pk_translate('General_ShortMonth_11'),
|
||||
_pk_translate('General_ShortMonth_12')],
|
||||
monthNames: [
|
||||
_pk_translate('General_LongMonth_1'),
|
||||
_pk_translate('General_LongMonth_2'),
|
||||
_pk_translate('General_LongMonth_3'),
|
||||
_pk_translate('General_LongMonth_4'),
|
||||
_pk_translate('General_LongMonth_5'),
|
||||
_pk_translate('General_LongMonth_6'),
|
||||
_pk_translate('General_LongMonth_7'),
|
||||
_pk_translate('General_LongMonth_8'),
|
||||
_pk_translate('General_LongMonth_9'),
|
||||
_pk_translate('General_LongMonth_10'),
|
||||
_pk_translate('General_LongMonth_11'),
|
||||
_pk_translate('General_LongMonth_12')]
|
||||
};
|
||||
};
|
||||
|
||||
var updateDate;
|
||||
|
||||
function getDatePickerOptions() {
|
||||
var result = piwik.getBaseDatePickerOptions(currentDate);
|
||||
result.beforeShowDay = isDateInCurrentPeriod;
|
||||
result.stepMonths = selectedPeriod == 'year' ? 12 : 1;
|
||||
result.onSelect = function () { updateDate.apply(this, arguments); };
|
||||
return result;
|
||||
}
|
||||
|
||||
$(function () {
|
||||
|
||||
var datepickerElem = $('#datepicker').datepicker(getDatePickerOptions()),
|
||||
periodLabels = $('#periodString').find('.period-type label'),
|
||||
periodTooltip = $('#periodString').find('.period-click-tooltip').html();
|
||||
|
||||
var toggleWhitespaceHighlighting = function (klass, toggleTop, toggleBottom) {
|
||||
var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
|
||||
viewedMonth = +$('.ui-datepicker-month', datepickerElem).val(), // convert to int w/ '+'
|
||||
firstOfViewedMonth = new Date(viewedYear, viewedMonth, 1),
|
||||
lastOfViewedMonth = new Date(viewedYear, viewedMonth + 1, 0);
|
||||
|
||||
// only highlight dates between piwik.minDate... & piwik.maxDate...
|
||||
// we select the cells to highlight by checking whether the first & last of the
|
||||
// currently viewed month are within the min/max dates.
|
||||
if (firstOfViewedMonth >= piwikMinDate) {
|
||||
$('tbody>tr:first-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleTop);
|
||||
}
|
||||
if (lastOfViewedMonth < piwikMaxDate) {
|
||||
$('tbody>tr:last-child td.ui-datepicker-other-month', datepickerElem).toggleClass(klass, toggleBottom);
|
||||
}
|
||||
};
|
||||
|
||||
// 'this' is the table cell
|
||||
var highlightCurrentPeriod = function () {
|
||||
switch (selectedPeriod) {
|
||||
case 'day':
|
||||
// highlight this link
|
||||
$('a', $(this)).addClass('ui-state-hover');
|
||||
break;
|
||||
case 'week':
|
||||
var row = $(this).parent();
|
||||
|
||||
// highlight parent row (the week)
|
||||
$('a', row).addClass('ui-state-hover');
|
||||
|
||||
// toggle whitespace if week goes into previous or next month. we check if week is on
|
||||
// top or bottom row.
|
||||
var toggleTop = row.is(':first-child'),
|
||||
toggleBottom = row.is(':last-child');
|
||||
toggleWhitespaceHighlighting('ui-state-hover', toggleTop, toggleBottom);
|
||||
break;
|
||||
case 'month':
|
||||
// highlight all parent rows (the month)
|
||||
$('a', $(this).parent().parent()).addClass('ui-state-hover');
|
||||
break;
|
||||
case 'year':
|
||||
// highlight table (month + whitespace)
|
||||
$('a', $(this).parent().parent()).addClass('ui-state-hover');
|
||||
toggleWhitespaceHighlighting('ui-state-hover', true, true);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
var unhighlightAllDates = function () {
|
||||
// make sure nothing is highlighted
|
||||
$('.ui-state-active,.ui-state-hover', datepickerElem).removeClass('ui-state-active ui-state-hover');
|
||||
|
||||
// color whitespace
|
||||
if (piwik.period == 'year') {
|
||||
var viewedYear = $('.ui-datepicker-year', datepickerElem).val(),
|
||||
toggle = selectedPeriod == 'year' && currentYear == viewedYear;
|
||||
toggleWhitespaceHighlighting('ui-datepicker-current-period', toggle, toggle);
|
||||
}
|
||||
else if (piwik.period == 'week') {
|
||||
var toggleTop = $('tr:first-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period'),
|
||||
toggleBottom = $('tr:last-child a', datepickerElem).parent().hasClass('ui-datepicker-current-period');
|
||||
toggleWhitespaceHighlighting('ui-datepicker-current-period', toggleTop, toggleBottom);
|
||||
}
|
||||
};
|
||||
|
||||
updateDate = function (dateText) {
|
||||
piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
|
||||
|
||||
// select new dates in calendar
|
||||
setCurrentDate(dateText);
|
||||
piwik.period = selectedPeriod;
|
||||
|
||||
// make sure it's called after jquery-ui is done, otherwise everything we do will
|
||||
// be undone.
|
||||
setTimeout(unhighlightAllDates, 1);
|
||||
|
||||
datepickerElem.datepicker('refresh');
|
||||
|
||||
// Let broadcast do its job:
|
||||
// It will replace date value to both search query and hash and load the new page.
|
||||
broadcast.propagateNewPage('date=' + dateText + '&period=' + selectedPeriod);
|
||||
};
|
||||
|
||||
var toggleMonthDropdown = function (disable) {
|
||||
if (typeof disable === 'undefined') {
|
||||
disable = selectedPeriod == 'year';
|
||||
}
|
||||
|
||||
// enable/disable month dropdown based on period == year
|
||||
$('.ui-datepicker-month', datepickerElem).attr('disabled', disable);
|
||||
};
|
||||
|
||||
var togglePeriodPickers = function (showSingle) {
|
||||
$('#periodString').find('.period-date').toggle(showSingle);
|
||||
$('#periodString').find('.period-range').toggle(!showSingle);
|
||||
$('#calendarRangeApply').toggle(!showSingle);
|
||||
};
|
||||
|
||||
//
|
||||
// setup datepicker
|
||||
//
|
||||
|
||||
unhighlightAllDates();
|
||||
|
||||
//
|
||||
// hook up event slots
|
||||
//
|
||||
|
||||
// highlight current period when mouse enters date
|
||||
datepickerElem.on('mouseenter', 'tbody td', function () {
|
||||
if ($(this).hasClass('ui-state-hover')) // if already highlighted, do nothing
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// unhighlight if cell is disabled/blank, unless the period is year
|
||||
if ($(this).hasClass('ui-state-disabled') && selectedPeriod != 'year') {
|
||||
unhighlightAllDates();
|
||||
|
||||
// if period is week, then highlight the current week
|
||||
if (selectedPeriod == 'week') {
|
||||
highlightCurrentPeriod.call(this);
|
||||
}
|
||||
}
|
||||
else {
|
||||
highlightCurrentPeriod.call(this);
|
||||
}
|
||||
});
|
||||
|
||||
// make sure cell stays highlighted when mouse leaves cell (overrides jquery-ui behavior)
|
||||
datepickerElem.on('mouseleave', 'tbody td', function () {
|
||||
$('a', this).addClass('ui-state-hover');
|
||||
});
|
||||
|
||||
// unhighlight everything when mouse leaves table body (can't do event on tbody, for some reason
|
||||
// that fails, so we do two events, one on the table & one on thead)
|
||||
datepickerElem.on('mouseleave', 'table', unhighlightAllDates)
|
||||
.on('mouseenter', 'thead', unhighlightAllDates);
|
||||
|
||||
// make sure whitespace is clickable when the period makes it appropriate
|
||||
datepickerElem.on('click', 'tbody td.ui-datepicker-other-month', function () {
|
||||
if ($(this).hasClass('ui-state-hover')) {
|
||||
var row = $(this).parent(), tbody = row.parent();
|
||||
|
||||
if (row.is(':first-child')) {
|
||||
// click on first of the month
|
||||
$('a', tbody).first().click();
|
||||
}
|
||||
else {
|
||||
// click on last of month
|
||||
$('a', tbody).last().click();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Hack to get around firefox bug. When double clicking a label in firefox, the 'click'
|
||||
// event of its associated input will not be fired twice. We want to change the period
|
||||
// if clicking the select period's label OR input, so we catch the click event on the
|
||||
// label & the input.
|
||||
var reloading = false;
|
||||
var changePeriodOnClick = function (periodInput) {
|
||||
if (reloading) // if a click event resulted in reloading, don't reload again
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var url = periodInput.val(),
|
||||
period = broadcast.getValueFromUrl('period', url);
|
||||
|
||||
// if clicking on the selected period, change the period but not the date
|
||||
if (selectedPeriod == period && selectedPeriod != 'range') {
|
||||
// only reload if current period is different from selected
|
||||
if (piwik.period != selectedPeriod && !reloading) {
|
||||
reloading = true;
|
||||
selectedPeriod = period;
|
||||
updateDate(piwik.currentDateString);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
$("#otherPeriods").find("label").on('click', function (e) {
|
||||
var id = $(e.target).attr('for');
|
||||
changePeriodOnClick($('#' + id));
|
||||
});
|
||||
|
||||
// when non-range period is clicked, change the period & refresh the date picker
|
||||
$("#otherPeriods").find("input").on('click', function (e) {
|
||||
var request_URL = $(e.target).val(),
|
||||
period = broadcast.getValueFromUrl('period', request_URL),
|
||||
lastPeriod = selectedPeriod;
|
||||
|
||||
if (changePeriodOnClick($(e.target))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// switch the selected period
|
||||
selectedPeriod = period;
|
||||
|
||||
// remove tooltips from the period inputs
|
||||
periodLabels.each(function () { $(this).attr('title', '').removeClass('selected-period-label'); });
|
||||
|
||||
// range periods are handled in an event handler below
|
||||
if (period == 'range') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// set the tooltip of the current period
|
||||
if (period != piwik.period) // don't add tooltip for current period
|
||||
{
|
||||
$(this).parent().find('label[for=period_id_' + period + ']')
|
||||
.attr('title', periodTooltip).addClass('selected-period-label');
|
||||
}
|
||||
|
||||
// toggle the right selector controls (show period selector datepicker & hide 'apply range' button)
|
||||
togglePeriodPickers(true);
|
||||
|
||||
// set months step to 12 for year period (or set back to 1 if leaving year period)
|
||||
if (selectedPeriod == 'year' || lastPeriod == 'year') {
|
||||
// setting stepMonths will change the month in view back to the selected date. to avoid
|
||||
// we set the selected date to the month in view.
|
||||
var currentMonth = $('.ui-datepicker-month', datepickerElem).val(),
|
||||
currentYear = $('.ui-datepicker-year', datepickerElem).val();
|
||||
|
||||
datepickerElem
|
||||
.datepicker('option', 'stepMonths', selectedPeriod == 'year' ? 12 : 1)
|
||||
.datepicker('setDate', new Date(currentYear, currentMonth));
|
||||
}
|
||||
|
||||
datepickerElem.datepicker('refresh'); // must be last datepicker call, otherwise cells get highlighted
|
||||
|
||||
unhighlightAllDates();
|
||||
toggleMonthDropdown();
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
// clicking left/right re-enables the month dropdown, so we disable it again
|
||||
$(datepickerElem).on('click', '.ui-datepicker-next,.ui-datepicker-prev', function () {
|
||||
unhighlightAllDates(); // make sure today's date isn't highlighted & toggle extra year highlighting
|
||||
toggleMonthDropdown(selectedPeriod == 'year');
|
||||
});
|
||||
|
||||
// reset date/period when opening calendar
|
||||
$("#periodString").on('click', "#date,.calendar-icon", function () {
|
||||
var periodMore = $("#periodMore").toggle();
|
||||
if (periodMore.is(":visible")) {
|
||||
periodMore.find(".ui-state-highlight").removeClass('ui-state-highlight');
|
||||
}
|
||||
});
|
||||
|
||||
$('body').on('click', function(e) {
|
||||
var target = $(e.target);
|
||||
if (target.closest('html').length && !target.closest('#periodString').length && !target.is('option') && $("#periodMore").is(":visible")) {
|
||||
$("#periodMore").hide();
|
||||
}
|
||||
});
|
||||
|
||||
function onDateRangeSelect(dateText, inst) {
|
||||
var toOrFrom = inst.id == 'calendarFrom' ? 'From' : 'To';
|
||||
$('#inputCalendar' + toOrFrom).val(dateText);
|
||||
}
|
||||
|
||||
// this will trigger to change only the period value on search query and hash string.
|
||||
$("#period_id_range").on('click', function (e) {
|
||||
togglePeriodPickers(false);
|
||||
|
||||
var options = getDatePickerOptions();
|
||||
|
||||
// Custom Date range callback
|
||||
options.onSelect = onDateRangeSelect;
|
||||
// Do not highlight the period
|
||||
options.beforeShowDay = '';
|
||||
// Create both calendars
|
||||
options.defaultDate = piwik.startDateString;
|
||||
$('#calendarFrom').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.startDateString));
|
||||
|
||||
// Technically we should trigger the onSelect event on the calendar, but I couldn't find how to do that
|
||||
// So calling the onSelect bind function manually...
|
||||
//$('#calendarFrom').trigger('dateSelected'); // or onSelect
|
||||
onDateRangeSelect(piwik.startDateString, { "id": "calendarFrom" });
|
||||
|
||||
// Same code for the other calendar
|
||||
options.defaultDate = piwik.endDateString;
|
||||
$('#calendarTo').datepicker(options).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', piwik.endDateString));
|
||||
onDateRangeSelect(piwik.endDateString, { "id": "calendarTo" });
|
||||
|
||||
|
||||
// If not called, the first date appears light brown instead of dark brown
|
||||
$('.ui-state-hover').removeClass('ui-state-hover');
|
||||
|
||||
// Apply date range button will reload the page with the selected range
|
||||
$('#calendarRangeApply')
|
||||
.on('click', function () {
|
||||
var request_URL = $(e.target).val();
|
||||
var dateFrom = $('#inputCalendarFrom').val(),
|
||||
dateTo = $('#inputCalendarTo').val(),
|
||||
oDateFrom = $.datepicker.parseDate('yy-mm-dd', dateFrom),
|
||||
oDateTo = $.datepicker.parseDate('yy-mm-dd', dateTo);
|
||||
|
||||
if (!isValidDate(oDateFrom)
|
||||
|| !isValidDate(oDateTo)
|
||||
|| oDateFrom > oDateTo) {
|
||||
$('#alert').find('h2').text(_pk_translate('General_InvalidDateRange'));
|
||||
piwikHelper.modalConfirm('#alert', {});
|
||||
return false;
|
||||
}
|
||||
piwikHelper.showAjaxLoading('ajaxLoadingCalendar');
|
||||
broadcast.propagateNewPage('period=range&date=' + dateFrom + ',' + dateTo);
|
||||
})
|
||||
.show();
|
||||
|
||||
|
||||
// Bind the input fields to update the calendar's date when date is manually changed
|
||||
$('#inputCalendarFrom, #inputCalendarTo')
|
||||
.keyup(function (e) {
|
||||
var fromOrTo = this.id == 'inputCalendarFrom' ? 'From' : 'To';
|
||||
var dateInput = $(this).val();
|
||||
try {
|
||||
var newDate = $.datepicker.parseDate('yy-mm-dd', dateInput);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
$("#calendar" + fromOrTo).datepicker("setDate", newDate);
|
||||
if (e.keyCode == 13) {
|
||||
$('#calendarRangeApply').click();
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
function isValidDate(d) {
|
||||
if (Object.prototype.toString.call(d) !== "[object Date]")
|
||||
return false;
|
||||
return !isNaN(d.getTime());
|
||||
}
|
||||
|
||||
if (piwik.period == 'range') {
|
||||
$("#period_id_range").click();
|
||||
}
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
225
www/analytics/plugins/CoreHome/javascripts/color_manager.js
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
var colorNames = {"aliceblue":"#f0f8ff","antiquewhite":"#faebd7","aqua":"#00ffff","aquamarine":"#7fffd4","azure":"#f0ffff",
|
||||
"beige":"#f5f5dc","bisque":"#ffe4c4","black":"#000000","blanchedalmond":"#ffebcd","blue":"#0000ff","blueviolet":"#8a2be2","brown":"#a52a2a","burlywood":"#deb887",
|
||||
"cadetblue":"#5f9ea0","chartreuse":"#7fff00","chocolate":"#d2691e","coral":"#ff7f50","cornflowerblue":"#6495ed","cornsilk":"#fff8dc","crimson":"#dc143c","cyan":"#00ffff",
|
||||
"darkblue":"#00008b","darkcyan":"#008b8b","darkgoldenrod":"#b8860b","darkgray":"#a9a9a9","darkgreen":"#006400","darkkhaki":"#bdb76b","darkmagenta":"#8b008b","darkolivegreen":"#556b2f",
|
||||
"darkorange":"#ff8c00","darkorchid":"#9932cc","darkred":"#8b0000","darksalmon":"#e9967a","darkseagreen":"#8fbc8f","darkslateblue":"#483d8b","darkslategray":"#2f4f4f","darkturquoise":"#00ced1",
|
||||
"darkviolet":"#9400d3","deeppink":"#ff1493","deepskyblue":"#00bfff","dimgray":"#696969","dodgerblue":"#1e90ff",
|
||||
"firebrick":"#b22222","floralwhite":"#fffaf0","forestgreen":"#228b22","fuchsia":"#ff00ff","gainsboro":"#dcdcdc","ghostwhite":"#f8f8ff","gold":"#ffd700","goldenrod":"#daa520","gray":"#808080","green":"#008000","greenyellow":"#adff2f",
|
||||
"honeydew":"#f0fff0","hotpink":"#ff69b4","indianred ":"#cd5c5c","indigo ":"#4b0082","ivory":"#fffff0","khaki":"#f0e68c",
|
||||
"lavender":"#e6e6fa","lavenderblush":"#fff0f5","lawngreen":"#7cfc00","lemonchiffon":"#fffacd","lightblue":"#add8e6","lightcoral":"#f08080","lightcyan":"#e0ffff","lightgoldenrodyellow":"#fafad2",
|
||||
"lightgrey":"#d3d3d3","lightgreen":"#90ee90","lightpink":"#ffb6c1","lightsalmon":"#ffa07a","lightseagreen":"#20b2aa","lightskyblue":"#87cefa","lightslategray":"#778899","lightsteelblue":"#b0c4de",
|
||||
"lightyellow":"#ffffe0","lime":"#00ff00","limegreen":"#32cd32","linen":"#faf0e6","magenta":"#ff00ff","maroon":"#800000","mediumaquamarine":"#66cdaa","mediumblue":"#0000cd","mediumorchid":"#ba55d3","mediumpurple":"#9370d8","mediumseagreen":"#3cb371","mediumslateblue":"#7b68ee",
|
||||
"mediumspringgreen":"#00fa9a","mediumturquoise":"#48d1cc","mediumvioletred":"#c71585","midnightblue":"#191970","mintcream":"#f5fffa","mistyrose":"#ffe4e1","moccasin":"#ffe4b5",
|
||||
"navajowhite":"#ffdead","navy":"#000080","oldlace":"#fdf5e6","olive":"#808000","olivedrab":"#6b8e23","orange":"#ffa500","orangered":"#ff4500","orchid":"#da70d6",
|
||||
"palegoldenrod":"#eee8aa","palegreen":"#98fb98","paleturquoise":"#afeeee","palevioletred":"#d87093","papayawhip":"#ffefd5","peachpuff":"#ffdab9","peru":"#cd853f","pink":"#ffc0cb","plum":"#dda0dd","powderblue":"#b0e0e6","purple":"#800080",
|
||||
"red":"#ff0000","rosybrown":"#bc8f8f","royalblue":"#4169e1","saddlebrown":"#8b4513","salmon":"#fa8072","sandybrown":"#f4a460","seagreen":"#2e8b57","seashell":"#fff5ee","sienna":"#a0522d","silver":"#c0c0c0","skyblue":"#87ceeb","slateblue":"#6a5acd","slategray":"#708090","snow":"#fffafa","springgreen":"#00ff7f","steelblue":"#4682b4",
|
||||
"tan":"#d2b48c","teal":"#008080","thistle":"#d8bfd8","tomato":"#ff6347","turquoise":"#40e0d0","violet":"#ee82ee","wheat":"#f5deb3","white":"#ffffff","whitesmoke":"#f5f5f5","yellow":"#ffff00","yellowgreen":"#9acd32"};
|
||||
|
||||
/**
|
||||
* The ColorManager class allows JS code to grab colors defined in CSS for
|
||||
* components that don't manage HTML (like jqPlot or sparklines). Such components
|
||||
* can't use CSS colors directly since the colors are used to generate images
|
||||
* or by <canvas> elements.
|
||||
*
|
||||
* Colors obtained via ColorManager are defined in CSS like this:
|
||||
*
|
||||
* .my-color-namespace[data-name=color-name] {
|
||||
* color: #fff
|
||||
* }
|
||||
*
|
||||
* and can be accessed in JavaScript like this:
|
||||
*
|
||||
* piwik.ColorManager.getColor("my-color-namespace", "color-name");
|
||||
*
|
||||
* The singleton instance of this class can be accessed via piwik.ColorManager.
|
||||
*/
|
||||
var ColorManager = function () {
|
||||
// empty
|
||||
};
|
||||
|
||||
ColorManager.prototype = {
|
||||
|
||||
/**
|
||||
* Returns the color for a namespace and name.
|
||||
*
|
||||
* @param {String} namespace The string identifier that groups related colors
|
||||
* together. For example, 'sparkline-colors'.
|
||||
* @param {String} name The name of the color to retrieve. For example, 'lineColor'.
|
||||
* @return {String} A hex color, eg, '#fff'.
|
||||
*/
|
||||
getColor: function (namespace, name) {
|
||||
var element = this._getElement();
|
||||
|
||||
element.attr('class', 'color-manager ' + namespace).attr('data-name', name);
|
||||
|
||||
return this._normalizeColor(element.css('color'));
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the colors for a namespace and a list of names.
|
||||
*
|
||||
* @param {String} namespace The string identifier that groups related colors
|
||||
* together. For example, 'sparkline-colors'.
|
||||
* @param {Array} names An array of color names to retrieve.
|
||||
* @param {Boolean} asArray Whether the result should be an array or an object.
|
||||
* @return {Object|Array} An object mapping color names with color values or an
|
||||
* array of colors.
|
||||
*/
|
||||
getColors: function (namespace, names, asArray) {
|
||||
var colors = asArray ? [] : {};
|
||||
for (var i = 0; i != names.length; ++i) {
|
||||
var name = names[i],
|
||||
color = this.getColor(namespace, name);
|
||||
if (color) {
|
||||
if (asArray) {
|
||||
colors.push(color);
|
||||
} else {
|
||||
colors[name] = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
return colors;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a color that is N % between two other colors.
|
||||
*
|
||||
* @param {String|Array} spectrumStart The start color. If percentFromStart is 0, this color will
|
||||
* be returned. Can be either a hex color or RGB array.
|
||||
* It will be converted to an RGB array if a hex color is supplied.
|
||||
* @param {String|Array} spectrumEnd The end color. If percentFromStart is 1, this color will be
|
||||
* returned. Can be either a hex color or RGB array. It will be
|
||||
* converted to an RGB array if a hex color is supplied.
|
||||
* @param {Number} percentFromStart The percent from spectrumStart and twoard spectrumEnd that the
|
||||
* result color should be. Must be a value between 0.0 & 1.0.
|
||||
* @return {String} A hex color.
|
||||
*/
|
||||
getSingleColorFromGradient: function (spectrumStart, spectrumEnd, percentFromStart) {
|
||||
if (!(spectrumStart instanceof Array)) {
|
||||
spectrumStart = this.getRgb(spectrumStart);
|
||||
}
|
||||
|
||||
if (!(spectrumEnd instanceof Array)) {
|
||||
spectrumEnd = this.getRgb(spectrumEnd);
|
||||
}
|
||||
|
||||
var result = [];
|
||||
for (var channel = 0; channel != spectrumStart.length; ++channel) {
|
||||
var delta = (spectrumEnd[channel] - spectrumStart[channel]) * percentFromStart;
|
||||
|
||||
result[channel] = Math.floor(spectrumStart[channel] + delta);
|
||||
}
|
||||
|
||||
return this.getHexColor(result);
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function that converts a hex color (ie, #fff or #1a1a1a) to an array of
|
||||
* RGB values.
|
||||
*
|
||||
* @param {String} hexColor The color to convert.
|
||||
* @return {Array} An array with three integers between 0 and 255.
|
||||
*/
|
||||
getRgb: function (hexColor) {
|
||||
if (hexColor[0] == '#') {
|
||||
hexColor = hexColor.substring(1);
|
||||
}
|
||||
|
||||
if (hexColor.length == 3) {
|
||||
return [
|
||||
parseInt(hexColor[0], 16),
|
||||
parseInt(hexColor[1], 16),
|
||||
parseInt(hexColor[2], 16)
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
parseInt(hexColor.substring(0,2), 16),
|
||||
parseInt(hexColor.substring(2,4), 16),
|
||||
parseInt(hexColor.substring(4,6), 16)
|
||||
];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Utility function that converts an RGB array to a hex color.
|
||||
*
|
||||
* @param {Array} rgbColor An array with three integers between 0 and 255.
|
||||
* @return {String} The hex color, eg, #1a1a1a.
|
||||
*/
|
||||
getHexColor: function (rgbColor) {
|
||||
// convert channels to hex with one leading 0
|
||||
for (var i = 0; i != rgbColor.length; ++i) {
|
||||
rgbColor[i] = ("00" + rgbColor[i].toString(16)).slice(-2);
|
||||
}
|
||||
|
||||
// create hex string
|
||||
return '#' + rgbColor.join('');
|
||||
},
|
||||
|
||||
/**
|
||||
* Turns a color string that might be an rgb value rgb(12, 34, 56) into
|
||||
* a hex color string.
|
||||
*/
|
||||
_normalizeColor: function (color) {
|
||||
if (color == this._getTransparentColor()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (color && colorNames[color]) {
|
||||
return colorNames[color];
|
||||
}
|
||||
|
||||
if (color
|
||||
&& color[0] != '#'
|
||||
) {
|
||||
// parse rgb(#, #, #) and get rgb numbers
|
||||
var parts = color.split(/[()rgb,\s]+/);
|
||||
parts = [+parts[1], +parts[2], +parts[3]];
|
||||
|
||||
// convert to hex
|
||||
color = this.getHexColor(parts);
|
||||
}
|
||||
return color;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the manufactured <div> element used to obtain color data. When
|
||||
* getting color data the class and data-name attribute of this element are
|
||||
* changed.
|
||||
*/
|
||||
_getElement: function () {
|
||||
if (!this.$element) {
|
||||
$('body').append('<div id="color-manager"></div>');
|
||||
this.$element = $('#color-manager');
|
||||
}
|
||||
|
||||
return this.$element;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns this browser's representation of the 'transparent' color. Used to
|
||||
* compare against colors obtained in getColor. If a color is 'transparent'
|
||||
* it means there's no color for that namespace/name combination.
|
||||
*/
|
||||
_getTransparentColor: function () {
|
||||
if (!this.transparentColor) {
|
||||
this.transparentColor =
|
||||
$('<div style="color:transparent;display:none;"></div>').appendTo($('body')).css('color');
|
||||
}
|
||||
|
||||
return this.transparentColor;
|
||||
}
|
||||
};
|
||||
|
||||
piwik.ColorManager = new ColorManager();
|
||||
|
||||
}(jQuery));
|
||||
160
www/analytics/plugins/CoreHome/javascripts/corehome.js
Executable file
|
|
@ -0,0 +1,160 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
$(function () {
|
||||
|
||||
//
|
||||
// 'check for updates' behavior
|
||||
//
|
||||
|
||||
var headerMessageParent = $('#header_message').parent();
|
||||
|
||||
// when 'check for updates...' link is clicked, force a check & display the result
|
||||
headerMessageParent.on('click', '#updateCheckLinkContainer', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
var headerMessage = $(this).closest('#header_message');
|
||||
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.setLoadingElement('#header_message .loadingPiwik');
|
||||
ajaxRequest.addParams({
|
||||
module: 'CoreHome',
|
||||
action: 'checkForUpdates'
|
||||
}, 'get');
|
||||
ajaxRequest.setCallback(function (response) {
|
||||
headerMessage.fadeOut('slow', function () {
|
||||
response = $(response);
|
||||
|
||||
var newVersionAvailable = response.hasClass('header_alert');
|
||||
if (newVersionAvailable) {
|
||||
headerMessage.replaceWith(response);
|
||||
}
|
||||
else {
|
||||
headerMessage.html(_pk_translate('CoreHome_YouAreUsingTheLatestVersion')).show();
|
||||
setTimeout(function () {
|
||||
headerMessage.fadeOut('slow', function () {
|
||||
headerMessage.replaceWith(response);
|
||||
});
|
||||
}, 4000);
|
||||
}
|
||||
});
|
||||
});
|
||||
ajaxRequest.setFormat('html');
|
||||
ajaxRequest.send(false);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// when clicking the header message, show the long message w/o needing to hover
|
||||
headerMessageParent.on('click', '#header_message', function (e) {
|
||||
if (e.target.tagName.toLowerCase() != 'a') {
|
||||
$(this).toggleClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
//
|
||||
// section toggler behavior
|
||||
//
|
||||
|
||||
var handleSectionToggle = function (self, showType, doHide) {
|
||||
var sectionId = $(self).attr('data-section-id'),
|
||||
section = $('#' + sectionId),
|
||||
showText = _pk_translate('General_Show'),
|
||||
hideText = _pk_translate('General_Hide');
|
||||
|
||||
if (typeof(doHide) == 'undefined') {
|
||||
doHide = section.is(':visible');
|
||||
}
|
||||
|
||||
if (doHide) {
|
||||
var newText = $(self).text().replace(hideText, showText),
|
||||
afterHide = function () { $(self).text(newText); };
|
||||
|
||||
if (showType == 'slide') {
|
||||
section.slideUp(afterHide);
|
||||
}
|
||||
else if (showType == 'inline') {
|
||||
section.hide();
|
||||
afterHide();
|
||||
}
|
||||
else {
|
||||
section.hide(afterHide);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var newText = $(self).text().replace(showText, hideText);
|
||||
$(self).text(newText);
|
||||
|
||||
if (showType == 'slide') {
|
||||
section.slideDown();
|
||||
}
|
||||
else if (showType == 'inline') {
|
||||
section.css('display', 'inline-block');
|
||||
}
|
||||
else {
|
||||
section.show();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// when click section toggler link, toggle the visibility of the associated section
|
||||
$('body').on('click', 'a.section-toggler-link', function (e) {
|
||||
e.preventDefault();
|
||||
handleSectionToggle(this, 'slide');
|
||||
return false;
|
||||
});
|
||||
|
||||
$('body').on('change', 'input.section-toggler-link', function (e) {
|
||||
handleSectionToggle(this, 'inline', !$(this).is(':checked'));
|
||||
});
|
||||
|
||||
//
|
||||
// reports by dimension list behavior
|
||||
//
|
||||
|
||||
// when a report dimension is clicked, load the appropriate report
|
||||
var currentWidgetLoading = null;
|
||||
$('body').on('click', '.reportDimension', function (e) {
|
||||
var view = $(this).closest('.reportsByDimensionView'),
|
||||
report = $('.dimensionReport', view),
|
||||
loading = $('.loadingPiwik', view);
|
||||
|
||||
// make this dimension the active one
|
||||
$('.activeDimension', view).removeClass('activeDimension');
|
||||
$(this).addClass('activeDimension');
|
||||
|
||||
// hide the visible report & show the loading elem
|
||||
report.hide();
|
||||
loading.show();
|
||||
|
||||
// load the report using the data-url attribute (which holds the URL to the report)
|
||||
var widgetParams = broadcast.getValuesFromUrl($(this).attr('data-url'));
|
||||
for (var key in widgetParams) {
|
||||
widgetParams[key] = decodeURIComponent(widgetParams[key]);
|
||||
}
|
||||
|
||||
var widgetUniqueId = widgetParams.module + widgetParams.action;
|
||||
currentWidgetLoading = widgetUniqueId;
|
||||
|
||||
widgetsHelper.loadWidgetAjax(widgetUniqueId, widgetParams, function (response) {
|
||||
// if the widget that was loaded was not for the latest clicked link, do nothing w/ the response
|
||||
if (widgetUniqueId != currentWidgetLoading) {
|
||||
return;
|
||||
}
|
||||
|
||||
loading.hide();
|
||||
report.html($(response)).css('display', 'inline-block');
|
||||
|
||||
// scroll to report
|
||||
piwikHelper.lazyScrollTo(report, 400);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
1767
www/analytics/plugins/CoreHome/javascripts/dataTable.js
Normal file
|
|
@ -0,0 +1,386 @@
|
|||
/**
|
||||
* Registry for row actions
|
||||
*
|
||||
* Plugins can call DataTable_RowActions_Registry.register() from their JS
|
||||
* files in order to add new actions to arbitrary data tables. The register()
|
||||
* method takes an object containing:
|
||||
* - name: string identifying the action. must be short, no spaces.
|
||||
* - dataTableIcon: path to the icon for the action
|
||||
* - createInstance: a factory method to create an instance of the appropriate
|
||||
* subclass of DataTable_RowAction
|
||||
* - isAvailable: a method to determine whether the action is available in a
|
||||
* given row of a data table
|
||||
*/
|
||||
var DataTable_RowActions_Registry = {
|
||||
|
||||
registry: [],
|
||||
|
||||
register: function (action) {
|
||||
var createInstance = action.createInstance;
|
||||
action.createInstance = function (dataTable, param) {
|
||||
var instance = createInstance(dataTable, param);
|
||||
instance.actionName = action.name;
|
||||
return instance;
|
||||
};
|
||||
|
||||
this.registry.push(action);
|
||||
},
|
||||
|
||||
getAvailableActionsForReport: function (dataTableParams, tr) {
|
||||
if (dataTableParams.disable_row_actions == '1') {
|
||||
return [];
|
||||
}
|
||||
|
||||
var available = [];
|
||||
for (var i = 0; i < this.registry.length; i++) {
|
||||
if (this.registry[i].isAvailableOnReport(dataTableParams, tr)) {
|
||||
available.push(this.registry[i]);
|
||||
}
|
||||
}
|
||||
available.sort(function (a, b) {
|
||||
return b.order - a.order;
|
||||
});
|
||||
return available;
|
||||
},
|
||||
|
||||
getActionByName: function (name) {
|
||||
for (var i = 0; i < this.registry.length; i++) {
|
||||
if (this.registry[i].name == name) {
|
||||
return this.registry[i];
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// Register Row Evolution (also servers as example)
|
||||
DataTable_RowActions_Registry.register({
|
||||
|
||||
name: 'RowEvolution',
|
||||
|
||||
dataTableIcon: 'plugins/Zeitgeist/images/row_evolution.png',
|
||||
dataTableIconHover: 'plugins/Zeitgeist/images/row_evolution_hover.png',
|
||||
|
||||
order: 50,
|
||||
|
||||
dataTableIconTooltip: [
|
||||
_pk_translate('General_RowEvolutionRowActionTooltipTitle'),
|
||||
_pk_translate('General_RowEvolutionRowActionTooltip')
|
||||
],
|
||||
|
||||
createInstance: function (dataTable, param) {
|
||||
if (dataTable !== null && typeof dataTable.rowEvolutionActionInstance != 'undefined') {
|
||||
return dataTable.rowEvolutionActionInstance;
|
||||
}
|
||||
|
||||
if (dataTable === null && param) {
|
||||
// when row evolution is triggered from the url (not a click on the data table)
|
||||
// we look for the data table instance in the dom
|
||||
var report = param.split(':')[0];
|
||||
var div = $(require('piwik/UI').DataTable.getDataTableByReport(report));
|
||||
if (div.size() > 0 && div.data('uiControlObject')) {
|
||||
dataTable = div.data('uiControlObject');
|
||||
if (typeof dataTable.rowEvolutionActionInstance != 'undefined') {
|
||||
return dataTable.rowEvolutionActionInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var instance = new DataTable_RowActions_RowEvolution(dataTable);
|
||||
if (dataTable !== null) {
|
||||
dataTable.rowEvolutionActionInstance = instance;
|
||||
}
|
||||
return instance;
|
||||
},
|
||||
|
||||
isAvailableOnReport: function (dataTableParams) {
|
||||
return (
|
||||
typeof dataTableParams.disable_row_evolution == 'undefined'
|
||||
|| dataTableParams.disable_row_evolution == "0"
|
||||
) && (
|
||||
typeof dataTableParams.flat == 'undefined'
|
||||
|| dataTableParams.flat == "0"
|
||||
);
|
||||
},
|
||||
|
||||
isAvailableOnRow: function (dataTableParams, tr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* DataTable Row Actions
|
||||
*
|
||||
* The lifecycle of an action is as follows:
|
||||
* - for each data table, a new instance of the action is created using the factory
|
||||
* - when the table is loaded, initTr is called for each tr
|
||||
* - when the action icon is clicked, trigger is called
|
||||
* - the label is put together and performAction is called
|
||||
* - performAction must call openPopover on the base class
|
||||
* - openPopover calls back doOpenPopover after doing general stuff
|
||||
*
|
||||
* The two template methods are performAction and doOpenPopover
|
||||
*/
|
||||
|
||||
|
||||
//
|
||||
// BASE CLASS
|
||||
//
|
||||
|
||||
function DataTable_RowAction(dataTable) {
|
||||
this.dataTable = dataTable;
|
||||
|
||||
// has to be overridden in subclasses
|
||||
this.trEventName = 'piwikTriggerRowAction';
|
||||
|
||||
// set in registry
|
||||
this.actionName = 'RowAction';
|
||||
}
|
||||
|
||||
/** Initialize a row when the table is loaded */
|
||||
DataTable_RowAction.prototype.initTr = function (tr) {
|
||||
var self = this;
|
||||
|
||||
// For subtables, we need to make sure that the actions are always triggered on the
|
||||
// action instance connected to the root table. Otherwise sharing data (e.g. for
|
||||
// for multi-row evolution) wouldn't be possible. Also, sub-tables might have different
|
||||
// API actions. For the label filter to work, we need to use the parent action.
|
||||
// We use jQuery events to let subtables access their parents.
|
||||
tr.bind(self.trEventName, function (e, params) {
|
||||
self.trigger($(this), params.originalEvent, params.label);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* This method is called from the click event and the tr event (see this.trEventName).
|
||||
* It derives the label and calls performAction.
|
||||
*/
|
||||
DataTable_RowAction.prototype.trigger = function (tr, e, subTableLabel) {
|
||||
var label = this.getLabelFromTr(tr);
|
||||
|
||||
label = label.trim();
|
||||
// if we have received the event from the sub table, add the label
|
||||
if (subTableLabel) {
|
||||
var separator = ' > '; // LabelFilter::SEPARATOR_RECURSIVE_LABEL
|
||||
label += separator + subTableLabel.trim();
|
||||
}
|
||||
|
||||
// handle sub tables in nested reports: forward to parent
|
||||
var subtable = tr.closest('table');
|
||||
if (subtable.is('.subDataTable')) {
|
||||
subtable.closest('tr').prev().trigger(this.trEventName, {
|
||||
label: label,
|
||||
originalEvent: e
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// ascend in action reports
|
||||
if (subtable.closest('div.dataTableActions').length) {
|
||||
var allClasses = tr.attr('class');
|
||||
var matches = allClasses.match(/level[0-9]+/);
|
||||
var level = parseInt(matches[0].substring(5, matches[0].length), 10);
|
||||
if (level > 0) {
|
||||
// .prev(.levelX) does not work for some reason => do it "by hand"
|
||||
var findLevel = 'level' + (level - 1);
|
||||
var ptr = tr;
|
||||
while ((ptr = ptr.prev()).size() > 0) {
|
||||
if (!ptr.hasClass(findLevel)) {
|
||||
continue;
|
||||
}
|
||||
ptr.trigger(this.trEventName, {
|
||||
label: label,
|
||||
originalEvent: e
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.performAction(label, tr, e);
|
||||
};
|
||||
|
||||
/** Get the label string from a tr dom element */
|
||||
DataTable_RowAction.prototype.getLabelFromTr = function (tr) {
|
||||
var label = tr.find('span.label');
|
||||
|
||||
// handle truncation
|
||||
var value = label.data('originalText');
|
||||
|
||||
if (!value) {
|
||||
value = label.text();
|
||||
}
|
||||
value = value.trim();
|
||||
|
||||
return encodeURIComponent(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Base method for opening popovers.
|
||||
* This method will remember the parameter in the url and call doOpenPopover().
|
||||
*/
|
||||
DataTable_RowAction.prototype.openPopover = function (parameter) {
|
||||
broadcast.propagateNewPopoverParameter('RowAction', this.actionName + ':' + parameter);
|
||||
};
|
||||
|
||||
broadcast.addPopoverHandler('RowAction', function (param) {
|
||||
var paramParts = param.split(':');
|
||||
var rowActionName = paramParts[0];
|
||||
paramParts.shift();
|
||||
param = paramParts.join(':');
|
||||
|
||||
var rowAction = DataTable_RowActions_Registry.getActionByName(rowActionName);
|
||||
if (rowAction) {
|
||||
rowAction.createInstance(null, param).doOpenPopover(param);
|
||||
}
|
||||
});
|
||||
|
||||
/** To be overridden */
|
||||
DataTable_RowAction.prototype.performAction = function (label, tr, e) {
|
||||
};
|
||||
DataTable_RowAction.prototype.doOpenPopover = function (parameter) {
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// ROW EVOLUTION
|
||||
//
|
||||
|
||||
function DataTable_RowActions_RowEvolution(dataTable) {
|
||||
this.dataTable = dataTable;
|
||||
this.trEventName = 'piwikTriggerRowEvolution';
|
||||
|
||||
/** The rows to be compared in multi row evolution */
|
||||
this.multiEvolutionRows = [];
|
||||
}
|
||||
|
||||
/** Static helper method to launch row evolution from anywhere */
|
||||
DataTable_RowActions_RowEvolution.launch = function (apiMethod, label) {
|
||||
var param = 'RowEvolution:' + apiMethod + ':0:' + label;
|
||||
broadcast.propagateNewPopoverParameter('RowAction', param);
|
||||
};
|
||||
|
||||
DataTable_RowActions_RowEvolution.prototype = new DataTable_RowAction;
|
||||
|
||||
DataTable_RowActions_RowEvolution.prototype.performAction = function (label, tr, e) {
|
||||
if (e.shiftKey) {
|
||||
// only mark for multi row evolution if shift key is pressed
|
||||
this.addMultiEvolutionRow(label);
|
||||
return;
|
||||
}
|
||||
|
||||
// check whether we have rows marked for multi row evolution
|
||||
var isMultiRowEvolution = '0';
|
||||
this.addMultiEvolutionRow(label);
|
||||
if (this.multiEvolutionRows.length > 1) {
|
||||
isMultiRowEvolution = '1';
|
||||
label = this.multiEvolutionRows.join(',');
|
||||
}
|
||||
|
||||
var apiMethod = this.dataTable.param.module + '.' + this.dataTable.param.action;
|
||||
this.openPopover(apiMethod, isMultiRowEvolution, label);
|
||||
};
|
||||
|
||||
DataTable_RowActions_RowEvolution.prototype.addMultiEvolutionRow = function (label) {
|
||||
if ($.inArray(label, this.multiEvolutionRows) == -1) {
|
||||
this.multiEvolutionRows.push(label);
|
||||
}
|
||||
};
|
||||
|
||||
DataTable_RowActions_RowEvolution.prototype.openPopover = function (apiMethod, multiRowEvolutionParam, label) {
|
||||
var urlParam = apiMethod + ':' + multiRowEvolutionParam + ':' + label;
|
||||
DataTable_RowAction.prototype.openPopover.apply(this, [urlParam]);
|
||||
};
|
||||
|
||||
DataTable_RowActions_RowEvolution.prototype.doOpenPopover = function (urlParam) {
|
||||
var urlParamParts = urlParam.split(':');
|
||||
|
||||
var apiMethod = urlParamParts[0];
|
||||
urlParamParts.shift();
|
||||
|
||||
var multiRowEvolutionParam = urlParamParts[0];
|
||||
urlParamParts.shift();
|
||||
|
||||
var label = urlParamParts.join(':');
|
||||
|
||||
this.showRowEvolution(apiMethod, label, multiRowEvolutionParam);
|
||||
};
|
||||
|
||||
/** Open the row evolution popover */
|
||||
DataTable_RowActions_RowEvolution.prototype.showRowEvolution = function (apiMethod, label, multiRowEvolutionParam) {
|
||||
|
||||
var self = this;
|
||||
|
||||
// open the popover
|
||||
var box = Piwik_Popover.showLoading('Row Evolution');
|
||||
box.addClass('rowEvolutionPopover');
|
||||
|
||||
// prepare loading the popover contents
|
||||
var requestParams = {
|
||||
apiMethod: apiMethod,
|
||||
label: label,
|
||||
disableLink: 1
|
||||
};
|
||||
|
||||
// derive api action and requested column from multiRowEvolutionParam
|
||||
var action;
|
||||
if (multiRowEvolutionParam == '0') {
|
||||
action = 'getRowEvolutionPopover';
|
||||
} else if (multiRowEvolutionParam == '1') {
|
||||
action = 'getMultiRowEvolutionPopover';
|
||||
} else {
|
||||
action = 'getMultiRowEvolutionPopover';
|
||||
requestParams.column = multiRowEvolutionParam;
|
||||
}
|
||||
|
||||
var callback = function (html) {
|
||||
Piwik_Popover.setContent(html);
|
||||
|
||||
// use the popover title returned from the server
|
||||
var title = box.find('div.popover-title');
|
||||
if (title.size() > 0) {
|
||||
Piwik_Popover.setTitle(title.html());
|
||||
title.remove();
|
||||
}
|
||||
|
||||
Piwik_Popover.onClose(function () {
|
||||
// reset rows marked for multi row evolution on close
|
||||
self.multiEvolutionRows = [];
|
||||
});
|
||||
|
||||
if (self.dataTable !== null) {
|
||||
// remember label for multi row evolution
|
||||
box.find('a.rowevolution-startmulti').click(function () {
|
||||
Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
|
||||
Piwik_Popover.close();
|
||||
return false;
|
||||
});
|
||||
} else {
|
||||
// when the popover is launched by copy&pasting a url, we don't have the data table.
|
||||
// in this case, we can't remember the row marked for multi row evolution so
|
||||
// we disable the picker.
|
||||
box.find('.compare-container, .rowevolution-startmulti').remove();
|
||||
}
|
||||
|
||||
// switch metric in multi row evolution
|
||||
box.find('select.multirowevoltion-metric').change(function () {
|
||||
var metric = $(this).val();
|
||||
Piwik_Popover.onClose(false); // unbind listener that resets multiEvolutionRows
|
||||
self.openPopover(apiMethod, metric, label);
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
requestParams.module = 'CoreHome';
|
||||
requestParams.action = action;
|
||||
requestParams.colors = JSON.stringify(piwik.getSparklineColors());
|
||||
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.addParams(requestParams, 'get');
|
||||
ajaxRequest.setCallback(callback);
|
||||
ajaxRequest.setFormat('html');
|
||||
ajaxRequest.send(false);
|
||||
};
|
||||
142
www/analytics/plugins/CoreHome/javascripts/donate.js
Executable file
|
|
@ -0,0 +1,142 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
(function ($) {
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
var donateAmounts = [0, 30, 60, 90, 120];
|
||||
|
||||
// returns the space between each donation amount in the donation slider
|
||||
var getTickWidth = function (slider) {
|
||||
var effectiveSliderWidth = $('.slider-range', slider).width() - $('.slider-position', slider).width();
|
||||
return effectiveSliderWidth / (donateAmounts.length - 1);
|
||||
};
|
||||
|
||||
// returns the position index on a slider based on a x coordinate value
|
||||
var getPositionFromPageCoord = function (slider, pageX) {
|
||||
return Math.round((pageX - $('.slider-range', slider).offset().left) / getTickWidth(slider));
|
||||
};
|
||||
|
||||
// set's the correct amount text & smiley face image based on the position of the slider
|
||||
var setSmileyFaceAndAmount = function (slider, pos) {
|
||||
// set text yearly amount
|
||||
$('.slider-donate-amount', slider).text('$' + donateAmounts[pos] + '/' + _pk_translate('General_YearShort'));
|
||||
|
||||
// set the right smiley face
|
||||
$('.slider-smiley-face').attr('src', 'plugins/Zeitgeist/images/smileyprog_' + pos + '.png');
|
||||
|
||||
// set the hidden option input for paypal
|
||||
var option = Math.max(1, pos);
|
||||
$('.piwik-donate-call input[name=os0]').val("Option " + option);
|
||||
};
|
||||
|
||||
// move's a slider's position to a specific spot
|
||||
var moveSliderPosition = function (slider, to) {
|
||||
// make sure 'to' is valid
|
||||
if (to < 0) {
|
||||
to = 0;
|
||||
}
|
||||
else if (to >= donateAmounts.length) {
|
||||
to = donateAmounts.length - 1;
|
||||
}
|
||||
|
||||
// set the slider position
|
||||
var left = to * getTickWidth(slider);
|
||||
if (left == 0) {
|
||||
left = -1; // at position 0 we move one pixel left to cover up some of slider bar
|
||||
}
|
||||
|
||||
$('.slider-position', slider).css({
|
||||
left: left + 'px'
|
||||
});
|
||||
|
||||
// reset the smiley face & amount based on the new position
|
||||
setSmileyFaceAndAmount(slider, to);
|
||||
};
|
||||
|
||||
// when a slider is clicked, set the amount & smiley face appropriately
|
||||
$('body').on('click', '.piwik-donate-slider>.slider-range', function (e) {
|
||||
var slider = $(this).parent(),
|
||||
currentPageX = $('.slider-position', this).offset().left,
|
||||
currentPos = getPositionFromPageCoord(slider, currentPageX),
|
||||
pos = getPositionFromPageCoord(slider, e.pageX);
|
||||
|
||||
// if the closest position is the current one, use the other position since
|
||||
// the user obviously wants to move the slider.
|
||||
if (currentPos == pos) {
|
||||
// if click is to right, go forward one, else backwards one
|
||||
if (e.pageX > currentPageX) {
|
||||
++pos;
|
||||
}
|
||||
else {
|
||||
--pos;
|
||||
}
|
||||
}
|
||||
|
||||
moveSliderPosition(slider, pos);
|
||||
});
|
||||
|
||||
// when the smiley icon is clicked, move the position up one to demonstrate how to use the slider
|
||||
$('body').on('click', '.piwik-donate-slider .slider-smiley-face,.piwik-donate-slider .slider-donate-amount',
|
||||
function (e) {
|
||||
var slider = $(this).closest('.piwik-donate-slider'),
|
||||
currentPageX = $('.slider-position', slider).offset().left,
|
||||
currentPos = getPositionFromPageCoord(slider, currentPageX);
|
||||
|
||||
moveSliderPosition(slider, currentPos + 1);
|
||||
}
|
||||
);
|
||||
|
||||
// stores the current slider being dragged
|
||||
var draggingSlider = false;
|
||||
|
||||
// start dragging on mousedown for a slider's position bar
|
||||
$('body').on('mousedown', '.piwik-donate-slider .slider-position', function () {
|
||||
draggingSlider = $(this).parent().parent();
|
||||
});
|
||||
|
||||
// move the slider position if currently dragging when the mouse moves anywhere over the entire page
|
||||
$('body').on('mousemove', function (e) {
|
||||
if (draggingSlider) {
|
||||
var slider = draggingSlider.find('.slider-range'),
|
||||
sliderPos = slider.find('.slider-position'),
|
||||
left = e.pageX - slider.offset().left;
|
||||
|
||||
// only move slider if the mouse x-coord is still on the slider (w/ some padding for borders)
|
||||
if (left <= slider.width() - sliderPos.width() + 2
|
||||
&& left >= -2) {
|
||||
sliderPos.css({left: left + 'px'});
|
||||
|
||||
var closestPos = Math.round(left / getTickWidth(draggingSlider));
|
||||
setSmileyFaceAndAmount(draggingSlider, closestPos);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// stop dragging and normalize a slider's position on mouseup over the entire page
|
||||
$('body').on('mouseup', function () {
|
||||
if (draggingSlider) {
|
||||
var sliderPos = $('.slider-position', draggingSlider),
|
||||
slider = sliderPos.parent();
|
||||
|
||||
if (sliderPos.length) {
|
||||
// move the slider to the nearest donation amount position
|
||||
var pos = getPositionFromPageCoord(draggingSlider, sliderPos.offset().left);
|
||||
moveSliderPosition(draggingSlider, pos);
|
||||
}
|
||||
|
||||
draggingSlider = false; // stop dragging
|
||||
}
|
||||
});
|
||||
|
||||
// event for programatically changing the position
|
||||
$('body').on('piwik:changePosition', '.piwik-donate-slider', function (e, data) {
|
||||
moveSliderPosition(this, data.position);
|
||||
});
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
110
www/analytics/plugins/CoreHome/javascripts/menu.js
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
function menu() {
|
||||
this.param = {};
|
||||
}
|
||||
|
||||
menu.prototype =
|
||||
{
|
||||
resetTimer: null,
|
||||
|
||||
adaptSubMenuHeight: function() {
|
||||
var subNavHeight = $('.sfHover > ul').outerHeight();
|
||||
$('.nav_sep').height(subNavHeight);
|
||||
},
|
||||
|
||||
overMainLI: function () {
|
||||
var $this = $(this);
|
||||
$this.siblings().removeClass('sfHover');
|
||||
$this.addClass('sfHover');
|
||||
menu.prototype.adaptSubMenuHeight();
|
||||
clearTimeout(menu.prototype.resetTimer);
|
||||
},
|
||||
|
||||
outMainLI: function () {
|
||||
clearTimeout(menu.prototype.resetTimer);
|
||||
menu.prototype.resetTimer = setTimeout(function() {
|
||||
$('.Menu-tabList > .sfHover', this.menuNode).removeClass('sfHover');
|
||||
$('.Menu-tabList > .sfActive', this.menuNode).addClass('sfHover');
|
||||
menu.prototype.adaptSubMenuHeight();
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
onItemClick: function (item) {
|
||||
$('.Menu--dashboard').trigger('piwikSwitchPage', item);
|
||||
broadcast.propagateAjax( $(item).attr('href').substr(1) );
|
||||
return false;
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this.menuNode = $('.Menu--dashboard');
|
||||
|
||||
this.menuNode.find("li:has(ul)").hover(this.overMainLI, this.outMainLI);
|
||||
|
||||
// add id to all li menu to support menu identification.
|
||||
// for all sub menu we want to have a unique id based on their module and action
|
||||
// for main menu we want to add just the module as its id.
|
||||
this.menuNode.find('li').each(function () {
|
||||
var url = $(this).find('a').attr('href').substr(1);
|
||||
var module = broadcast.getValueFromUrl("module", url);
|
||||
var action = broadcast.getValueFromUrl("action", url);
|
||||
var moduleId = broadcast.getValueFromUrl("idGoal", url) || broadcast.getValueFromUrl("idDashboard", url);
|
||||
var main_menu = $(this).parent().hasClass('Menu-tabList') ? true : false;
|
||||
if (main_menu) {
|
||||
$(this).attr({id: module});
|
||||
}
|
||||
// if there's a idGoal or idDashboard, use this in the ID
|
||||
else if (moduleId != '') {
|
||||
$(this).attr({id: module + '_' + action + '_' + moduleId});
|
||||
}
|
||||
else {
|
||||
$(this).attr({id: module + '_' + action});
|
||||
}
|
||||
});
|
||||
|
||||
menu.prototype.adaptSubMenuHeight();
|
||||
},
|
||||
|
||||
activateMenu: function (module, action, id) {
|
||||
this.menuNode.find('li').removeClass('sfHover').removeClass('sfActive');
|
||||
var $li = this.getSubmenuID(module, id, action);
|
||||
var mainLi = $("#" + module);
|
||||
if (!mainLi.length) {
|
||||
mainLi = $li.parents('li');
|
||||
}
|
||||
|
||||
mainLi.addClass('sfActive').addClass('sfHover');
|
||||
|
||||
$li.addClass('sfHover');
|
||||
},
|
||||
|
||||
// getting the right li is a little tricky since goals uses idGoal, and overview is index.
|
||||
getSubmenuID: function (module, id, action) {
|
||||
var $li = '';
|
||||
// So, if module is Goals, id is present, and action is not Index, must be one of the goals
|
||||
if (module == 'Goals' && id != '' && (action != 'index')) {
|
||||
$li = $("#" + module + "_" + action + "_" + id);
|
||||
// if module is Dashboard and id is present, must be one of the dashboards
|
||||
} else if (module == 'Dashboard') {
|
||||
if (!id) id = 1;
|
||||
$li = $("#" + module + "_" + action + "_" + id);
|
||||
} else {
|
||||
$li = $("#" + module + "_" + action);
|
||||
}
|
||||
return $li;
|
||||
},
|
||||
|
||||
loadFirstSection: function () {
|
||||
if (broadcast.isHashExists() == false) {
|
||||
$('li:first a:first', this.menuNode).click().addClass('sfHover').addClass('sfActive');
|
||||
}
|
||||
}
|
||||
};
|
||||
19
www/analytics/plugins/CoreHome/javascripts/menu_init.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
$(function () {
|
||||
var isPageHasMenu = $('.Menu--dashboard').size();
|
||||
var isPageIsAdmin = $('#content.admin').size();
|
||||
if (isPageHasMenu) {
|
||||
piwikMenu = new menu();
|
||||
piwikMenu.init();
|
||||
piwikMenu.loadFirstSection();
|
||||
}
|
||||
|
||||
if(isPageIsAdmin) {
|
||||
// don't use broadcast in admin pages
|
||||
return;
|
||||
}
|
||||
if(isPageHasMenu) {
|
||||
broadcast.init();
|
||||
} else {
|
||||
broadcast.init(true);
|
||||
}
|
||||
});
|
||||
198
www/analytics/plugins/CoreHome/javascripts/notification.js
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
/**
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($, require) {
|
||||
|
||||
var exports = require('piwik/UI');
|
||||
|
||||
/**
|
||||
* Creates a new notifications.
|
||||
*
|
||||
* Example:
|
||||
* var UI = require('piwik/UI');
|
||||
* var notification = new UI.Notification();
|
||||
* notification.show('My Notification Message', {title: 'Low space', context: 'warning'});
|
||||
*/
|
||||
var Notification = function () {
|
||||
this.$node = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes the notification visible.
|
||||
*
|
||||
* @param {string} message The actual message that will be displayed. Must be set.
|
||||
* @param {Object} [options]
|
||||
* @param {string} [options.id] Only needed for persistent notifications. The id will be sent to the
|
||||
* frontend once the user closes the notifications. The notification has to
|
||||
* be registered/notified under this name
|
||||
* @param {string} [options.title] The title of the notification. For instance the plugin name.
|
||||
* @param {bool} [options.animate=true] If enabled, the notification will be faded in.
|
||||
* @param {string} [options.context=warning] Context of the notification: 'info', 'warning', 'success' or
|
||||
* 'error'
|
||||
* @param {string} [options.type=transient] The type of the notification: Either 'toast' or 'transitent'
|
||||
* @param {bool} [options.noclear=false] If set, the close icon is not displayed.
|
||||
* @param {object} [options.style] Optional style/css dictionary. For instance {'display': 'inline-block'}
|
||||
* @param {string} [options.placeat] By default, the notification will be displayed in the "stats bar".
|
||||
* You can specify any other CSS selector to place the notifications
|
||||
* whereever you want.
|
||||
*/
|
||||
Notification.prototype.show = function (message, options) {
|
||||
if (!message) {
|
||||
throw new Error('No message given, cannot display notification');
|
||||
}
|
||||
if (options && !$.isPlainObject(options)) {
|
||||
throw new Error('Options has the wrong format, cannot display notification');
|
||||
} else if (!options) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if ('persistent' == options.type) {
|
||||
// otherwise it is never possible to dismiss the notification
|
||||
options.noclear = false;
|
||||
}
|
||||
|
||||
closeExistingNotificationHavingSameIdIfNeeded(options);
|
||||
|
||||
var template = generateNotificationHtmlMarkup(options, message);
|
||||
var $notificationNode = placeNotification(template, options);
|
||||
this.$node = $notificationNode;
|
||||
|
||||
if ('persistent' == options.type) {
|
||||
addPersistentEvent($notificationNode);
|
||||
} else if ('toast' == options.type) {
|
||||
addToastEvent($notificationNode);
|
||||
}
|
||||
|
||||
if (!options.noclear) {
|
||||
addCloseEvent($notificationNode);
|
||||
}
|
||||
};
|
||||
|
||||
Notification.prototype.scrollToNotification = function () {
|
||||
if (this.$node) {
|
||||
piwikHelper.lazyScrollTo(this.$node, 250);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Notification = Notification;
|
||||
|
||||
function closeExistingNotificationHavingSameIdIfNeeded(options)
|
||||
{
|
||||
if (!options.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $existingNode = $('.system.notification[data-id=' + options.id + ']');
|
||||
if ($existingNode && $existingNode.length) {
|
||||
$existingNode.remove();
|
||||
}
|
||||
}
|
||||
|
||||
function generateNotificationHtmlMarkup(options, message) {
|
||||
var template = buildNotificationStart(options);
|
||||
|
||||
if (!options.noclear) {
|
||||
template += buildClearButton();
|
||||
}
|
||||
|
||||
if (options.title) {
|
||||
template += buildTitle(options);
|
||||
}
|
||||
|
||||
template += message;
|
||||
template += buildNotificationEnd();
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
function buildNotificationStart(options) {
|
||||
var template = '<div class="notification system';
|
||||
|
||||
if (options.context) {
|
||||
template += ' notification-' + options.context;
|
||||
}
|
||||
|
||||
template += '"';
|
||||
|
||||
if (options.id) {
|
||||
template += ' data-id="' + options.id + '"';
|
||||
}
|
||||
|
||||
template += '>';
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
function buildNotificationEnd() {
|
||||
return '</div>';
|
||||
}
|
||||
|
||||
function buildClearButton() {
|
||||
return '<button type="button" class="close" data-dismiss="alert">×</button>';
|
||||
}
|
||||
|
||||
function buildTitle(options) {
|
||||
return '<strong>' + options.title + '</strong> ';
|
||||
}
|
||||
|
||||
function placeNotification(template, options) {
|
||||
|
||||
var $notificationNode = $(template);
|
||||
|
||||
if (options.style) {
|
||||
$notificationNode.css(options.style);
|
||||
}
|
||||
|
||||
$notificationNode = $notificationNode.hide();
|
||||
$(options.placeat || '#notificationContainer').append($notificationNode);
|
||||
|
||||
if (false === options.animate) {
|
||||
$notificationNode.show();
|
||||
} else {
|
||||
$notificationNode.fadeIn(1000);
|
||||
}
|
||||
|
||||
return $notificationNode;
|
||||
}
|
||||
|
||||
function addToastEvent($notificationNode)
|
||||
{
|
||||
setTimeout(function () {
|
||||
$notificationNode.fadeOut( 'slow', function() {
|
||||
$notificationNode.remove();
|
||||
$notificationNode = null;
|
||||
});
|
||||
}, 12 * 1000);
|
||||
}
|
||||
|
||||
function addCloseEvent($notificationNode) {
|
||||
$notificationNode.on('click', '.close', function (event) {
|
||||
if (event && event.delegateTarget) {
|
||||
$(event.delegateTarget).remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function addPersistentEvent($notificationNode) {
|
||||
var notificationId = $notificationNode.data('id');
|
||||
|
||||
if (!notificationId) {
|
||||
return;
|
||||
}
|
||||
|
||||
$notificationNode.on('click', '.close', function (event) {
|
||||
var ajaxHandler = new ajaxHelper();
|
||||
ajaxHandler.addParams({
|
||||
module: 'CoreHome',
|
||||
action: 'markNotificationAsRead'
|
||||
}, 'GET');
|
||||
ajaxHandler.addParams({notificationId: notificationId}, 'POST');
|
||||
ajaxHandler.send(true);
|
||||
});
|
||||
};
|
||||
|
||||
})(jQuery, require);
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
$(document).ready((function ($, require) {
|
||||
return function () {
|
||||
|
||||
var UI = require('piwik/UI');
|
||||
|
||||
var $notificationNodes = $('[data-role="notification"]');
|
||||
|
||||
$notificationNodes.each(function (index, notificationNode) {
|
||||
$notificationNode = $(notificationNode);
|
||||
var attributes = $notificationNode.data();
|
||||
var message = $notificationNode.html();
|
||||
|
||||
if (message) {
|
||||
var notification = new UI.Notification();
|
||||
attributes.animate = false;
|
||||
notification.show(message, attributes);
|
||||
}
|
||||
|
||||
$notificationNodes.remove();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
})(jQuery, require));
|
||||
254
www/analytics/plugins/CoreHome/javascripts/popover.js
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
var Piwik_Popover = (function () {
|
||||
|
||||
var container = false;
|
||||
var isOpen = false;
|
||||
var closeCallback = false;
|
||||
|
||||
var createContainer = function () {
|
||||
if (container === false) {
|
||||
container = $(document.createElement('div')).attr('id', 'Piwik_Popover');
|
||||
}
|
||||
};
|
||||
|
||||
var openPopover = function (title, dialogClass) {
|
||||
createContainer();
|
||||
|
||||
var options =
|
||||
{
|
||||
title: title,
|
||||
modal: true,
|
||||
width: '950px',
|
||||
position: ['center', 'center'],
|
||||
resizable: false,
|
||||
autoOpen: true,
|
||||
open: function (event, ui) {
|
||||
if (dialogClass) {
|
||||
$(this).parent().addClass(dialogClass).attr('style', '');
|
||||
}
|
||||
|
||||
$('.ui-widget-overlay').on('click.popover', function () {
|
||||
container.dialog('close');
|
||||
});
|
||||
},
|
||||
close: function (event, ui) {
|
||||
container.find('div.jqplot-target').trigger('piwikDestroyPlot');
|
||||
container[0].innerHTML = ''; // IE8 fix
|
||||
container.dialog('destroy').remove();
|
||||
globalAjaxQueue.abort();
|
||||
$('.ui-widget-overlay').off('click.popover');
|
||||
isOpen = false;
|
||||
broadcast.propagateNewPopoverParameter(false);
|
||||
require('piwik/UI').UIControl.cleanupUnusedControls();
|
||||
if (typeof closeCallback == 'function') {
|
||||
closeCallback();
|
||||
closeCallback = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
container.dialog(options);
|
||||
|
||||
// override the undocumented _title function to ensure that the title attribute is not escaped (according to jQueryUI bug #6016)
|
||||
container.data( "uiDialog" )._title = function(title) {
|
||||
title.html( this.options.title );
|
||||
};
|
||||
|
||||
isOpen = true;
|
||||
};
|
||||
|
||||
var centerPopover = function () {
|
||||
if (container !== false) {
|
||||
container.dialog({position: ['center', 'center']});
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
/**
|
||||
* Open the popover with a loading message
|
||||
*
|
||||
* @param {string} popoverName name of the popover
|
||||
* @param {string} [popoverSubject] subject of the popover (e.g. url, optional)
|
||||
* @param {int} [height] height of the popover in px (optional)
|
||||
* @param {string} [dialogClass] css class to add to dialog
|
||||
*/
|
||||
showLoading: function (popoverName, popoverSubject, height, dialogClass) {
|
||||
var loading = $(document.createElement('div')).addClass('Piwik_Popover_Loading');
|
||||
|
||||
var loadingMessage = popoverSubject ? translations.General_LoadingPopoverFor :
|
||||
translations.General_LoadingPopover;
|
||||
|
||||
loadingMessage = loadingMessage.replace(/%s/, popoverName);
|
||||
|
||||
var p1 = $(document.createElement('p')).addClass('Piwik_Popover_Loading_Name');
|
||||
loading.append(p1.text(loadingMessage));
|
||||
|
||||
var p2;
|
||||
if (popoverSubject) {
|
||||
popoverSubject = piwikHelper.addBreakpointsToUrl(popoverSubject);
|
||||
p1.addClass('Piwik_Popover_Loading_NameWithSubject');
|
||||
p2 = $(document.createElement('p')).addClass('Piwik_Popover_Loading_Subject');
|
||||
loading.append(p2.html(popoverSubject));
|
||||
}
|
||||
|
||||
if (height) {
|
||||
loading.height(height);
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
openPopover(null, dialogClass);
|
||||
}
|
||||
|
||||
this.setContent(loading);
|
||||
this.setTitle('');
|
||||
|
||||
if (height) {
|
||||
var offset = loading.height() - p1.outerHeight();
|
||||
if (popoverSubject) {
|
||||
offset -= p2.outerHeight();
|
||||
}
|
||||
var spacingEl = $(document.createElement('div'));
|
||||
spacingEl.height(Math.round(offset / 2));
|
||||
loading.prepend(spacingEl);
|
||||
}
|
||||
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a help button to the current popover
|
||||
*
|
||||
* @param {string} helpUrl
|
||||
*/
|
||||
addHelpButton: function (helpUrl) {
|
||||
if (!isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
var titlebar = container.parent().find('.ui-dialog-titlebar');
|
||||
|
||||
var button = $(document.createElement('a')).addClass('ui-dialog-titlebar-help');
|
||||
button.attr({href: helpUrl, target: '_blank'});
|
||||
|
||||
titlebar.append(button);
|
||||
},
|
||||
|
||||
/** Set the title of the popover */
|
||||
setTitle: function (titleHtml) {
|
||||
container.dialog('option', 'title', titleHtml);
|
||||
},
|
||||
|
||||
/** Set inner HTML of the popover */
|
||||
setContent: function (html) {
|
||||
if (typeof closeCallback == 'function') {
|
||||
closeCallback();
|
||||
closeCallback = false;
|
||||
}
|
||||
|
||||
container[0].innerHTML = ''; // IE8 fix
|
||||
container.html(html);
|
||||
centerPopover();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show an error message. All params are HTML!
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {string} [message]
|
||||
* @param {string} [backLabel]
|
||||
*/
|
||||
showError: function (title, message, backLabel) {
|
||||
var error = $(document.createElement('div')).addClass('Piwik_Popover_Error');
|
||||
|
||||
var p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Title');
|
||||
error.append(p.html(title));
|
||||
|
||||
if (message) {
|
||||
p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Message');
|
||||
error.append(p.html(message));
|
||||
}
|
||||
|
||||
if (backLabel) {
|
||||
var back = $(document.createElement('a')).addClass('Piwik_Popover_Error_Back');
|
||||
back.attr('href', '#').click(function () {
|
||||
history.back();
|
||||
return false;
|
||||
});
|
||||
error.append(back.html(backLabel));
|
||||
}
|
||||
|
||||
if (!isOpen) {
|
||||
openPopover();
|
||||
}
|
||||
|
||||
this.setContent(error);
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a callback for the next time the popover is closed or the content changes
|
||||
*
|
||||
* @param {function} callback
|
||||
*/
|
||||
onClose: function (callback) {
|
||||
closeCallback = callback;
|
||||
},
|
||||
|
||||
/** Close the popover */
|
||||
close: function () {
|
||||
if (isOpen) {
|
||||
container.dialog('close');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a Popover and load the specified URL in it.
|
||||
*
|
||||
* Note: If you want the popover to be persisted in the URL (so if the URL is copy/pasted
|
||||
* to a new window/tab it will be opened there), use broadcast.propagateNewPopoverParameter
|
||||
* with a popover handler function that calls this one.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {string} loadingName
|
||||
* @param {string} [dialogClass] css class to add to dialog
|
||||
*/
|
||||
createPopupAndLoadUrl: function (url, loadingName, dialogClass) {
|
||||
// make sure the minimum top position of the popover is 15px
|
||||
var ensureMinimumTop = function () {
|
||||
var popoverContainer = $('#Piwik_Popover').parent();
|
||||
if (popoverContainer.position().top < 106) {
|
||||
popoverContainer.css('top', '15px');
|
||||
}
|
||||
};
|
||||
|
||||
// open the popover
|
||||
var box = Piwik_Popover.showLoading(loadingName, null, null, dialogClass);
|
||||
ensureMinimumTop();
|
||||
|
||||
var callback = function (html) {
|
||||
function setPopoverTitleIfOneFoundInContainer() {
|
||||
var title = $('h1,h2', container);
|
||||
if (title.length == 1) {
|
||||
Piwik_Popover.setTitle(title.text());
|
||||
$(title).hide();
|
||||
}
|
||||
}
|
||||
|
||||
Piwik_Popover.setContent(html);
|
||||
setPopoverTitleIfOneFoundInContainer();
|
||||
ensureMinimumTop();
|
||||
};
|
||||
var ajaxRequest = new ajaxHelper();
|
||||
ajaxRequest.addParams(piwikHelper.getArrayFromQueryString(url), 'get');
|
||||
ajaxRequest.setCallback(callback);
|
||||
ajaxRequest.setFormat('html');
|
||||
ajaxRequest.send(false);
|
||||
}
|
||||
};
|
||||
})();
|
||||
12
www/analytics/plugins/CoreHome/javascripts/promo.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
$(function () {
|
||||
$('#piwik-promo-thumbnail').click(function () {
|
||||
var promoEmbed = $('#piwik-promo-embed'),
|
||||
widgetWidth = $(this).closest('.widgetContent').width(),
|
||||
height = (266 * widgetWidth) / 421,
|
||||
embedHtml = '<iframe width="100%" height="' + height + '" src="http://www.youtube.com/embed/OslfF_EH81g?autoplay=1&vq=hd720&wmode=transparent" frameborder="0" wmode="Opaque"></iframe>';
|
||||
|
||||
$(this).hide();
|
||||
promoEmbed.height(height).html(embedHtml);
|
||||
promoEmbed.show();
|
||||
});
|
||||
});
|
||||
39
www/analytics/plugins/CoreHome/javascripts/require.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* Module creation & inclusion for Piwik.
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function (window) {
|
||||
|
||||
var MODULE_SPLIT_REGEX = /[\/.\\]/;
|
||||
|
||||
/**
|
||||
* Returns a module for its ID. Empty modules are created if they does not exist.
|
||||
*
|
||||
* Modules are currently stored in the window object.
|
||||
*
|
||||
* @param {String} moduleId e.g. 'piwik/UserCountryMap' or 'myPlugin/Widgets/FancySchmancyThing'.
|
||||
* The following characters can be used to separate individual modules:
|
||||
* '/', '.' or '\'.
|
||||
* @return {Object} The module object.
|
||||
*/
|
||||
window.require = function (moduleId) {
|
||||
var parts = moduleId.split(MODULE_SPLIT_REGEX);
|
||||
|
||||
// TODO: we use window objects for backwards compatibility. when rest of Piwik is rewritten to use
|
||||
// require, we can switch simply holding the modules in a private variable.
|
||||
var currentModule = window;
|
||||
for (var i = 0; i != parts.length; ++i) {
|
||||
var part = parts[i];
|
||||
|
||||
currentModule[part] = currentModule[part] || {};
|
||||
currentModule = currentModule[part];
|
||||
}
|
||||
return currentModule;
|
||||
};
|
||||
|
||||
})(window);
|
||||
83
www/analytics/plugins/CoreHome/javascripts/sparkline.js
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($) {
|
||||
|
||||
var sparklineColorNames = ['backgroundColor', 'lineColor', 'minPointColor', 'maxPointColor', 'lastPointColor'];
|
||||
|
||||
piwik.getSparklineColors = function () {
|
||||
return piwik.ColorManager.getColors('sparkline-colors', sparklineColorNames);
|
||||
};
|
||||
|
||||
// initializes each sparkline so they use colors defined in CSS
|
||||
piwik.initSparklines = function() {
|
||||
$('.sparkline > img').each(function () {
|
||||
var $self = $(this);
|
||||
|
||||
if ($self.attr('src')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var colors = JSON.stringify(piwik.getSparklineColors());
|
||||
var appendToSparklineUrl = '&colors=' + encodeURIComponent(colors);
|
||||
|
||||
// Append the token_auth to the URL if it was set (eg. embed dashboard)
|
||||
var token_auth = broadcast.getValueFromUrl('token_auth');
|
||||
if (token_auth.length) {
|
||||
appendToSparklineUrl += '&token_auth=' + token_auth;
|
||||
}
|
||||
$self.attr('src', $self.attr('data-src') + appendToSparklineUrl);
|
||||
});
|
||||
};
|
||||
|
||||
window.initializeSparklines = function () {
|
||||
var sparklineUrlParamsToIgnore = ['module', 'action', 'idSite', 'period', 'date', 'viewDataTable'];
|
||||
|
||||
$("[data-graph-id]").each(function () {
|
||||
var graph = $(this);
|
||||
|
||||
// try to find sparklines and add them clickable behaviour
|
||||
graph.parent().find('div.sparkline').each(function () {
|
||||
// find the sparkline and get it's src attribute
|
||||
var sparklineUrl = $('img', this).attr('data-src');
|
||||
|
||||
if (sparklineUrl != "") {
|
||||
var params = broadcast.getValuesFromUrl(sparklineUrl);
|
||||
for (var i = 0; i != sparklineUrlParamsToIgnore.length; ++i) {
|
||||
delete params[sparklineUrlParamsToIgnore[i]];
|
||||
}
|
||||
for (var key in params) {
|
||||
if (typeof params[key] == 'undefined') {
|
||||
// this happens for example with an empty segment parameter
|
||||
delete params[key];
|
||||
} else {
|
||||
params[key] = decodeURIComponent(params[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// on click, reload the graph with the new url
|
||||
$(this).click(function () {
|
||||
var reportId = graph.attr('data-graph-id'),
|
||||
dataTable = $(require('piwik/UI').DataTable.getDataTableByReport(reportId));
|
||||
|
||||
// when the metrics picker is used, the id of the data table might be updated (which is correct behavior).
|
||||
// for example, in goal reports it might change from GoalsgetEvolutionGraph to GoalsgetEvolutionGraph1.
|
||||
// if this happens, we can't find the graph using $('#'+idDataTable+"Chart");
|
||||
// instead, we just use the first evolution graph we can find.
|
||||
if (dataTable.length == 0) {
|
||||
dataTable = $('div.dataTableVizEvolution');
|
||||
}
|
||||
|
||||
// reload the datatable w/ a new column & scroll to the graph
|
||||
dataTable.trigger('reload', params);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}(jQuery));
|
||||
27
www/analytics/plugins/CoreHome/javascripts/top_controls.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/*!
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
function initTopControls() {
|
||||
var $topControlsContainer = $('.top_controls'),
|
||||
left = 0;
|
||||
|
||||
if ($topControlsContainer.length) {
|
||||
$('.piwikTopControl').each(function () {
|
||||
var $control = $(this);
|
||||
if ($control.css('display') == 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
$control.css('left', left);
|
||||
|
||||
if (!$.contains($topControlsContainer[0], this)) {
|
||||
$control.detach().appendTo($topControlsContainer);
|
||||
}
|
||||
|
||||
left += $control.outerWidth(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
113
www/analytics/plugins/CoreHome/javascripts/uiControl.js
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/**
|
||||
* Piwik - Web Analytics
|
||||
*
|
||||
* Visitor profile popup control.
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
*/
|
||||
|
||||
(function ($, require) {
|
||||
|
||||
var exports = require('piwik/UI');
|
||||
|
||||
/**
|
||||
* Base type for Piwik UI controls. Provides functionality that all controls need (such as
|
||||
* cleanup on destruction).
|
||||
*
|
||||
* @param {Element} element The root element of the control.
|
||||
*/
|
||||
var UIControl = function (element) {
|
||||
if (!element) {
|
||||
throw new Error("no element passed to UIControl constructor");
|
||||
}
|
||||
|
||||
this._controlId = UIControl._controls.length;
|
||||
UIControl._controls.push(this);
|
||||
|
||||
var $element = this.$element = $(element);
|
||||
$element.data('uiControlObject', this);
|
||||
|
||||
var params = JSON.parse($element.attr('data-params') || '{}');
|
||||
for (var key in params) { // convert values in params that are arrays to comma separated string lists
|
||||
if (params[key] instanceof Array) {
|
||||
params[key] = params[key].join(',');
|
||||
}
|
||||
}
|
||||
this.param = params;
|
||||
|
||||
this.props = JSON.parse($element.attr('data-props') || '{}');
|
||||
};
|
||||
|
||||
/**
|
||||
* Contains all active control instances.
|
||||
*/
|
||||
UIControl._controls = [];
|
||||
|
||||
/**
|
||||
* Utility method that will clean up all piwik UI controls whose elements are not attached
|
||||
* to the DOM.
|
||||
*
|
||||
* TODO: instead of having other pieces of the UI manually calling cleanupUnusedControls,
|
||||
* MutationObservers should be used
|
||||
*/
|
||||
UIControl.cleanupUnusedControls = function () {
|
||||
var controls = UIControl._controls;
|
||||
|
||||
for (var i = 0; i != controls.length; ++i) {
|
||||
var control = controls[i];
|
||||
if (control
|
||||
&& control.$element
|
||||
&& !$.contains(document.documentElement, control.$element[0])
|
||||
) {
|
||||
controls[i] = null;
|
||||
control._destroy();
|
||||
|
||||
if (!control._baseDestroyCalled) {
|
||||
throw new Error("Error: " + control.constructor.name + "'s destroy method does not call " +
|
||||
"UIControl.destroy. You may have a memory leak.");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
UIControl.initElements = function (klass, selector) {
|
||||
$(selector).each(function () {
|
||||
if (!$(this).attr('data-inited')) {
|
||||
var control = new klass(this);
|
||||
$(this).attr('data-inited', 1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
UIControl.prototype = {
|
||||
|
||||
/**
|
||||
* Perform cleanup. Called when the control has been removed from the DOM. Derived
|
||||
* classes should overload this function to perform their own cleanup.
|
||||
*/
|
||||
_destroy: function () {
|
||||
this.$element.removeData('uiControlObject');
|
||||
delete this.$element;
|
||||
|
||||
this._baseDestroyCalled = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the widget resize event, if we're currently in a widget.
|
||||
*
|
||||
* TODO: should use proper resize detection (see
|
||||
* http://www.backalleycoder.com/2013/03/18/cross-browser-event-based-element-resize-detection/ )
|
||||
* with timeouts (since resizing widgets can be expensive)
|
||||
*/
|
||||
onWidgetResize: function (handler) {
|
||||
var $widget = this.$element.closest('.widgetContent');
|
||||
$widget.on('widget:maximise', handler)
|
||||
.on('widget:minimise', handler)
|
||||
.on('widget:resize', handler);
|
||||
}
|
||||
};
|
||||
|
||||
exports.UIControl = UIControl;
|
||||
|
||||
})(jQuery, require);
|
||||
125
www/analytics/plugins/CoreHome/stylesheets/_donate.less
Executable file
|
|
@ -0,0 +1,125 @@
|
|||
.piwik-donate-call {
|
||||
padding: 1em;
|
||||
border: 1px solid #CCC;
|
||||
border-radius: 4px;
|
||||
max-width: 432px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#piwik-worth {
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
display: block;
|
||||
margin: 0 1em 0 1em;
|
||||
}
|
||||
|
||||
.piwik-donate-slider {
|
||||
margin: 1em 0 1em 1em;
|
||||
}
|
||||
|
||||
.piwik-donate-slider > .slider-range {
|
||||
vertical-align: top;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
border: 1px solid #999;
|
||||
|
||||
background-color: #f7f7f7;
|
||||
|
||||
border-radius: 6px;
|
||||
|
||||
height: 14px;
|
||||
width: 270px;
|
||||
margin: 22px 8px 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.piwik-donate-slider .slider-position {
|
||||
border: 1px solid #999;
|
||||
background-color: #CCC;
|
||||
|
||||
border-radius: 3px;
|
||||
|
||||
height: 18px;
|
||||
width: 10px;
|
||||
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -1px;
|
||||
}
|
||||
|
||||
.piwik-donate-slider .slider-donate-amount {
|
||||
display: inline-block;
|
||||
|
||||
padding: .3em .5em .3em .5em;
|
||||
margin: 16px 8px 0 0;
|
||||
vertical-align: top;
|
||||
width: 48px;
|
||||
text-align: center;
|
||||
background-color: #CCC;
|
||||
color: #333;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.piwik-donate-slider .slider-smiley-face {
|
||||
margin: 8px 0 8px 0;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.piwik-donate-call .donate-submit {
|
||||
min-height: 55px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.piwik-donate-call .donate-submit input {
|
||||
margin-left: 13px;
|
||||
border-style: none;
|
||||
background-image: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.piwik-donate-call .donate-submit a {
|
||||
display: inline-block;
|
||||
margin-left: 1.2em;
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.piwik-donate-call .donate-submit a.donate-spacer {
|
||||
margin-bottom:.5em;
|
||||
visibility:hidden;
|
||||
}
|
||||
|
||||
.piwik-donate-call .donate-submit a.donate-one-time {
|
||||
position: absolute;
|
||||
bottom: .5em;
|
||||
right: 1.2em;
|
||||
}
|
||||
|
||||
.piwik-donate-call > .piwik-donate-message {
|
||||
margin-bottom: .5em;
|
||||
}
|
||||
|
||||
.piwik-donate-call > .piwik-donate-message p {
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.piwik-donate-call > .form-description {
|
||||
margin-top: 1.25em;
|
||||
}
|
||||
|
||||
.donate-form-instructions {
|
||||
font-size: .8em;
|
||||
margin: 0 1.25em 0 1.25em;
|
||||
color: #666;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.widget .piwik-donate-call {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
.widget .piwik-donate-slider > .slider-range {
|
||||
width: 205px;
|
||||
}
|
||||
55
www/analytics/plugins/CoreHome/stylesheets/cloud.less
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
.tagCloud {
|
||||
padding: 10px;
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.word a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.word {
|
||||
padding: 4px 8px 4px 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.valueIsZero {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
span.size0, span.size0 a {
|
||||
color: #255792;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
span.size1, span.size1 a {
|
||||
color: #255792;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
span.size2, span.size2 a {
|
||||
color: #255792;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
span.size3, span.size3 a {
|
||||
color: #255792;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span.size4, span.size4 a {
|
||||
color: #255792;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
span.size5, span.size5 a {
|
||||
color: #255792;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
span.size6, span.size6 a {
|
||||
color: #255792;
|
||||
font-size: 11px;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
.color-manager {
|
||||
color: transparent;
|
||||
}
|
||||
260
www/analytics/plugins/CoreHome/stylesheets/coreHome.less
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
h1 {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #7e7363;
|
||||
padding: 3px 0 7px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 18px;
|
||||
font-weight: normal;
|
||||
color: #7e7363;
|
||||
padding: 12px 0 7px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
h2 a {
|
||||
text-decoration: none;
|
||||
color: #7e7363;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.3em;
|
||||
margin-top: 2em;
|
||||
color: #1D3256;
|
||||
}
|
||||
|
||||
.home p {
|
||||
padding-bottom: 1em;
|
||||
margin-right: 1em;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
.nav_sep {
|
||||
height: 39px;
|
||||
border-radius: 0 4px 0 0;
|
||||
border: 1px solid #DDD;
|
||||
}
|
||||
|
||||
.pageWrap {
|
||||
border-left: 1px solid #DDDDDD;
|
||||
border-right: 1px solid #DDDDDD;
|
||||
min-height: 10px;
|
||||
overflow: visible;
|
||||
padding: 15px 15px 0;
|
||||
position: relative;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* Content */
|
||||
#content.home {
|
||||
padding-top: 5px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 2 columns reports */
|
||||
#leftcolumn {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#rightcolumn {
|
||||
float: right;
|
||||
width: 45%;
|
||||
}
|
||||
|
||||
/* not in widget */
|
||||
.widget #leftcolumn, .widget #rightcolumn {
|
||||
float: left;
|
||||
padding: 0 10px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Calendar*/
|
||||
div.ui-datepicker {
|
||||
font-size: 62.5%;
|
||||
}
|
||||
|
||||
.ui-datepicker-current-period a, .ui-datepicker-current-period a:link, .ui-datepicker-current-period a:visited {
|
||||
border: 1px solid #2E85FF;
|
||||
color: #2E85FF;
|
||||
}
|
||||
|
||||
#otherPeriods a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#otherPeriods a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#currentPeriod {
|
||||
border-bottom: 1px dotted #520202;
|
||||
}
|
||||
|
||||
.hoverPeriod {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
border-bottom: 1px solid #520202;
|
||||
}
|
||||
|
||||
#calendarRangeTo {
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
#calendarRangeFrom {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#inputCalendarFrom, #inputCalendarTo {
|
||||
margin-left: 10px;
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
#calendarRangeApply {
|
||||
display: none;
|
||||
margin-top: 10px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#invalidDateRange {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div .sparkline {
|
||||
float: left;
|
||||
clear: both;
|
||||
padding-bottom: 1px;
|
||||
margin-top: 10px;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
.sparkline img {
|
||||
vertical-align: middle;
|
||||
padding-right: 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
div.pk-emptyGraph {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 10px;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Popover
|
||||
* @see popover.js
|
||||
*/
|
||||
|
||||
#Piwik_Popover {
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Loading_Name {
|
||||
padding: 50px 0 65px 0;
|
||||
font-size: 16px;
|
||||
line-height: 20px;
|
||||
font-weight: normal;
|
||||
text-align: center;
|
||||
background: url(plugins/Zeitgeist/images/loading-blue.gif) no-repeat center 20px;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Loading_NameWithSubject {
|
||||
padding-bottom: 30px;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Loading_Subject {
|
||||
padding: 0 70px 55px 70px;
|
||||
color: #7e7363;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Error {
|
||||
padding: 50px 20px 65px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Error_Title {
|
||||
color: #E87500;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Error_Title span {
|
||||
color: #222;
|
||||
font-weight: normal;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.Piwik_Popover_Error_Message {
|
||||
color: #7e7363;
|
||||
padding: 20px 0 0 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
a.Piwik_Popover_Error_Back {
|
||||
display: block;
|
||||
margin: 20px 0 0 0;
|
||||
color: #1D3256;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#alert.ui-confirm input {
|
||||
display: block;
|
||||
margin: 10px auto 5px !important;
|
||||
}
|
||||
|
||||
.header_short #updateCheckLinkContainer .icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.header_full #updateCheckLinkContainer {
|
||||
margin-top: -2px;
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
.header_full #updateCheckLinkContainer {
|
||||
margin-top: -3px;
|
||||
}
|
||||
}
|
||||
|
||||
#updateCheckLinkContainer {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#updateCheckLinkContainer:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#updateCheckLinkContainer {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#updateCheckLinkContainer>* {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#header_message #checkForUpdates {
|
||||
font-size: 10px;
|
||||
line-height: 16px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
#header_message #checkForUpdates {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header_message #checkForUpdates:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Used to link within content text, without adding visual clutter */
|
||||
.linkContent { color:#333; text-decoration:none}
|
||||
.linkContent:hover { text-decoration:underline;}
|
||||
10
www/analytics/plugins/CoreHome/stylesheets/dataTable.less
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
@dataTable-link-color: #255792;
|
||||
@dataTable-header-background: #e4e2d7;
|
||||
@dataTable-headerActive-background: #D5D3C8;
|
||||
|
||||
@import "dataTable/_dataTable.less";
|
||||
@import "dataTable/_limitSelection.less";
|
||||
@import "dataTable/_reportDocumentation.less";
|
||||
@import "dataTable/_rowActions.less";
|
||||
@import "dataTable/_subDataTable.less";
|
||||
@import "dataTable/_tableConfiguration.less";
|
||||
|
|
@ -0,0 +1,613 @@
|
|||
/* main data table */
|
||||
.dataTable {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
border-spacing: 0;
|
||||
margin: 0;
|
||||
|
||||
td .ratio {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
display:none;
|
||||
text-align: right;
|
||||
min-width: 45px;
|
||||
margin-left: 4px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
td.highlight > .ratio {
|
||||
display: inline-block;
|
||||
line-height: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
div.dataTable {
|
||||
position:relative;
|
||||
}
|
||||
|
||||
table.dataTable td.label,
|
||||
table.subDataTable td.label {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable img,
|
||||
table.subDataTable img {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table.dataTable img {
|
||||
border: 0;
|
||||
margin-right: 1em;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.dataTable tr.subDataTable {
|
||||
cursor: pointer;
|
||||
|
||||
td.label span.label {
|
||||
word-break: break-all;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
width: inherit;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
table.dataTable th {
|
||||
margin: 0;
|
||||
color: @dataTable-link-color;
|
||||
text-align: left;
|
||||
padding: 6px 6px 6px 12px;
|
||||
background: @dataTable-header-background;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
border-left: 1px solid #d4d0c4;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.dataTable th.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
table.dataTable th.columnSorted {
|
||||
font-weight: bold;
|
||||
padding-right: 20px;
|
||||
background: @dataTable-headerActive-background;
|
||||
}
|
||||
|
||||
table.dataTable td {
|
||||
padding: 5px 5px 5px 12px;
|
||||
background: #fff;
|
||||
border-left: 1px solid #e7e7e7;
|
||||
}
|
||||
|
||||
table.dataTable td,
|
||||
table.dataTable td a {
|
||||
margin: 0;
|
||||
text-decoration: none;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
table.dataTable tr:hover > td,
|
||||
table.dataTable tr:hover > td .dataTableRowActions {
|
||||
background-color: #FFFFF7;
|
||||
}
|
||||
|
||||
table.dataTable tr.subDataTable:hover > td,
|
||||
table.dataTable tr.subDataTable:hover > td .dataTableRowActions {
|
||||
background-color: #ffffcb;
|
||||
}
|
||||
|
||||
table.dataTable tr:hover > td.cellSubDataTable
|
||||
table.dataTable tr:hover > td.cellSubDataTable .dataTableRowActions {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
td.clean {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
table.dataTable td.column {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.dataTable td.columneven {
|
||||
background: #efeeec;
|
||||
}
|
||||
|
||||
table.dataTable td.columnodd {
|
||||
background: #f6f5f3;
|
||||
}
|
||||
|
||||
.dataTable tr.highlight td {
|
||||
background-color: #ECF9DD;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table.dataTable td.label,
|
||||
table.subActionsDataTable td.label,
|
||||
table.actionsDataTable td.label {
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
table.dataTable th.label {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.dataTableActions table.dataTable th.label {
|
||||
/* Ensures tables have enough space to display subtable on click, and prevent the jumping effect */
|
||||
min-width: 250px;
|
||||
}
|
||||
|
||||
table.dataTable span.label.highlighted {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
/* the cell containing the subdatatable */
|
||||
table.dataTable .cellSubDataTable {
|
||||
margin: 0;
|
||||
border-left: 0;
|
||||
padding: 6px 12px 6px;
|
||||
}
|
||||
|
||||
.cellSubDataTable > .dataTable {
|
||||
padding: 6px 0 0;
|
||||
}
|
||||
|
||||
/* A link in a column in the DataTable */
|
||||
table.dataTable td #urlLink {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.dataTable img {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.dataTable > .dataTableWrapper {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
.subDataTable > .dataTableWrapper {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.sortIconContainer {
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sortIcon {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.datatableFooterMessage {
|
||||
color: #888;
|
||||
text-align: left;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.dataTablePages {
|
||||
color: #BFBFBF;
|
||||
font-weight: bold;
|
||||
margin: 10px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dataTableSearchPattern {
|
||||
margin: 5px 0 2px 0;
|
||||
height: 20px;
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
background: url(plugins/Zeitgeist/images/search_bg.png) no-repeat center 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dataTableSearchPattern input {
|
||||
vertical-align: top;
|
||||
font-size: 10px;
|
||||
color: #454545;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
width: 21px;
|
||||
height: 17px;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
filter: Alpha(opacity=0);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.dataTableSearchPattern .searchInput {
|
||||
width: 114px;
|
||||
height: auto;
|
||||
overflow: visible;
|
||||
padding: 2px 6px;
|
||||
opacity: 1;
|
||||
cursor: text;
|
||||
filter: Alpha(opacity=100);
|
||||
}
|
||||
|
||||
.dataTableNext,
|
||||
.dataTablePrevious {
|
||||
font-size: 12px;
|
||||
color: #184A83;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.datatableRelatedReports {
|
||||
color: #888;
|
||||
font-size: 11px;
|
||||
padding-bottom: 5px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
#dashboard .datatableRelatedReports {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.datatableRelatedReports span {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dataTableFeatures {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dataTableFooterNavigation {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.dataTableNext,
|
||||
.dataTablePrevious,
|
||||
.dataTableSearchPattern {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dataTableFeatures .loadingPiwik {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.subDataTable .dataTableFooterIcons {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.dataTable .loadingPiwikBelow {
|
||||
padding-bottom: 5px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.dataTableFooterIcons div {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
#dashboard {
|
||||
|
||||
.dataTableFeatures {
|
||||
&.expanded {
|
||||
.dataTableFooterIcons {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.expandDataTableFooterDrawer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.hasEvolution {
|
||||
.dataTableFooterIcons {
|
||||
margin-top: 17px;
|
||||
}
|
||||
.expandDataTableFooterDrawer {
|
||||
margin-top: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.expandDataTableFooterDrawer {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 0px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background-color: #D9D9D9;
|
||||
height: 7px;
|
||||
width: 70px;
|
||||
-webkit-border-top-left-radius: 10px;
|
||||
-webkit-border-top-right-radius: 10px;
|
||||
-moz-border-radius-topleft: 10px;
|
||||
-moz-border-radius-topright: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
line-height: 0px;
|
||||
|
||||
img {
|
||||
margin-bottom: 0px;
|
||||
line-height: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dataTableFooterIcons {
|
||||
|
||||
display: none;
|
||||
margin-top: 5px;
|
||||
height: auto;
|
||||
|
||||
div {
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.foldDataTableFooterDrawer {
|
||||
display: block;
|
||||
padding-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background-color: #D9D9D9;
|
||||
-webkit-border-top-left-radius: 10px;
|
||||
-webkit-border-top-right-radius: 10px;
|
||||
-moz-border-radius-topleft: 10px;
|
||||
-moz-border-radius-topright: 10px;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
line-height: 0px;
|
||||
height: 7px;
|
||||
width: 70px;
|
||||
clear: both;
|
||||
|
||||
img {
|
||||
margin-top: 2px;
|
||||
margin-bottom: 0px;
|
||||
line-height: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.controls {
|
||||
padding: 15px 0px;
|
||||
text-align: left;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.dataTableFooterIcons .foldDataTableFooterDrawer,
|
||||
.dataTableFeatures .expandDataTableFooterDrawer {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@-moz-document url-prefix() {
|
||||
|
||||
#dashboard .dataTableFeatures .expandDataTableFooterDrawer {
|
||||
line-height: 1px;
|
||||
|
||||
img {
|
||||
margin-bottom: 1px;
|
||||
line-height: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dataTableFooterIcons {
|
||||
height: 20px;
|
||||
white-space: nowrap;
|
||||
font-size: 10px;
|
||||
padding: 6px 5px;
|
||||
border-top: 1px solid #B6B0A6;
|
||||
}
|
||||
|
||||
.dataTableFooterWrap {
|
||||
position: relative;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.dataTableFooterWrap select {
|
||||
float: left;
|
||||
margin: 1px 0 1px 10px;
|
||||
}
|
||||
|
||||
.tableIcon {
|
||||
background: #f2f1ed;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin: 0 1px 0 0;
|
||||
padding: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.tableIcon:hover {
|
||||
background: #e9e8e1;
|
||||
}
|
||||
|
||||
.activeIcon {
|
||||
background: #e9e8e1;
|
||||
}
|
||||
|
||||
.tableIconsGroup > span > span {
|
||||
position:relative;
|
||||
float:left;
|
||||
}
|
||||
|
||||
.dataTableFooterActiveItem {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.exportToFormatItems {
|
||||
background: #dcdacf;
|
||||
float: left;
|
||||
margin: 0 1px 0 0;
|
||||
padding: 4px 6px 3px 6px;
|
||||
color: #968d7f;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.exportToFormatItems img {
|
||||
vertical-align: middle;
|
||||
margin: -4px -3px -2px 2px;
|
||||
}
|
||||
|
||||
.tableIconsGroup {
|
||||
float: left;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.tableIconsGroup .tableIcon span {
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.tableIconsGroup img {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.tableIconsGroupActive {
|
||||
display: block;
|
||||
float: left;
|
||||
background: #dcdacf;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.tableIconsGroupActive .tableIcon {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.tableIconsGroupActive .tableIcon:hover {
|
||||
background: #e9e8e1;
|
||||
}
|
||||
|
||||
.exportToFormatIcons,
|
||||
.dataTableFooterIconsShow {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.dataTableFooterIcons,
|
||||
.dataTableFooterIcons a {
|
||||
text-decoration: none;
|
||||
color: @dataTable-link-color;
|
||||
}
|
||||
|
||||
.dataTableSpacer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* Actions table */
|
||||
.dataTableActions table.dataTable tr td.labelodd {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
/* levels higher than 4 have a default padding left */
|
||||
.actionsDataTable tr td.label {
|
||||
padding-left: 7em;
|
||||
}
|
||||
|
||||
tr.level0 td.label {
|
||||
padding-left: 1.5em;
|
||||
}
|
||||
|
||||
tr.level1 td.label {
|
||||
padding-left: 2.5em;
|
||||
}
|
||||
|
||||
tr.level2 td.label {
|
||||
padding-left: 3.5em;
|
||||
}
|
||||
|
||||
tr.level3 td.label {
|
||||
padding-left: 4.5em;
|
||||
}
|
||||
|
||||
tr.level4 td.label {
|
||||
padding-left: 5em;
|
||||
}
|
||||
tr.level5 td.label {
|
||||
padding-left: 5.5em;
|
||||
}
|
||||
tr.level6 td.label {
|
||||
padding-left: 6em;
|
||||
}
|
||||
tr.level7 td.label {
|
||||
padding-left: 6.5em;
|
||||
}
|
||||
tr.level8 td.label {
|
||||
padding-left: 7em;
|
||||
}
|
||||
tr.level9 td.label {
|
||||
padding-left: 7.5em;
|
||||
}
|
||||
tr.level10 td.label {
|
||||
padding-left: 8em;
|
||||
}
|
||||
tr.level11 td.label {
|
||||
padding-left: 8.5em;
|
||||
}
|
||||
tr.level12 td.label {
|
||||
padding-left: 9em;
|
||||
}
|
||||
|
||||
/* less right margins for the link image in the Pa*/
|
||||
.dataTableActions table.dataTable img.link {
|
||||
margin-right: 0.5em;
|
||||
margin-left: -0.5em;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
tr td.label img.plusMinus {
|
||||
margin-left: -1em;
|
||||
margin-right: 3px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
|
||||
.pk-emptyDataTable {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 10px;
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.helpDate {
|
||||
color: #777777;
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
padding: 4px;
|
||||
text-align: right;
|
||||
display: block;
|
||||
}
|
||||
|
||||
body .ui-tooltip.rowActionTooltip {
|
||||
font-size: 11px;
|
||||
padding: 3px 5px 3px 6px;
|
||||
}
|
||||
|
||||
table.dataTable span.cell-tooltip {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.dataTable .jqplot-graph {
|
||||
padding-left: 6px;
|
||||
> div {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
td.cellSubDataTable .loadingPiwik {
|
||||
padding:0;
|
||||
}
|
||||
|
||||
.dataTable .searchReset {
|
||||
position:relative;
|
||||
|
||||
img {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
left: -15px;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
.limitSelection {
|
||||
float: right;
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
min-height: 20px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.limitSelection.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.limitSelection > div {
|
||||
border: 1px solid #DFDFDF;
|
||||
border-radius: 4px;
|
||||
background: url(plugins/Zeitgeist/images/sort_subtable_desc_light.png) no-repeat right 2px;
|
||||
padding: 0 14px 0 4px;
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.limitSelection.disabled > div {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
filter: Alpha(opacity=50);
|
||||
}
|
||||
|
||||
.limitSelection.visible > div {
|
||||
border-radius: 0 0 4px 4px;
|
||||
background-image: url(plugins/Zeitgeist/images/sort_subtable_asc_light.png)
|
||||
}
|
||||
|
||||
.limitSelection > ul {
|
||||
margin-top: 1px;
|
||||
overflow: visible;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.limitSelection > ul > li {
|
||||
cursor: pointer;
|
||||
width: 28px;
|
||||
padding: 0 10px 0 4px;
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
height: 20px;
|
||||
margin-top: -40px;
|
||||
background-color: #fff;
|
||||
border-left: 1px solid #DFDFDF;
|
||||
border-right: 1px solid #DFDFDF;
|
||||
vertical-align: middle;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.limitSelection > ul > li.last {
|
||||
border-top: 1px solid #DFDFDF;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.limitSelection > ul > li:hover {
|
||||
background-color: #EBEAE6;
|
||||
}
|
||||
|
||||
.limitSelection span {
|
||||
padding-top: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
/* Documentation */
|
||||
table.dataTable th .columnDocumentation {
|
||||
display: none;
|
||||
width: 165px;
|
||||
text-align: left;
|
||||
background: #f7f7f7;
|
||||
color: #444;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
border: 1px solid #e4e5e4;
|
||||
padding: 5px 10px 6px 10px;
|
||||
border-radius: 4px;
|
||||
z-index: 125;
|
||||
position: absolute;
|
||||
box-shadow: 0 0 4px #e4e5e4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
table.dataTable th .columnDocumentationTitle {
|
||||
background: url(plugins/Zeitgeist/images/help.png) no-repeat;
|
||||
line-height: 14px;
|
||||
padding: 2px 0 3px 21px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.reportDocumentation {
|
||||
display: none;
|
||||
background: #f7f7f7;
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
border: 1px solid #e4e5e4;
|
||||
margin: 0 0 10px 0;
|
||||
padding: 0;
|
||||
border-radius: 4px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.reportDocumentation p {
|
||||
padding: 5px 10px 6px 10px;
|
||||
margin: 0;
|
||||
color: #444;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.reportDocumentationIcon {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 10px 0;
|
||||
background: url(plugins/Zeitgeist/images/help.png) no-repeat;
|
||||
}
|
||||
|
||||
h2 .reportDocumentationIcon {
|
||||
position: absolute;
|
||||
margin: 4px 0 0 0;
|
||||
display: none;
|
||||
}
|
||||
|
||||
h2 .reportDocumentationIcon.hidden {
|
||||
background: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
|
||||
|
||||
table.dataTable .dataTableRowActions {
|
||||
position: absolute;
|
||||
display: none;
|
||||
overflow: hidden;
|
||||
margin-top: -5px;
|
||||
z-index: 1000; /* Work around FF bug to make sure it displays over ellipsis */
|
||||
}
|
||||
|
||||
*+html table.dataTable .dataTableRowActions {
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
table.dataTable .dataTableRowActions a {
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 6px 4px 6px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
table.dataTable .dataTableRowActions a.leftmost {
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
table.dataTable .dataTableRowActions a.rightmost {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
table.dataTable .dataTableRowActions a img {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
width: 20px;
|
||||
height: 17px;
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/* SUBDATATABLE */
|
||||
/* a datatable inside another datatable */
|
||||
table.subDataTable td {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
table.subDataTable thead th {
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
text-align: left;
|
||||
padding: .3em 1em;
|
||||
border: 0;
|
||||
border-top: 1px solid #e7e7e7;
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
}
|
||||
|
||||
table.subDataTable td.labeleven, table.subDataTable td.labelodd {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
table.subDataTable td {
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
table.subDataTable td, table.subDataTable td a {
|
||||
color: #615B53;
|
||||
}
|
||||
|
||||
table.subDataTable td.labeleven, table.subDataTable td.columneven {
|
||||
color: #2D2A27;
|
||||
}
|
||||
|
||||
table.subDataTable td.label {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
table.subDataTable td.label {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* are the following two supposed to be together? */
|
||||
.subDataTable.dataTableFeatures {
|
||||
padding-top: 0;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
.tableConfiguration {
|
||||
float: right;
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
min-height: 20px;
|
||||
min-width: 25px;
|
||||
}
|
||||
|
||||
a.tableConfigurationIcon {
|
||||
display: block;
|
||||
width: 30px;
|
||||
height: 22px;
|
||||
background: url(plugins/Zeitgeist/images/configure.png) no-repeat center 2px;
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
a.tableConfigurationIcon.highlighted {
|
||||
display: block;
|
||||
width: 30px;
|
||||
height: 22px;
|
||||
background-image: url(plugins/Zeitgeist/images/configure-highlight.png);
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.tableConfiguration ul {
|
||||
overflow: visible;
|
||||
display: none;
|
||||
position: relative;
|
||||
z-index: 8;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tableConfiguration ul.open {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tableConfiguration ul li {
|
||||
padding: 0;
|
||||
font-size: 1.1em;
|
||||
height: 40px;
|
||||
margin-top: -80px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #DFDFDF;
|
||||
border-width: 0 1px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.tableConfiguration ul li.firstDummy {
|
||||
border-bottom-width: 1px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
height: 25px;
|
||||
cursor: default;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.tableConfiguration ul li.first {
|
||||
margin-top: -65px;
|
||||
}
|
||||
|
||||
.tableConfiguration ul li.last {
|
||||
border-top-width: 1px;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.tableConfiguration div.configItem {
|
||||
cursor: pointer;
|
||||
padding: 5px 10px;
|
||||
line-height: 15px;
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.tableConfiguration div.configItem:hover {
|
||||
background-color: #EBEAE6;
|
||||
}
|
||||
|
||||
.tableConfiguration div.configItem span.action {
|
||||
color: @dataTable-link-color;
|
||||
}
|
||||
150
www/analytics/plugins/CoreHome/stylesheets/jqplotColors.less
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
// evolution graph colors
|
||||
.evolution-graph-colors[data-name=grid-background] {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=grid-border] {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series1] {
|
||||
color: #5170AE;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series2] {
|
||||
color: #F29007;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series3] {
|
||||
color: #CC3399;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series4] {
|
||||
color: #9933CC;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series5] {
|
||||
color: #80a033;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series6] {
|
||||
color: #246AD2;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series7] {
|
||||
color: #FD16EA;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=series8] {
|
||||
color: #49C100;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=ticks] {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.evolution-graph-colors[data-name=single-metric-label] {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
// bar graph colors
|
||||
.bar-graph-colors[data-name=grid-background] {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=grid-border] {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series1] {
|
||||
color: #5170AE;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series2] {
|
||||
color: #F3A010;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series3] {
|
||||
color: #CC3399;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series4] {
|
||||
color: #9933CC;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series5] {
|
||||
color: #80a033;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series6] {
|
||||
color: #246AD2;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series7] {
|
||||
color: #FD16EA;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=series8] {
|
||||
color: #49C100;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=ticks] {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.bar-graph-colors[data-name=single-metric-label] {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
// pie graph colors
|
||||
.pie-graph-colors[data-name=grid-background] {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=grid-border] {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series1] {
|
||||
color: #59727F;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series2] {
|
||||
color: #7DAAC0;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series3] {
|
||||
color: #7F7259;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series4] {
|
||||
color: #C09E7D;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series5] {
|
||||
color: #9BB39B;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series6] {
|
||||
color: #B1D8B3;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series7] {
|
||||
color: #B39BA7;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series8] {
|
||||
color: #D8B1C5;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=series9] {
|
||||
color: #A5A5A5;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=ticks] {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.pie-graph-colors[data-name=single-metric-label] {
|
||||
color: #666;
|
||||
}
|
||||
70
www/analytics/plugins/CoreHome/stylesheets/jquery.ui.autocomplete.css
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* Autocomplete
|
||||
----------------------------------*/
|
||||
.ui-autocomplete {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ui-autocomplete-loading {
|
||||
background: white;
|
||||
}
|
||||
|
||||
/* workarounds */
|
||||
* html .ui-autocomplete {
|
||||
/* without this, the menu expands to 100% in IE6 */
|
||||
width: 1px;
|
||||
}
|
||||
|
||||
/* Menu
|
||||
----------------------------------*/
|
||||
.ui-menu {
|
||||
list-style: none;
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
font-family: Arial, Verdana, Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu {
|
||||
margin-top: -3px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item {
|
||||
line-height: 18px;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item a {
|
||||
line-height: 18px;
|
||||
color: #255792;
|
||||
font-size: 12px;
|
||||
padding: 0 5px 0 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item a.ui-state-focus,
|
||||
.ui-menu .ui-menu-item a.ui-state-active {
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ui-widget-content {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.ui-corner-all {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ui-menu .ui-menu-item a.ui-state-focus {
|
||||
background: #ebeae6;
|
||||
border: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
131
www/analytics/plugins/CoreHome/stylesheets/menu.less
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
.Menu--dashboard {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList {
|
||||
line-height: 1;
|
||||
display: table; // The nav has the height og his children
|
||||
margin-bottom: -1px; // Allow tabs to merge with the submenu
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList ul {
|
||||
background: #fff; /*IE6 needs this*/
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* LEVEL1 NORMAL */
|
||||
.Menu--dashboard > .Menu-tabList > li {
|
||||
background: #f1f1f1;
|
||||
float: left;
|
||||
list-style: none;
|
||||
z-index: 49;
|
||||
margin-right: -1px;
|
||||
border: 1px solid #ddd;
|
||||
border-bottom: 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList a {
|
||||
color: #444;
|
||||
font-size: 18px;
|
||||
display: block;
|
||||
float: left;
|
||||
padding: 8px 27px 0;
|
||||
height: 25px;
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* LEVEL1 HOVER */
|
||||
.Menu--dashboard > .Menu-tabList > li:hover,
|
||||
.Menu--dashboard > .Menu-tabList > li.sfHover {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li:hover > a,
|
||||
.Menu--dashboard > .Menu-tabList > li.sfHover > a,
|
||||
.Menu--dashboard > .Menu-tabList > li.sfActive > a,
|
||||
.Menu--dashboard > .Menu-tabList a:hover {
|
||||
color: #e87500;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li:hover > a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li.sfHover > a {
|
||||
border-bottom: 1px solid #fff;
|
||||
}
|
||||
|
||||
/* LEVEL2 NORMAL */
|
||||
.Menu--dashboard > .Menu-tabList > li > ul {
|
||||
padding: 9px 0 5px 0;
|
||||
left: 0;
|
||||
top: -999em;
|
||||
position: absolute;
|
||||
min-height: 25px;
|
||||
width: 100%;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li li {
|
||||
float: left;
|
||||
background: none;
|
||||
border: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li li > a {
|
||||
padding: 5px 15px;
|
||||
font-size: 14px;
|
||||
border: 0;
|
||||
float: none;
|
||||
display: inline-block;
|
||||
height: auto;
|
||||
background: none;
|
||||
color: #444;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* LEVEL2 HOVER */
|
||||
.Menu--dashboard > .Menu-tabList > li.sfHover > ul,
|
||||
.Menu--dashboard > .Menu-tabList > li:hover > ul {
|
||||
z-index: 1;
|
||||
top: 100%;
|
||||
opacity: 1;
|
||||
-webkit-transition: opacity 300ms ease-out 10ms; /* property duration timing-function delay */
|
||||
-moz-transition: opacity 300ms ease-out 10ms;
|
||||
-o-transition: opacity 300ms ease-out 10ms;
|
||||
transition: opacity 300ms ease-out 10ms;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li li:hover > a,
|
||||
.Menu--dashboard > .Menu-tabList > li li.sfHover > a {
|
||||
color: #e87500;
|
||||
}
|
||||
|
||||
.Menu--dashboard > .Menu-tabList > li li.sfHover > a {
|
||||
font-weight: bold;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
@media all and (max-width: 949px) {
|
||||
.nav {
|
||||
clear: right;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 749px) {
|
||||
.Menu--dashboard > .Menu-tabList a {
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 549px) {
|
||||
.Menu--dashboard > ul.Menu-tabList > li.sfHover > a,
|
||||
.Menu--dashboard > ul.Menu-tabList > li.sfActive.sfHover > a {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
93
www/analytics/plugins/CoreHome/stylesheets/notification.less
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
#content.admin #notificationContainer {
|
||||
width: 750px;
|
||||
display: table-header-group; /* no overlap with About Piwik box */
|
||||
|
||||
.notification {
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.system.notification {
|
||||
color: #9b7a44;
|
||||
float: none;
|
||||
|
||||
padding: 15px 35px 15px 15px;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,.5);
|
||||
background-color: #ffffe0;
|
||||
border: 1px solid #e6db55;
|
||||
border-radius: 3px;
|
||||
font-size: 14px;
|
||||
margin: 0px;
|
||||
margin-bottom: 16px;
|
||||
|
||||
a {
|
||||
color: #9b7a44;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.close {
|
||||
position: relative;
|
||||
top: -5px;
|
||||
right: -28px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
button.close {
|
||||
padding: 0;
|
||||
cursor: pointer;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
.close {
|
||||
float: right;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
line-height: 20px;
|
||||
color: #000000;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
opacity: 0.2;
|
||||
filter: alpha(opacity=20);
|
||||
}
|
||||
|
||||
&.notification-success {
|
||||
background-color: #dff0d8;
|
||||
border-color: #c3d6b7;
|
||||
color: #468847;
|
||||
|
||||
a {
|
||||
color: #468847
|
||||
}
|
||||
}
|
||||
&.notification-danger,
|
||||
&.notification-error {
|
||||
background-color: #f2dede;
|
||||
border-color: #d5bfc4;
|
||||
color: #b94a48;
|
||||
|
||||
a {
|
||||
color: #b94a48
|
||||
}
|
||||
}
|
||||
&.notification-info {
|
||||
background-color: #d9edf7;
|
||||
border-color: #a7d3e3;
|
||||
color: #3a87ad;
|
||||
|
||||
a {
|
||||
color: #3a87ad
|
||||
}
|
||||
}
|
||||
|
||||
&.notification-block {
|
||||
padding-top: 14px;
|
||||
padding-bottom: 14px;
|
||||
}
|
||||
&.notification-block > p,
|
||||
&.notification-block > ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
&.notification-block p + p {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
72
www/analytics/plugins/CoreHome/stylesheets/promo.less
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
#piwik-promo-thumbnail {
|
||||
background: #fff url(plugins/CoreHome/images/promo_splash.png) no-repeat 0 0;
|
||||
background-position: center;
|
||||
width: 321px;
|
||||
margin: 0 auto 0 auto;
|
||||
}
|
||||
|
||||
#piwik-promo-embed {
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
#piwik-promo-embed>iframe {
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#piwik-promo-thumbnail {
|
||||
height: 178px;
|
||||
}
|
||||
|
||||
#piwik-promo-thumbnail:hover {
|
||||
opacity: .75;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#piwik-promo-thumbnail>img {
|
||||
display: block;
|
||||
position: relative;
|
||||
top: 53px;
|
||||
left: 125px;
|
||||
}
|
||||
|
||||
#piwik-promo-video {
|
||||
margin: 2em 0 2em 0;
|
||||
}
|
||||
|
||||
#piwik-widget-footer {
|
||||
margin: 0 1em 1em 1em;
|
||||
}
|
||||
|
||||
#piwik-promo-share {
|
||||
margin: 0 2em 1em 0;
|
||||
background-color: #CCC;
|
||||
border: 1px solid #CCC;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
padding: 0 .5em 0 .5em;
|
||||
float: right;
|
||||
}
|
||||
|
||||
#piwik-promo-share > a {
|
||||
margin-left: .5em;
|
||||
margin-top: 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#piwik-promo-share>span {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
#piwik-promo-videos-link {
|
||||
font-size: .8em;
|
||||
font-style: italic;
|
||||
margin: 1em 0 0 1.25em;
|
||||
color: #666;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#piwik-promo-videos-link:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// sparkline styles
|
||||
div.sparkline {
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
||||
div.sparkline:hover {
|
||||
cursor: pointer;
|
||||
border-bottom: 1px dashed #c3c3c3;
|
||||
}
|
||||
|
||||
// sparkline colors
|
||||
.sparkline-colors[data-name=backgroundColor] {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.sparkline-colors[data-name=lineColor] {
|
||||
color: rgb(22, 44, 74);
|
||||
}
|
||||
|
||||
.sparkline-colors[data-name=minPointColor] {
|
||||
color: #ff7f7f;
|
||||
}
|
||||
|
||||
.sparkline-colors[data-name=lastPointColor] {
|
||||
color: #55AAFF;
|
||||
}
|
||||
|
||||
.sparkline-colors[data-name=maxPointColor] {
|
||||
color: #75BF7C;
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<h2 id="{{ reportId }}" style="color: rgb({{ reportTitleTextColor }}); font-size: {{ reportTitleTextSize }}pt;">
|
||||
{{ reportName }}
|
||||
</h2>
|
||||
|
||||
{% if reportRows is empty %}
|
||||
{{ 'CoreHome_ThereIsNoDataForThisReport'|translate }}
|
||||
{% else %}
|
||||
{% if displayGraph %}
|
||||
<img alt=""
|
||||
{% if renderImageInline %}
|
||||
src="data:image/png;base64,{{ generatedImageGraph }}"
|
||||
{% else %}
|
||||
src="cid:{{ reportId }}"
|
||||
{% endif %}
|
||||
height="{{ graphHeight }}"
|
||||
width="{{ graphWidth }}"/>
|
||||
{% endif %}
|
||||
|
||||
{% if displayGraph and displayTable %}
|
||||
<br/>
|
||||
<br/>
|
||||
{% endif %}
|
||||
|
||||
{% if displayTable %}
|
||||
<table style="border-collapse:collapse; margin-left: 5px;">
|
||||
<thead style="background-color: rgb({{ tableHeaderBgColor }}); color: rgb({{ tableHeaderTextColor }}); font-size: {{ reportTableHeaderTextSize }}pt;">
|
||||
{% for columnName in reportColumns %}
|
||||
<th style="padding: 6px 0;">
|
||||
{{ columnName }}
|
||||
</th>
|
||||
{% endfor %}
|
||||
</thead>
|
||||
<tbody>
|
||||
{% set cycleValues=['','background-color: rgb('~tableBgColor~')'] %}
|
||||
{% set cycleIndex=0 %}
|
||||
{% for rowId,row in reportRows %}
|
||||
{% set rowMetrics=row.columns %}
|
||||
|
||||
{% if reportRowsMetadata[rowId] is defined %}
|
||||
{% set rowMetadata=reportRowsMetadata[rowId].columns %}
|
||||
{% else %}
|
||||
{% set rowMetadata=null %}
|
||||
{% endif %}
|
||||
<tr style="{{ cycle(cycleValues, cycleIndex) }}">
|
||||
{% set cycleIndex=cycleIndex+1 %}
|
||||
{% for columnId, columnName in reportColumns %}
|
||||
<td style="font-size: {{ reportTableRowTextSize }}pt; border-bottom: 1px solid rgb({{ tableCellBorderColor }}); padding: 5px 0 5px 5px;">
|
||||
{% if columnId == 'label' %}
|
||||
{% if rowMetrics[columnId] is defined %}
|
||||
{% if rowMetadata.logo is defined %}
|
||||
<img src='{{ currentPath }}{{ rowMetadata.logo }}'>
|
||||
|
||||
{% endif %}
|
||||
{% if rowMetadata.url is defined %}
|
||||
<a style="color: rgb({{ reportTextColor }});" href='{% if rowMetadata.url|slice(0,4) not in ['http','ftp:'] %}http://{% endif %}{{ rowMetadata.url }}'>
|
||||
{% endif %}
|
||||
{{ rowMetrics[columnId] | raw }}{# labels are escaped by SafeDecodeLabel filter in core/API/Response.php #}
|
||||
{% if rowMetadata.url is defined %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if rowMetrics[columnId] is empty %}
|
||||
0
|
||||
{% else %}
|
||||
{{ rowMetrics[columnId] }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<br/>
|
||||
<a style="text-decoration:none; color: rgb({{ reportTitleTextColor }}); font-size: {{ reportBackToTopTextSize }}pt;" href="#reportTop">
|
||||
{{ 'ScheduledReports_TopOfReport'|translate }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body style="color: rgb({{ reportTextColor }});">
|
||||
|
||||
<a id="reportTop" target="_blank" href="{{ currentPath }}"><img title="{{ 'General_GoTo'|translate("Piwik") }}" border="0" alt="Piwik" src='{{ logoHeader }}'/></a>
|
||||
|
||||
<h1 style="color: rgb({{ reportTitleTextColor }}); font-size: {{ reportTitleTextSize }}pt;">
|
||||
{{ reportTitle }}
|
||||
</h1>
|
||||
|
||||
<p>
|
||||
{{ description }} - {{ 'General_DateRange'|translate }} {{ prettyDate }}
|
||||
</p>
|
||||
|
||||
{% if displaySegment %}
|
||||
<p style="color: rgb({{ reportTitleTextColor }});">
|
||||
{{ 'ScheduledReports_CustomVisitorSegment'|translate("Piwik") }} {{ segmentName }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if reportMetadata|length > 1 %}
|
||||
<h2 style="color: rgb({{ reportTitleTextColor }}); font-size: {{ reportTitleTextSize }}pt;">
|
||||
{{ 'ScheduledReports_TableOfContent'|translate }}
|
||||
</h2>
|
||||
<ul>
|
||||
{% for metadata in reportMetadata %}
|
||||
<li>
|
||||
<a href="#{{ metadata.uniqueId }}" style="text-decoration:none; color: rgb({{ reportTextColor }});">
|
||||
{{ metadata.name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<div class="reportsByDimensionView">
|
||||
|
||||
<div class="entityList">
|
||||
{% for category, dimensions in dimensionCategories %}
|
||||
{% set firstCategory = (loop.index0 == 0) %}
|
||||
<div class='dimensionCategory'>
|
||||
{{ category|translate }}
|
||||
<ul class='listCircle'>
|
||||
{% for idx, dimension in dimensions %}
|
||||
<li class="reportDimension {% if idx == 0 and firstCategory %}activeDimension{% endif %}"
|
||||
data-url="{{ dimension.url }}">
|
||||
<span class='dimension'>{{ dimension.title|translate }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div style="float:left;max-width:900px;">
|
||||
<div class="loadingPiwik" style="display:none;">
|
||||
<img src="plugins/Zeitgeist/images/loading-blue.gif" alt=""/>{{ 'General_LoadingData'|translate }}
|
||||
</div>
|
||||
|
||||
<div class="dimensionReport">{{ firstReport|raw }}</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
</div>
|
||||
40
www/analytics/plugins/CoreHome/templates/_dataTable.twig
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
{% if properties.show_visualization_only %}
|
||||
{% include visualizationTemplate %}
|
||||
{%- else -%}
|
||||
|
||||
{% set summaryRowId = constant('Piwik\\DataTable::ID_SUMMARY_ROW') %}{# ID_SUMMARY_ROW #}
|
||||
{% set isSubtable = javascriptVariablesToSet.idSubtable is defined and javascriptVariablesToSet.idSubtable != 0 %}
|
||||
<div class="dataTable {{ visualizationCssClass }} {{ properties.datatable_css_class|default('') }} {% if isSubtable %}subDataTable{% endif %}"
|
||||
data-table-type="{{ properties.datatable_js_type }}"
|
||||
data-report="{{ properties.report_id }}"
|
||||
data-props="{% if clientSideProperties is empty %}{}{% else %}{{ clientSideProperties|json_encode }}{% endif %}"
|
||||
data-params="{% if clientSideParameters is empty %}{}{% else %}{{ clientSideParameters|json_encode }}{% endif %}">
|
||||
<div class="reportDocumentation">
|
||||
{% if properties.documentation|default is not empty %}<p>{{ properties.documentation|raw }}</p>{% endif %}
|
||||
{% if reportLastUpdatedMessage is defined %}<span class='helpDate'>{{ reportLastUpdatedMessage }}</span>{% endif %}
|
||||
</div>
|
||||
<div class="dataTableWrapper">
|
||||
{% if error is defined %}
|
||||
{{ error.message }}
|
||||
{% else %}
|
||||
{% if dataTable is empty or dataTableHasNoData|default(false) %}
|
||||
<div class="pk-emptyDataTable">
|
||||
{% if showReportDataWasPurgedMessage is defined and showReportDataWasPurgedMessage %}
|
||||
{{ 'CoreHome_DataForThisReportHasBeenPurged'|translate(deleteReportsOlderThan) }}
|
||||
{% else %}
|
||||
{{ 'CoreHome_ThereIsNoDataForThisReport'|translate }}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
{% include visualizationTemplate %}
|
||||
{% endif %}
|
||||
|
||||
{% if properties.show_footer %}
|
||||
{% include "@CoreHome/_dataTableFooter.twig" %}
|
||||
{% endif %}
|
||||
{% include "@CoreHome/_dataTableJS.twig" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{%- endif %}
|
||||
49
www/analytics/plugins/CoreHome/templates/_dataTableCell.twig
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{% spaceless %}
|
||||
{% set tooltipIndex = column ~ '_tooltip' %}
|
||||
{% if row.getMetadata(tooltipIndex) %}<span class="cell-tooltip" data-tooltip="{{ row.getMetadata(tooltipIndex) }}">{% endif %}
|
||||
{% if not row.getIdSubDataTable() and column=='label' and row.getMetadata('url') %}
|
||||
<a target="_blank" href='{% if row.getMetadata('url')|slice(0,4) not in ['http','ftp:'] %}http://{% endif %}{{ row.getMetadata('url')|raw }}'>
|
||||
{% if not row.getMetadata('logo') %}
|
||||
<img class="link" width="10" height="9" src="plugins/Zeitgeist/images/link.gif"/>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if column=='label' %}
|
||||
{% import 'macros.twig' as piwik %}
|
||||
|
||||
<span class='label{% if row.getMetadata('is_aggregate') %} highlighted{% endif %}'
|
||||
{% if properties is defined and properties.tooltip_metadata_name is not empty %}title="{{ row.getMetadata(properties.tooltip_metadata_name) }}"{% endif %}>
|
||||
{{ piwik.logoHtml(row.getMetadata(), row.getColumn('label')) }}
|
||||
{% if row.getMetadata('html_label_prefix') %}<span class='label-prefix'>{{ row.getMetadata('html_label_prefix') | raw }} </span>{% endif -%}
|
||||
{%- if row.getMetadata('html_label_suffix') %}<span class='label-suffix'>{{ row.getMetadata('html_label_suffix') | raw }}</span>{% endif -%}
|
||||
{% endif %}<span class="value">{% if row.getColumn(column) %}{{- row.getColumn(column)|raw -}}{% else %}-{% endif %}</span>
|
||||
{% if column=='label' %}</span>{% endif %}
|
||||
{% if not row.getIdSubDataTable() and column=='label' and row.getMetadata('url') %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if row.getMetadata(tooltipIndex) %}</span>{% endif %}
|
||||
|
||||
{% set totals = dataTable.getMetadata('totals') %}
|
||||
{% if column in totals|keys -%}
|
||||
{% set labelColumn = columns_to_display|first %}
|
||||
{% set reportTotal = totals[column] %}
|
||||
{% if siteSummary is defined and siteSummary is not empty and siteSummary.getFirstRow %}
|
||||
{% set siteTotal = siteSummary.getFirstRow.getColumn(column) %}
|
||||
{% else %}
|
||||
{% set siteTotal = 0 %}
|
||||
{% endif %}
|
||||
{% set rowPercentage = row.getColumn(column)|percentage(reportTotal, 1) %}
|
||||
{% set metricTitle = translations[column]|default(column) %}
|
||||
{% set reportLabel = row.getColumn(labelColumn)|truncate(40)|raw %}
|
||||
|
||||
{% set reportRatioTooltip = 'General_ReportRatioTooltip'|translate(reportLabel, rowPercentage|e('html_attr'), reportTotal|e('html_attr'), metricTitle|e('html_attr'), translations[labelColumn]|default(labelColumn)|e('html_attr')) %}
|
||||
|
||||
{% if siteTotal and siteTotal > reportTotal %}
|
||||
{% set totalPercentage = row.getColumn(column)|percentage(siteTotal, 1) %}
|
||||
{% set totalRatioTooltip = 'General_TotalRatioTooltip'|translate(totalPercentage, siteTotal, metricTitle) %}
|
||||
{% else %}
|
||||
{% set totalRatioTooltip = '' %}
|
||||
{% endif %}
|
||||
|
||||
<span class="ratio" title="{{ reportRatioTooltip|raw }} {{ totalRatioTooltip|e('html_attr') }}"> {{ rowPercentage }}</span>
|
||||
{%- endif %}
|
||||
{% endspaceless %}
|
||||
140
www/analytics/plugins/CoreHome/templates/_dataTableFooter.twig
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
<div class="dataTableFeatures">
|
||||
|
||||
<div class="dataTableFooterNavigation">
|
||||
{% if properties.show_offset_information %}
|
||||
<span>
|
||||
<span class="dataTablePages"></span>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if properties.show_pagination_control %}
|
||||
<span>
|
||||
<span class="dataTablePrevious">‹ {% if clientSideParameters.dataTablePreviousIsFirst is defined %}{{ 'General_First'|translate }}{% else %}{{ 'General_Previous'|translate }}{% endif %} </span>
|
||||
<span class="dataTableNext">{{ 'General_Next'|translate }} ›</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if properties.show_search %}
|
||||
<span class="dataTableSearchPattern">
|
||||
<input type="text" class="searchInput" length="15" />
|
||||
<input type="submit" value="{{ 'General_Search'|translate }}" />
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<span class="loadingPiwik" style="display:none;"><img src="plugins/Zeitgeist/images/loading-blue.gif"/> {{ 'General_LoadingData'|translate }}</span>
|
||||
|
||||
{% if properties.show_footer_icons %}
|
||||
<div class="dataTableFooterIcons">
|
||||
<div class="dataTableFooterWrap">
|
||||
{% for footerIconGroup in footerIcons %}
|
||||
<div class="tableIconsGroup">
|
||||
<span class="{{ footerIconGroup.class }}">
|
||||
{% for footerIcon in footerIconGroup.buttons %}
|
||||
<span>
|
||||
{% if properties.show_active_view_icon and clientSideParameters.viewDataTable == footerIcon.id %}
|
||||
<img src="plugins/Zeitgeist/images/data_table_footer_active_item.png" class="dataTableFooterActiveItem"/>
|
||||
{% endif %}
|
||||
<a class="tableIcon {% if clientSideParameters.viewDataTable == footerIcon.id %}activeIcon{% endif %}" data-footer-icon-id="{{ footerIcon.id }}">
|
||||
<img width="16" height="16" title="{{ footerIcon.title }}" src="{{ footerIcon.icon }}"/>
|
||||
{% if footerIcon.text is defined %}<span>{{ footerIcon.text }}</span>{% endif %}
|
||||
</a>
|
||||
</span>
|
||||
{% endfor %}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<div class="tableIconsGroup">
|
||||
{% if footerIcons is empty %}
|
||||
<img src="plugins/Zeitgeist/images/data_table_footer_active_item.png" class="dataTableFooterActiveItem"/>
|
||||
{% endif %}
|
||||
<span class="exportToFormatIcons">
|
||||
<a class="tableIcon" var="export">
|
||||
<img width="16" height="16" src="plugins/Zeitgeist/images/export.png" title="{{ 'General_ExportThisReport'|translate }}"/>
|
||||
</a>
|
||||
</span>
|
||||
<span class="exportToFormatItems" style="display:none;">
|
||||
{{ 'General_Export'|translate }}:
|
||||
<a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="CSV" filter_limit="{{ properties.export_limit }}">CSV</a> |
|
||||
<a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="TSV" filter_limit="{{ properties.export_limit }}">TSV (Excel)</a> |
|
||||
<a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="XML" filter_limit="{{ properties.export_limit }}">XML</a> |
|
||||
<a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="JSON" filter_limit="{{ properties.export_limit }}">Json</a> |
|
||||
<a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="PHP" filter_limit="{{ properties.export_limit }}">Php</a>
|
||||
{% if properties.show_export_as_rss_feed %}
|
||||
|
|
||||
<a target="_blank" methodToCall="{{ properties.apiMethodToRequestDataTable }}" format="RSS" filter_limit="{{ properties.export_limit }}" date="last10">
|
||||
<img border="0" src="plugins/Zeitgeist/images/feed.png"/>
|
||||
</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
{% if properties.show_export_as_image_icon %}
|
||||
<span id="dataTableFooterExportAsImageIcon">
|
||||
<a class="tableIcon" href="#" onclick="$(this).closest('.dataTable').find('div.jqplot-target').trigger('piwikExportAsImage'); return false;">
|
||||
<img title="{{ 'General_ExportAsImage'|translate }}" src="plugins/Zeitgeist/images/image.png"/>
|
||||
</a>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="limitSelection {% if not properties.show_pagination_control and not properties.show_limit_control %} hidden{% endif %}"
|
||||
title="{{ 'General_RowsToDisplay'|translate }}"></div>
|
||||
<div class="tableConfiguration">
|
||||
<a class="tableConfigurationIcon" href="#"></a>
|
||||
<ul>
|
||||
{% if properties.show_flatten_table %}
|
||||
{% if clientSideParameters.flat is defined and clientSideParameters.flat == 1 %}
|
||||
<li>
|
||||
<div class="configItem dataTableIncludeAggregateRows"></div>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li>
|
||||
<div class="configItem dataTableFlatten"></div>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if properties.show_exclude_low_population %}
|
||||
<li>
|
||||
<div class="configItem dataTableExcludeLowPopulation"></div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% if isPluginLoaded('Annotations') and not properties.hide_annotations_view %}
|
||||
<div class="annotationView" title="{{ 'Annotations_IconDesc'|translate }}">
|
||||
<a class="tableIcon">
|
||||
<img width="16" height="16" src="plugins/Zeitgeist/images/annotations.png"/>
|
||||
</a>
|
||||
<span>{{ 'Annotations_Annotations'|translate }}</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="foldDataTableFooterDrawer" title="{{ 'General_Close'|translate|e('html_attr') }}"
|
||||
><img width="7" height="4" src="plugins/Morpheus/images/sortasc_dark.png"></div>
|
||||
|
||||
</div>
|
||||
<div class="expandDataTableFooterDrawer" title="{{ 'General_ExpandDataTableFooter'|translate|e('html_attr') }}"
|
||||
><img width="7" height="4" src="plugins/Morpheus/images/sortdesc_dark.png" style=""></div>
|
||||
{% endif %}
|
||||
|
||||
<div class="datatableRelatedReports">
|
||||
{% if (properties.related_reports is not empty) and properties.show_related_reports %}
|
||||
{{ properties.related_reports_title|raw }}
|
||||
<ul style="list-style:none;{% if properties.related_reports|length == 1 %}display:inline-block;{% endif %}}">
|
||||
<li><span href="{{ properties.self_url }}" style="display:none;">{{ properties.title }}</span></li>
|
||||
|
||||
{% for reportUrl,reportTitle in properties.related_reports %}
|
||||
<li><span href="{{ reportUrl }}">{{ reportTitle }}</span></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if properties.show_footer_message is defined and properties.show_footer_message is not empty %}
|
||||
<div class='datatableFooterMessage'>{{ properties.show_footer_message | raw }}</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
<span class="loadingPiwikBelow" style="display:none;"><img src="plugins/Zeitgeist/images/loading-blue.gif"/> {{ 'General_LoadingData'|translate }}</span>
|
||||
|
||||
<div class="dataTableSpacer"></div>
|
||||
17
www/analytics/plugins/CoreHome/templates/_dataTableHead.twig
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<thead>
|
||||
<tr>
|
||||
{% for column in properties.columns_to_display %}
|
||||
<th class="{% if properties.enable_sort %}sortable{% endif %} {% if loop.first %}first{% elseif loop.last %}last{% endif %}" id="{{ column }}">
|
||||
{% if properties.metrics_documentation[column]|default is not empty %}
|
||||
<div class="columnDocumentation">
|
||||
<div class="columnDocumentationTitle">
|
||||
{{ properties.translations[column]|default(column)|raw }}
|
||||
</div>
|
||||
{{ properties.metrics_documentation[column]|raw }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div id="thDIV">{{ properties.translations[column]|default(column)|raw }}</div>
|
||||
</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<script type="text/javascript" defer="defer">
|
||||
$(document).ready(function () {
|
||||
require('piwik/UI/DataTable').initNewDataTables();
|
||||
});
|
||||
</script>
|
||||
63
www/analytics/plugins/CoreHome/templates/_donate.twig
Executable file
|
|
@ -0,0 +1,63 @@
|
|||
<div class="piwik-donate-call">
|
||||
<div class="piwik-donate-message">
|
||||
{% if msg is defined %}
|
||||
{{ msg }}
|
||||
{% else %}
|
||||
<p>{{ 'CoreHome_DonateCall1'|translate }}</p>
|
||||
<p><strong><em>{{ 'CoreHome_DonateCall2'|translate }}</em></strong></p>
|
||||
<p>{{ 'CoreHome_DonateCall3'|translate('<em><strong>','</strong></em>')|raw }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<span id="piwik-worth">{{ 'CoreHome_HowMuchIsPiwikWorth'|translate }}</span>
|
||||
|
||||
<div class="donate-form-instructions">({{ 'CoreHome_DonateFormInstructions'|translate }})</div>
|
||||
|
||||
<form action="index.php?module=CoreHome&action=redirectToPaypal&idSite=1" method="post" target="_blank">
|
||||
<input type="hidden" name="cmd" value="_s-xclick"/>
|
||||
<input type="hidden" name="hosted_button_id" value="DVKLY73RS7JTE"/>
|
||||
<input type="hidden" name="currency_code" value="USD"/>
|
||||
<input type="hidden" name="on0" value="Piwik Supporter"/>
|
||||
|
||||
<div class="piwik-donate-slider">
|
||||
<div class="slider-range">
|
||||
<div class="slider-position"></div>
|
||||
</div>
|
||||
<div style="display:inline-block;">
|
||||
<div class="slider-donate-amount">$30/{{ 'General_YearShort'|translate }}</div>
|
||||
|
||||
<img class="slider-smiley-face" width="40" height="40" src="plugins/Zeitgeist/images/smileyprog_1.png"/>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="os0" value="Option 1"/>
|
||||
</div>
|
||||
|
||||
<div class="donate-submit">
|
||||
<input type="image" src="plugins/Zeitgeist/images/paypal_subscribe.gif" border="0" name="submit"
|
||||
title="{{ 'CoreHome_SubscribeAndBecomePiwikSupporter'|translate }}"/>
|
||||
<a class="donate-spacer">{{ 'CoreHome_MakeOneTimeDonation'|translate }}</a>
|
||||
<a href="index.php?module=CoreHome&action=redirectToPaypal&idSite=1&cmd=_s-xclick&hosted_button_id=RPL23NJURMTFA&bb2_screener_=1357583494+83.233.186.82"
|
||||
target="_blank" class="donate-one-time">{{ 'CoreHome_MakeOneTimeDonation'|translate }}</a>
|
||||
</div>
|
||||
|
||||
<!-- to cache images -->
|
||||
<img style="display:none;" src="plugins/Zeitgeist/images/smileyprog_0.png"/>
|
||||
<img style="display:none;" src="plugins/Zeitgeist/images/smileyprog_1.png"/>
|
||||
<img style="display:none;" src="plugins/Zeitgeist/images/smileyprog_2.png"/>
|
||||
<img style="display:none;" src="plugins/Zeitgeist/images/smileyprog_3.png"/>
|
||||
<img style="display:none;" src="plugins/Zeitgeist/images/smileyprog_4.png"/>
|
||||
</form>
|
||||
{% if footerMessage is defined %}
|
||||
<div class="form-description">
|
||||
{{ footerMessage }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
// Note: this will cause problems if more than one donate form is on the page
|
||||
$('.piwik-donate-slider').each(function () {
|
||||
$(this).trigger('piwik:changePosition', {position: 1});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
47
www/analytics/plugins/CoreHome/templates/_headerMessage.twig
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{# testing, remove test_ from var names #}
|
||||
{% set test_latest_version_available="3.0" %}
|
||||
{% set test_piwikUrl='http://demo.piwik.org/' %}
|
||||
{% set isPiwikDemo %}{{ piwikUrl == 'http://demo.piwik.org/' or piwikUrl == 'https://demo.piwik.org/'}}{% endset %}
|
||||
|
||||
{% set updateCheck %}
|
||||
<div id="updateCheckLinkContainer">
|
||||
<span class='loadingPiwik' style="display:none;"><img src='plugins/Zeitgeist/images/loading-blue.gif'/></span>
|
||||
<img class="icon" src="plugins/Zeitgeist/images/reload.png"/>
|
||||
<a href="#" id="checkForUpdates"><em>{{ 'CoreHome_CheckForUpdates'|translate }}</em></a>
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
{% if isPiwikDemo or (latest_version_available and hasSomeViewAccess and not isUserIsAnonymous) or (isSuperUser and adminMenu is defined and adminMenu) %}
|
||||
<span id="header_message" class="{% if isPiwikDemo or not latest_version_available %}header_info{% else %}header_alert{% endif %}">
|
||||
<span class="header_short">
|
||||
{% if isPiwikDemo %}
|
||||
{{ 'General_YouAreViewingDemoShortMessage'|translate }}
|
||||
{% elseif latest_version_available %}
|
||||
{{ 'General_NewUpdatePiwikX'|translate(latest_version_available) }}
|
||||
{% elseif isSuperUser and adminMenu is defined and adminMenu %}
|
||||
{{ updateCheck|raw }}
|
||||
{% endif %}
|
||||
</span>
|
||||
|
||||
<span class="header_full">
|
||||
{% if isPiwikDemo %}
|
||||
{{ 'General_YouAreViewingDemoShortMessage'|translate }}
|
||||
<br />
|
||||
{{ 'General_DownloadFullVersion'|translate("<a href='http://piwik.org/'>","</a>","<a href='http://piwik.org'>piwik.org</a>")|raw }}
|
||||
<br/>
|
||||
{% endif %}
|
||||
{% if latest_version_available and isSuperUser %}
|
||||
{{ 'General_PiwikXIsAvailablePleaseUpdateNow'|translate(latest_version_available,"<br /><a href='index.php?module=CoreUpdater&action=newVersionAvailable'>","</a>","<a href='?module=Proxy&action=redirect&url=http://piwik.org/changelog/' target='_blank'>","</a>")|raw }}
|
||||
<br/>
|
||||
{{ 'General_YouAreCurrentlyUsing'|translate(piwik_version) }}
|
||||
{% elseif latest_version_available and not isPiwikDemo and hasSomeViewAccess and not isUserIsAnonymous %}
|
||||
{% set updateSubject = 'General_NewUpdatePiwikX'|translate(latest_version_available)|e('url') %}
|
||||
{{ 'General_PiwikXIsAvailablePleaseNotifyPiwikAdmin'|translate("<a href='?module=Proxy&action=redirect&url=http://piwik.org/' target='_blank'>Piwik</a> <a href='?module=Proxy&action=redirect&url=http://piwik.org/changelog/' target='_blank'>" ~ latest_version_available ~ "</a>", "<a href='mailto:" ~ superUserEmails ~ "?subject=" ~ updateSubject ~ "'>", "</a>")|raw }}
|
||||
{% elseif isSuperUser and adminMenu is defined and adminMenu %}
|
||||
{{ updateCheck|raw }}
|
||||
<br />
|
||||
{{ 'General_YouAreCurrentlyUsing'|translate(piwik_version) }}
|
||||
{% endif %}
|
||||
</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
20
www/analytics/plugins/CoreHome/templates/_indexContent.twig
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
{% import 'ajaxMacros.twig' as ajax %}
|
||||
<div class="pageWrap">
|
||||
{% include "@CoreHome/_notifications.twig" %}
|
||||
<div class="top_controls">
|
||||
{% include "@CoreHome/_periodSelect.twig" %}
|
||||
{{ postEvent("Template.nextToCalendar") }}
|
||||
{% render dashboardSettingsControl %}
|
||||
{% include "@CoreHome/_headerMessage.twig" %}
|
||||
{{ ajax.requestErrorDiv }}
|
||||
</div>
|
||||
|
||||
{{ ajax.loadingDiv() }}
|
||||
|
||||
<div id="content" class="home">
|
||||
{% if content %}{{ content }}{% endif %}
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
|
||||
<br/><br/>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<noscript>
|
||||
<div id="javascriptDisabled">{{ 'CoreHome_JavascriptDisabled'|translate('<a href="">','</a>')|raw }}</div>
|
||||
</noscript>
|
||||
10
www/analytics/plugins/CoreHome/templates/_logo.twig
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<span id="logo">
|
||||
<a href="index.php" title="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik # {{ 'General_OpenSourceWebAnalytics'|translate }}">
|
||||
{% if hasSVGLogo %}
|
||||
<img src='{{ logoSVG }}' alt="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik" class="ie-hide {% if not isCustomLogo %}default-piwik-logo{% endif %}" />
|
||||
<!--[if lt IE 9]>
|
||||
{% endif %}
|
||||
<img src='{{ logoHeader }}' alt="{% if isCustomLogo %}{{ 'General_PoweredBy'|translate }} {% endif %}Piwik" />
|
||||
{% if hasSVGLogo %}<![endif]-->{% endif %}
|
||||
</a>
|
||||
</span>
|
||||
23
www/analytics/plugins/CoreHome/templates/_menu.twig
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<div class="Menu--dashboard">
|
||||
<ul class="Menu-tabList">
|
||||
{% for level1,level2 in menu %}
|
||||
<li id="{{ level2._url|urlRewriteWithParameters }}">
|
||||
<a href="#{{ level2._url|urlRewriteWithParameters|slice(1) }}"
|
||||
onclick="return piwikMenu.onItemClick(this);">{{ level1|translate }}</a>
|
||||
<ul>
|
||||
{% for name,urlParameters in level2 %}
|
||||
{% if name|slice(0,1) != '_' %}
|
||||
<li>
|
||||
<a href='#{{ urlParameters._url|urlRewriteWithParameters|slice(1) }}'
|
||||
onclick='return piwikMenu.onItemClick(this);'>
|
||||
{{ name|translate }}
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<div class="nav_sep"></div>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<div id="notificationContainer">
|
||||
{% if notifications|length %}
|
||||
{% for notificationId, n in notifications %}
|
||||
|
||||
{{ n.message|notification({'id': notificationId, 'type': n.type, 'title': n.title, 'noclear': n.hasNoClear, 'context': n.context, 'raw': n.raw}, false) }}
|
||||
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
37
www/analytics/plugins/CoreHome/templates/_periodSelect.twig
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<div id="periodString" class="piwikTopControl periodSelector">
|
||||
<div id="date">{{ 'General_DateRange'|translate }} <strong>{{ prettyDate }}</strong></div>
|
||||
<div class="calendar-icon"></div>
|
||||
<div id="periodMore">
|
||||
<div class="period-date">
|
||||
<h6>{{ 'General_Date'|translate }}</h6>
|
||||
|
||||
<div id="datepicker"></div>
|
||||
</div>
|
||||
<div class="period-range" style="display:none;">
|
||||
<div id="calendarRangeFrom">
|
||||
<h6>{{ 'General_DateRangeFrom'|translate }}<input tabindex="1" type="text" id="inputCalendarFrom" name="inputCalendarFrom"/></h6>
|
||||
|
||||
<div id="calendarFrom"></div>
|
||||
</div>
|
||||
<div id="calendarRangeTo">
|
||||
<h6>{{ 'General_DateRangeTo'|translate }}<input tabindex="2" type="text" id="inputCalendarTo" name="inputCalendarTo"/></h6>
|
||||
|
||||
<div id="calendarTo"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="period-type">
|
||||
<h6>{{ 'General_Period'|translate }}</h6>
|
||||
<span id="otherPeriods">
|
||||
{% for label,thisPeriod in periodsNames %}
|
||||
<input type="radio" name="period" id="period_id_{{ label }}" value="{{ linkTo( { 'period': label} ) }}"{% if label==period %} checked="checked"{% endif %} />
|
||||
<label for="period_id_{{ label }}">{{ thisPeriod.singular }}</label>
|
||||
<br/>
|
||||
{% endfor %}
|
||||
</span>
|
||||
<input tabindex="3" type="submit" value="{{ 'General_ApplyDateRange'|translate }}" id="calendarRangeApply"/>
|
||||
{% import 'ajaxMacros.twig' as ajax %}
|
||||
{{ ajax.loadingDiv('ajaxLoadingCalendar') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="period-click-tooltip" style="display:none;">{{ 'General_ClickToChangePeriod'|translate }}</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<h2 piwik-enriched-headline>{{ title }}</h2>
|
||||
{{ report|raw }}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<div class="top_bar_sites_selector {% if currentModule == 'CoreHome' %}sites_selector_in_dashboard{% endif %}">
|
||||
<label>{{ 'General_Website'|translate }}</label>
|
||||
<div piwik-siteselector class="sites_autocomplete"></div>
|
||||
|
||||
</div>
|
||||
5
www/analytics/plugins/CoreHome/templates/_topBar.twig
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{{ postEvent("Template.beforeTopBar", userAlias, userLogin, topMenu) }}
|
||||
<div id="topBars">
|
||||
{% include "@CoreHome/_topBarHelloMenu.twig" %}
|
||||
{% include "@CoreHome/_topBarTopMenu.twig" %}
|
||||
</div>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<div id="topRightBar">
|
||||
{% set helloAlias %}
|
||||
{% if userAlias is not empty %}
|
||||
<strong>{{ userAlias }}</strong>
|
||||
{% else %}
|
||||
<strong>{{ userLogin }}</strong>
|
||||
{% endif %}
|
||||
{% endset %}
|
||||
<span class="topBarElem">{{ 'General_HelloUser'|translate(helloAlias|trim)|raw }}</span>
|
||||
{% if userLogin != 'anonymous' %}
|
||||
|
|
||||
{% if isAdminLayout is defined %}
|
||||
<span class="topBarElem topBarElemActive">{{ 'General_Settings'|translate }}</span>
|
||||
{% else %}
|
||||
<span class="topBarElem"><a href='index.php?module=CoreAdminHome'>{{ 'General_Settings'|translate }}</a></span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
| <span class="topBarElem">
|
||||
{% if userLogin == 'anonymous' %}
|
||||
<a href='index.php?module={{ loginModule }}'>{{ 'Login_LogIn'|translate }}</a>
|
||||
{% else %}
|
||||
<a href='index.php?module={{ loginModule }}&action=logout'>{{ 'General_Logout'|translate }}</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
13
www/analytics/plugins/CoreHome/templates/_topBarTopMenu.twig
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<div id="topLeftBar">
|
||||
{% for label,menu in topMenu %}
|
||||
{% if menu._html is defined %}
|
||||
{{ menu._html|raw }}
|
||||
{% elseif (menu._url.module == currentModule and (menu._url.action is empty or menu._url.action == currentAction)) %}
|
||||
<span class="topBarElem topBarElemActive"><strong>{{ label|translate }}</strong></span>{% if not loop.last %} |{% endif %}
|
||||
{% else %}
|
||||
<span class="topBarElem" {% if menu._tooltip is defined %}title="{{ menu._tooltip }}"{% endif %}>
|
||||
<a id="topmenu-{{ menu._url.module|lower }}" href="index.php{{ menu._url|urlRewriteWithParameters }}">{{ label|translate }}</a>
|
||||
</span>{% if not loop.last %} | {% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||