forked from rebillar/site-accueil-insa
1704 lines
61 KiB
JavaScript
1704 lines
61 KiB
JavaScript
/*!
|
|
* Matomo - free/libre analytics platform
|
|
*
|
|
* @link https://matomo.org
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
|
*/
|
|
|
|
//
|
|
// TRANSITIONS ROW ACTION FOR DATA TABLES
|
|
//
|
|
|
|
function DataTable_RowActions_Transitions(dataTable) {
|
|
this.dataTable = dataTable;
|
|
this.transitions = null;
|
|
}
|
|
|
|
DataTable_RowActions_Transitions.prototype = new DataTable_RowAction;
|
|
|
|
/** Static helper method to launch transitions from anywhere */
|
|
DataTable_RowActions_Transitions.launchForUrl = function (url, segment) {
|
|
var value = 'Transitions:url:' + url;
|
|
if (segment) {
|
|
value += ':segment:' + segment;
|
|
}
|
|
broadcast.propagateNewPopoverParameter('RowAction', value);
|
|
};
|
|
|
|
DataTable_RowActions_Transitions.isPageUrlReport = function (module, action) {
|
|
return module == 'Actions' &&
|
|
(action == 'getPageUrls' || action == 'getEntryPageUrls' || action == 'getExitPageUrls' || action == 'getPageUrlsFollowingSiteSearch');
|
|
};
|
|
|
|
DataTable_RowActions_Transitions.isPageTitleReport = function (module, action) {
|
|
return module == 'Actions' && (action == 'getPageTitles' || action == 'getPageTitlesFollowingSiteSearch');
|
|
};
|
|
|
|
DataTable_RowActions_Transitions.registeredReports = [];
|
|
DataTable_RowActions_Transitions.registerReport = function (handler) {
|
|
DataTable_RowActions_Transitions.registeredReports.push(handler);
|
|
}
|
|
|
|
DataTable_RowActions_Transitions.prototype.trigger = function (tr, e, subTableLabel) {
|
|
var i = 0;
|
|
for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) {
|
|
var report = DataTable_RowActions_Transitions.registeredReports[i];
|
|
if (report
|
|
&& report.trigger
|
|
&& report.isAvailableOnReport
|
|
&& report.isAvailableOnReport(this.dataTable.param)) {
|
|
report.trigger.apply(this, arguments);
|
|
return;
|
|
}
|
|
}
|
|
|
|
alert('Transitions can\'t be used on this report.');
|
|
};
|
|
|
|
DataTable_RowActions_Transitions.prototype.performAction = function (label, tr, e) {
|
|
var separator = ' > '; // LabelFilter::SEPARATOR_RECURSIVE_LABEL
|
|
var labelParts = label.split(separator);
|
|
for (var i = 0; i < labelParts.length; i++) {
|
|
var labelPart = labelParts[i].replace('@', '');
|
|
labelParts[i] = $.trim(decodeURIComponent(labelPart));
|
|
}
|
|
var delimiter = piwik.config.action_url_category_delimiter;
|
|
if(this.dataTable.param.action.indexOf('PageTitles') !== false) {
|
|
delimiter = piwik.config.action_title_category_delimiter;
|
|
}
|
|
label = labelParts.join(delimiter);
|
|
this.openPopover('title:' + label);
|
|
};
|
|
|
|
DataTable_RowActions_Transitions.prototype.doOpenPopover = function (link) {
|
|
var ALLOWED_OVERRIDE_PARAMS = ['segment', 'date', 'period', 'idSite'];
|
|
|
|
var parts = link.split(':');
|
|
|
|
var overrideParams = {};
|
|
|
|
var i = 0;
|
|
while (i < parts.length) {
|
|
var paramName = decodeURIComponent(parts[i]);
|
|
if (ALLOWED_OVERRIDE_PARAMS.indexOf(paramName) === -1) {
|
|
i += 1;
|
|
continue;
|
|
}
|
|
|
|
overrideParams[paramName] = decodeURIComponent(parts[i + 1]);
|
|
parts.splice(i, 2);
|
|
}
|
|
|
|
if (parts.length < 2) {
|
|
return;
|
|
}
|
|
|
|
var actionType = parts[0];
|
|
parts.shift();
|
|
var actionName = parts.join(':');
|
|
|
|
if (this.transitions === null) {
|
|
this.transitions = new Piwik_Transitions(actionType, actionName, this, overrideParams);
|
|
} else {
|
|
this.transitions.reset(actionType, actionName, segment);
|
|
}
|
|
this.transitions.showPopover();
|
|
};
|
|
|
|
DataTable_RowActions_Registry.register({
|
|
|
|
name: 'Transitions',
|
|
|
|
dataTableIcon: 'icon-transition',
|
|
|
|
order: 20,
|
|
|
|
dataTableIconTooltip: [
|
|
_pk_translate('General_TransitionsRowActionTooltipTitle'),
|
|
_pk_translate('General_TransitionsRowActionTooltip')
|
|
],
|
|
|
|
createInstance: function (dataTable) {
|
|
return new DataTable_RowActions_Transitions(dataTable);
|
|
},
|
|
|
|
isAvailableOnReport: function (dataTableParams) {
|
|
|
|
if (piwik.transitionsMaxPeriodAllowed && dataTableParams['period']) {
|
|
|
|
if (dataTableParams['period'] === 'range') {
|
|
|
|
var piwikPeriods = piwikHelper.getAngularDependency('piwikPeriods');
|
|
if (piwikPeriods) {
|
|
var range = piwikPeriods.parse(dataTableParams['period'], dataTableParams['date']);
|
|
if (range) {
|
|
var rangeDays = range.getDayCount();
|
|
if ((piwik.transitionsMaxPeriodAllowed === 'day' && rangeDays > 1) ||
|
|
(piwik.transitionsMaxPeriodAllowed === 'week' && rangeDays > 7) ||
|
|
(piwik.transitionsMaxPeriodAllowed === 'month' && rangeDays > 31) ||
|
|
(piwik.transitionsMaxPeriodAllowed === 'year' && rangeDays > 365))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (piwik.transitionsMaxPeriodAllowed === 'day' && dataTableParams['period'] !== 'day') {
|
|
return false;
|
|
}
|
|
if (piwik.transitionsMaxPeriodAllowed === 'week' && dataTableParams['period'] !== 'day'
|
|
&& dataTableParams['period'] !== 'week') {
|
|
return false;
|
|
}
|
|
if (piwik.transitionsMaxPeriodAllowed === 'month' && dataTableParams['period'] !== 'day'
|
|
&& dataTableParams['period'] !== 'week' && dataTableParams['period'] !== 'month') {
|
|
return false;
|
|
}
|
|
if (piwik.transitionsMaxPeriodAllowed === 'year' && dataTableParams['period'] !== 'day'
|
|
&& dataTableParams['period'] !== 'week' && dataTableParams['period'] !== 'month'
|
|
&& dataTableParams['period'] !== 'year'
|
|
) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
var i = 0;
|
|
for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) {
|
|
var report = DataTable_RowActions_Transitions.registeredReports[i];
|
|
if (report
|
|
&& report.isAvailableOnReport
|
|
&& report.isAvailableOnReport(dataTableParams)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
isAvailableOnRow: function (dataTableParams, tr) {
|
|
if (tr.hasClass('subDataTable') || tr.hasClass('totalsRow')) {
|
|
// not available on groups (i.e. folders)
|
|
return false;
|
|
}
|
|
|
|
var i = 0;
|
|
for (i; i < DataTable_RowActions_Transitions.registeredReports.length; i++) {
|
|
var report = DataTable_RowActions_Transitions.registeredReports[i];
|
|
if (report
|
|
&& report.isAvailableOnRow
|
|
&& report.isAvailableOnReport
|
|
&& report.isAvailableOnReport(dataTableParams)) {
|
|
return report.isAvailableOnRow(dataTableParams, tr);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
});
|
|
|
|
//
|
|
// TRANSITIONS IMPLEMENTATION
|
|
//
|
|
|
|
function Piwik_Transitions(actionType, actionName, rowAction, overrideParams) {
|
|
this.reset(actionType, actionName, overrideParams);
|
|
this.rowAction = rowAction;
|
|
|
|
this.ajax = new Piwik_Transitions_Ajax();
|
|
this.model = new Piwik_Transitions_Model(this.ajax);
|
|
|
|
this.leftGroups = ['previousPages', 'previousSiteSearches', 'searchEngines', 'socialNetworks', 'websites', 'campaigns'];
|
|
this.rightGroups = ['followingPages', 'followingSiteSearches', 'downloads', 'outlinks'];
|
|
}
|
|
|
|
Piwik_Transitions.prototype.reset = function (actionType, actionName, overrideParams) {
|
|
this.actionType = actionType;
|
|
this.actionName = actionName;
|
|
this.overrideParams = overrideParams;
|
|
|
|
this.popover = null;
|
|
this.canvas = null;
|
|
this.centerBox = null;
|
|
|
|
this.leftOpenGroup = 'previousPages';
|
|
this.rightOpenGroup = 'followingPages';
|
|
|
|
this.highlightedGroup = false;
|
|
this.highlightedGroupSide = false;
|
|
this.highlightedGroupCenterEl = false;
|
|
};
|
|
|
|
/** Open the popover */
|
|
Piwik_Transitions.prototype.showPopover = function (showEmbeddedInReport) {
|
|
var self = this;
|
|
this.showEmbeddedInReport = showEmbeddedInReport;
|
|
|
|
$('#transitions_report .popoverContainer').hide();
|
|
|
|
if (showEmbeddedInReport) {
|
|
this.popover = $('#transitions_report');
|
|
$('#Transitions_Error_Container').hide();
|
|
$('#transitions_inline_loading').show();
|
|
} else {
|
|
this.popover = Piwik_Popover.showLoading('Transitions', self.actionName, 550);
|
|
Piwik_Popover.addHelpButton('https://matomo.org/docs/transitions');
|
|
}
|
|
|
|
var bothLoaded = function () {
|
|
if (!showEmbeddedInReport) {
|
|
Piwik_Popover.setContent(Piwik_Transitions.popoverHtml);
|
|
} else {
|
|
$('#transitions_inline_loading').hide();
|
|
$('#transitions_report .popoverContainer').html(Piwik_Transitions.popoverHtml);
|
|
$('#transitions_report .popoverContainer').show();
|
|
}
|
|
self.preparePopover();
|
|
self.model.htmlLoaded();
|
|
|
|
if (self.model.searchEnginesNbTransitions > 0 && self.model.websitesNbTransitions > 0 && self.model.socialNetworksNbTransitions > 0
|
|
&& self.model.campaignsNbTransitions > 0) {
|
|
self.canvas.narrowMode();
|
|
}
|
|
|
|
self.render();
|
|
self.canvas.truncateVisibleBoxTexts();
|
|
};
|
|
|
|
// load the popover HTML (only done once)
|
|
var callbackForHtml = false;
|
|
if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
|
|
this.ajax.callTransitionsController('renderPopover', function (html) {
|
|
Piwik_Transitions.popoverHtml = html;
|
|
if (callbackForHtml !== false) {
|
|
callbackForHtml();
|
|
}
|
|
});
|
|
}
|
|
|
|
// load the data
|
|
self.model.loadData(self.actionType, self.actionName, self.overrideParams, function () {
|
|
if (typeof Piwik_Transitions.popoverHtml == 'undefined') {
|
|
// html not there yet
|
|
callbackForHtml = bothLoaded;
|
|
} else {
|
|
// html already loaded
|
|
bothLoaded();
|
|
}
|
|
});
|
|
};
|
|
|
|
/** Prepare the popover with the basic DOM to add data later. */
|
|
Piwik_Transitions.prototype.preparePopover = function () {
|
|
var self = this;
|
|
|
|
var width = 900;
|
|
var height = 550;
|
|
|
|
var canvasBgLeft = self.prepareCanvas('Background_Left', width, height);
|
|
var canvasBgRight = self.prepareCanvas('Background_Right', width, height);
|
|
var canvasLeft = self.prepareCanvas('Left', width, height);
|
|
var canvasRight = self.prepareCanvas('Right', width, height);
|
|
var canvasLoops = self.prepareCanvas('Loops', width, height);
|
|
|
|
self.canvas = new Piwik_Transitions_Canvas(canvasBgLeft, canvasBgRight, canvasLeft, canvasRight,
|
|
canvasLoops, width, height);
|
|
|
|
self.centerBox = self.popover.find('#Transitions_CenterBox');
|
|
|
|
var title = self.actionName;
|
|
if (self.actionType == 'url') {
|
|
title = Piwik_Transitions_Util.shortenUrl(title, true);
|
|
}
|
|
var h2 = self.centerBox.find('h2');
|
|
var textContainer = h2;
|
|
if (self.actionType == 'url') {
|
|
var a = $(document.createElement('a'));
|
|
a.attr('href', self.actionName);
|
|
a.attr('rel', 'noreferrer noopener');
|
|
a.attr('target', '_blank');
|
|
h2.append(a);
|
|
textContainer = a;
|
|
}
|
|
|
|
textContainer.addClass('Transitions_ApplyTextAndTruncate')
|
|
.data('text', title)
|
|
.data('maxLines', 3);
|
|
|
|
var element = textContainer.add(self.popover.find('p.Transitions_Pageviews'));
|
|
|
|
element.tooltip({
|
|
track: true,
|
|
content: function () {
|
|
var totalNbPageviews = self.model.getTotalNbPageviews();
|
|
if (totalNbPageviews > 0) {
|
|
|
|
var share = NumberFormatter.formatPercent(Math.round(self.model.pageviews / totalNbPageviews * 1000) / 10);
|
|
|
|
var text = Piwik_Transitions_Translations.ShareOfAllPageviews;
|
|
text = sprintf(text, NumberFormatter.formatNumber(self.model.pageviews), share);
|
|
text += '<br /><em>' + Piwik_Transitions_Translations.DateRange + ' ' + self.model.date + '</em>';
|
|
|
|
var title = '<h3>' + piwikHelper.addBreakpointsToUrl(self.actionName) + '</h3>';
|
|
|
|
return title + text;
|
|
}
|
|
return false;
|
|
},
|
|
items: '*',
|
|
tooltipClass: 'Transitions_Tooltip_Small',
|
|
show: false,
|
|
hide: false
|
|
});
|
|
};
|
|
|
|
Piwik_Transitions.prototype.prepareCanvas = function (canvasId, width, height) {
|
|
canvasId = 'Transitions_Canvas_' + canvasId;
|
|
var div = $('#' + canvasId).width(width).height(height);
|
|
var canvas;
|
|
|
|
if (typeof Piwik_Transitions.canvasCache == 'undefined'
|
|
|| typeof window.G_vmlCanvasManager != "undefined") {
|
|
// no recycling for excanvas because they are disposed properly anyway and
|
|
// recycling doesn't work this way in IE8
|
|
Piwik_Transitions.canvasCache = {};
|
|
}
|
|
|
|
if (typeof Piwik_Transitions.canvasCache[canvasId] == 'undefined') {
|
|
Piwik_Transitions.canvasCache[canvasId] = document.createElement('canvas');
|
|
canvas = Piwik_Transitions.canvasCache[canvasId];
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
} else {
|
|
canvas = Piwik_Transitions.canvasCache[canvasId];
|
|
canvas.getContext('2d').clearRect(0, 0, width, height);
|
|
}
|
|
|
|
div.append(canvas);
|
|
return canvas;
|
|
};
|
|
|
|
/** Render the popover content */
|
|
Piwik_Transitions.prototype.render = function () {
|
|
this.renderCenterBox();
|
|
|
|
this.renderLeftSide();
|
|
this.renderRightSide();
|
|
|
|
this.renderLoops();
|
|
|
|
var $rootScope = piwikHelper.getAngularDependency('$rootScope');
|
|
if ($rootScope) {
|
|
$rootScope.$emit('Transitions.dataChanged', {'actionType': this.actionType, 'actionName': this.actionName});
|
|
}
|
|
};
|
|
|
|
/** Render left side: referrer groups & direct entries */
|
|
Piwik_Transitions.prototype.renderLeftSide = function (onlyBg) {
|
|
this.renderGroups(this.leftGroups, this.leftOpenGroup, 'left', onlyBg);
|
|
this.renderEntries(onlyBg);
|
|
|
|
this.reRenderIfNeededToCenter('left', onlyBg);
|
|
};
|
|
|
|
/** Render right side: following pages & exits */
|
|
Piwik_Transitions.prototype.renderRightSide = function (onlyBg) {
|
|
this.renderGroups(this.rightGroups, this.rightOpenGroup, 'right', onlyBg);
|
|
this.renderExits(onlyBg);
|
|
|
|
this.reRenderIfNeededToCenter('right', onlyBg);
|
|
};
|
|
|
|
/** Helper method to render open and closed groups for both sides */
|
|
Piwik_Transitions.prototype.renderGroups = function (groups, openGroup, side, onlyBg) {
|
|
for (var i = 0; i < groups.length; i++) {
|
|
var groupName = groups[i];
|
|
if (groupName == openGroup) {
|
|
if (i != 0) {
|
|
var spacing = this.canvas.isNarrowMode() ? 7 : 13;
|
|
this.canvas.addBoxSpacing(spacing, side);
|
|
}
|
|
this.renderOpenGroup(groupName, side, onlyBg);
|
|
} else {
|
|
this.renderClosedGroup(groupName, side, onlyBg);
|
|
}
|
|
}
|
|
|
|
this.canvas.addBoxSpacing(13, side);
|
|
};
|
|
|
|
/**
|
|
* If one side doesn't have much information, it doesn't look good to start from y=0.
|
|
* In this case, add some spacing on top and redraw.
|
|
*/
|
|
Piwik_Transitions.prototype.reRenderIfNeededToCenter = function (side, onlyBg) {
|
|
var height = (side == 'left' ? this.canvas.leftBoxPositionY : this.canvas.rightBoxPositionY) - 20;
|
|
if (height < 460 && !this.reRendering) {
|
|
var yOffset = (460 - height) / 2;
|
|
this.canvas.clearSide(side, onlyBg);
|
|
this.canvas.addBoxSpacing(yOffset, side);
|
|
this.reRendering = true;
|
|
side == 'left' ? this.renderLeftSide(onlyBg) : this.renderRightSide(onlyBg);
|
|
this.reRendering = false;
|
|
}
|
|
};
|
|
|
|
/** Render the center box with the main metrics */
|
|
Piwik_Transitions.prototype.renderCenterBox = function () {
|
|
var box = this.centerBox;
|
|
|
|
Piwik_Transitions_Util.replacePlaceholderInHtml(
|
|
box.find('.Transitions_Pageviews'), NumberFormatter.formatNumber(this.model.pageviews));
|
|
|
|
var self = this;
|
|
var showMetric = function (cssClass, modelProperty, highlightCurveOnSide, groupCanBeExpanded) {
|
|
var el = box.find('.Transitions_' + cssClass);
|
|
Piwik_Transitions_Util.replacePlaceholderInHtml(el, NumberFormatter.formatNumber(self.model[modelProperty]));
|
|
|
|
if (self.model[modelProperty] == 0) {
|
|
el.addClass('Transitions_Value0');
|
|
} else {
|
|
self.addTooltipShowingPercentageOfAllPageviews(el, modelProperty);
|
|
var groupName = cssClass.charAt(0).toLowerCase() + cssClass.slice(1);
|
|
el.hover(function () {
|
|
self.highlightGroup(groupName, highlightCurveOnSide);
|
|
}, function () {
|
|
self.unHighlightGroup(groupName, highlightCurveOnSide);
|
|
});
|
|
if (groupCanBeExpanded) {
|
|
el.click(function () {
|
|
self.openGroup(highlightCurveOnSide, groupName);
|
|
}).css('cursor', 'pointer');
|
|
}
|
|
}
|
|
};
|
|
|
|
showMetric('DirectEntries', 'directEntries', 'left', false);
|
|
showMetric('PreviousSiteSearches', 'previousSiteSearchesNbTransitions', 'left', true);
|
|
showMetric('PreviousPages', 'previousPagesNbTransitions', 'left', true);
|
|
showMetric('SearchEngines', 'searchEnginesNbTransitions', 'left', true);
|
|
showMetric('SocialNetworks', 'socialNetworksNbTransitions', 'left', true);
|
|
showMetric('Websites', 'websitesNbTransitions', 'left', true);
|
|
showMetric('Campaigns', 'campaignsNbTransitions', 'left', true);
|
|
|
|
showMetric('FollowingPages', 'followingPagesNbTransitions', 'right', true);
|
|
showMetric('FollowingSiteSearches', 'followingSiteSearchesNbTransitions', 'right', true);
|
|
showMetric('Outlinks', 'outlinksNbTransitions', 'right', true);
|
|
showMetric('Downloads', 'downloadsNbTransitions', 'right', true);
|
|
showMetric('Exits', 'exits', 'right', false);
|
|
|
|
box.find('.Transitions_CenterBoxMetrics').show();
|
|
};
|
|
|
|
Piwik_Transitions.prototype.addTooltipShowingPercentageOfAllPageviews = function (element, metric) {
|
|
var tip = Piwik_Transitions_Translations.XOfAllPageviews;
|
|
var percentage = this.model.getPercentage(metric, true);
|
|
tip = sprintf(tip, '<strong>' + percentage + '</strong>');
|
|
|
|
element.tooltip({
|
|
track: true,
|
|
content: tip,
|
|
items: '*',
|
|
tooltipClass: 'Transitions_Tooltip_Small',
|
|
show: false,
|
|
hide: false
|
|
});
|
|
};
|
|
|
|
/** Render the loops (i.e. page reloads) */
|
|
Piwik_Transitions.prototype.renderLoops = function () {
|
|
if (this.model.loops == 0) {
|
|
return;
|
|
}
|
|
|
|
var loops = this.popover.find('#Transitions_Loops').show();
|
|
Piwik_Transitions_Util.replacePlaceholderInHtml(loops, NumberFormatter.formatNumber(this.model.loops));
|
|
|
|
this.addTooltipShowingPercentageOfAllPageviews(loops, 'loops');
|
|
|
|
this.canvas.renderLoops(this.model.getPercentage('loops'));
|
|
};
|
|
|
|
Piwik_Transitions.prototype.renderEntries = function (onlyBg) {
|
|
if (this.model.directEntries > 0) {
|
|
var self = this;
|
|
|
|
var isHighlighted = this.highlightedGroup == 'directEntries';
|
|
var gradient = this.canvas.createHorizontalGradient('entries', 'left', isHighlighted);
|
|
|
|
this.canvas.renderBox({
|
|
side: 'left',
|
|
onlyBg: onlyBg,
|
|
share: this.model.getPercentage('directEntries'),
|
|
gradient: gradient,
|
|
boxText: Piwik_Transitions_Translations.directEntries,
|
|
boxTextNumLines: 1,
|
|
boxTextCssClass: 'SingleLine',
|
|
smallBox: true,
|
|
onMouseOver: function () {
|
|
self.highlightGroup('directEntries', 'left');
|
|
},
|
|
onMouseOut: function () {
|
|
self.unHighlightGroup('directEntries', 'left');
|
|
}
|
|
});
|
|
this.canvas.addBoxSpacing(20, 'left');
|
|
}
|
|
};
|
|
|
|
Piwik_Transitions.prototype.renderExits = function (onlyBg) {
|
|
if (this.model.exits > 0) {
|
|
var self = this;
|
|
|
|
var isHighlighted = this.highlightedGroup == 'exits';
|
|
var gradient = this.canvas.createHorizontalGradient('exits', 'right', isHighlighted);
|
|
|
|
this.canvas.renderBox({
|
|
side: 'right',
|
|
onlyBg: onlyBg,
|
|
share: this.model.getPercentage('exits'),
|
|
gradient: gradient,
|
|
boxText: Piwik_Transitions_Translations.exits,
|
|
boxTextNumLines: 1,
|
|
boxTextCssClass: 'SingleLine',
|
|
smallBox: true,
|
|
onMouseOver: function () {
|
|
self.highlightGroup('exits', 'right');
|
|
},
|
|
onMouseOut: function () {
|
|
self.unHighlightGroup('exits', 'right');
|
|
}
|
|
});
|
|
this.canvas.addBoxSpacing(20, 'right');
|
|
}
|
|
};
|
|
|
|
/** Render the open group with the detailed data */
|
|
Piwik_Transitions.prototype.renderOpenGroup = function (groupName, side, onlyBg) {
|
|
var self = this;
|
|
|
|
// get data from the model
|
|
var nbTransitionsVarName = groupName + 'NbTransitions';
|
|
var nbTransitions = self.model[nbTransitionsVarName];
|
|
if (nbTransitions == 0) {
|
|
return;
|
|
}
|
|
|
|
var totalShare = this.model.getPercentage(nbTransitionsVarName);
|
|
var details = self.model.getDetailsForGroup(groupName);
|
|
|
|
// prepare gradients
|
|
var gradientItems = this.canvas.createHorizontalGradient('items', side);
|
|
var gradientOthers = this.canvas.createHorizontalGradient('others', side);
|
|
var gradientBackground =
|
|
this.canvas.createHorizontalGradient('background', side, groupName == this.highlightedGroup);
|
|
|
|
// remember current offsets to reset them later for drawing the background
|
|
var boxPositionBefore, curvePositionBefore;
|
|
if (side == 'left') {
|
|
boxPositionBefore = this.canvas.leftBoxPositionY;
|
|
curvePositionBefore = this.canvas.leftCurvePositionY;
|
|
} else {
|
|
boxPositionBefore = this.canvas.rightBoxPositionY;
|
|
curvePositionBefore = this.canvas.rightCurvePositionY;
|
|
}
|
|
|
|
// headline of the open group
|
|
var titleX, titleClass;
|
|
if (side == 'left') {
|
|
titleX = this.canvas.leftBoxBeginX + 10;
|
|
titleClass = 'BoxTextLeft';
|
|
} else {
|
|
titleX = this.canvas.rightBoxBeginX - 1;
|
|
titleClass = 'BoxTextRight';
|
|
}
|
|
if (!onlyBg) {
|
|
var groupTitle = self.model.getGroupTitle(groupName);
|
|
var titleEl = this.canvas.renderText(groupTitle, titleX, boxPositionBefore + 11,
|
|
[titleClass, 'TitleOfOpenGroup']);
|
|
titleEl.hover(function () {
|
|
self.highlightGroup(groupName, side);
|
|
}, function () {
|
|
self.unHighlightGroup(groupName, side);
|
|
});
|
|
}
|
|
this.canvas.addBoxSpacing(34, side);
|
|
|
|
// draw detail boxes
|
|
for (var i = 0; i < details.length; i++) {
|
|
var data = details[i];
|
|
var label = (typeof data.url != 'undefined' ? data.url : data.label);
|
|
label = (typeof label != 'undefined' && label !== null ? label : '');
|
|
var isOthers = (label == 'Others');
|
|
var onClick = false;
|
|
if (!isOthers && (groupName == 'previousPages' || groupName == 'followingPages')) {
|
|
|
|
if (this.showEmbeddedInReport) {
|
|
onClick = (function (url) {
|
|
return function () {
|
|
var $rootScope = piwikHelper.getAngularDependency('$rootScope');
|
|
if ($rootScope) {
|
|
$rootScope.$emit('Transitions.switchTransitionsUrl', {
|
|
url:url
|
|
});
|
|
}
|
|
};
|
|
})(label);
|
|
} else {
|
|
onClick = (function (url) {
|
|
return function () {
|
|
if (self.actionType == 'url') {
|
|
url = url.replace(/^(?!http)/, 'http://');
|
|
}
|
|
self.reloadPopover(url);
|
|
};
|
|
})(label);
|
|
}
|
|
|
|
} else if (!isOthers && (groupName == 'outlinks' || groupName == 'websites' || groupName == 'downloads')) {
|
|
onClick = label
|
|
}
|
|
|
|
var tooltip = Piwik_Transitions_Translations.XOfY;
|
|
tooltip = '<strong>' + sprintf(tooltip, data.referrals, nbTransitions) + '</strong>';
|
|
tooltip = this.model.getShareInGroupTooltip(tooltip, groupName);
|
|
|
|
var fullLabel = label;
|
|
var shortened = false;
|
|
if ((this.actionType == 'url' && (groupName == 'previousPages' || groupName == 'followingPages'))
|
|
|| groupName == 'downloads') {
|
|
// remove http + www + domain for internal URLs
|
|
label = Piwik_Transitions_Util.shortenUrl(label, true);
|
|
shortened = true;
|
|
} else if (groupName == 'outlinks' || groupName == 'websites') {
|
|
// remove http + www for external URLs
|
|
label = Piwik_Transitions_Util.shortenUrl(label);
|
|
shortened = true;
|
|
}
|
|
|
|
this.canvas.renderBox({
|
|
side: side,
|
|
onlyBg: onlyBg,
|
|
share: data.percentage / 100 * totalShare,
|
|
gradient: isOthers ? gradientOthers : gradientItems,
|
|
boxText: label,
|
|
boxTextTooltip: isOthers || !shortened ? false : fullLabel,
|
|
boxTextNumLines: 3,
|
|
curveText: NumberFormatter.formatPercent(data.percentage),
|
|
curveTextTooltip: tooltip,
|
|
onClick: onClick
|
|
});
|
|
}
|
|
|
|
// draw background
|
|
var boxPositionAfter, curvePositionAfter;
|
|
if (side == 'left') {
|
|
boxPositionAfter = this.canvas.leftBoxPositionY;
|
|
curvePositionAfter = this.canvas.leftCurvePositionY;
|
|
this.canvas.leftBoxPositionY = boxPositionBefore;
|
|
this.canvas.leftCurvePositionY = curvePositionBefore;
|
|
} else {
|
|
boxPositionAfter = this.canvas.rightBoxPositionY;
|
|
curvePositionAfter = this.canvas.rightCurvePositionY;
|
|
this.canvas.rightBoxPositionY = boxPositionBefore;
|
|
this.canvas.rightCurvePositionY = curvePositionBefore;
|
|
}
|
|
|
|
this.canvas.renderBox({
|
|
side: side,
|
|
boxHeight: boxPositionAfter - boxPositionBefore - this.canvas.boxSpacing - 2,
|
|
curveHeight: curvePositionAfter - curvePositionBefore - this.canvas.curveSpacing,
|
|
gradient: gradientBackground,
|
|
bgCanvas: true
|
|
});
|
|
|
|
var spacing = this.canvas.isNarrowMode() ? 8 : 15;
|
|
this.canvas.addBoxSpacing(spacing, side);
|
|
};
|
|
|
|
/** Render a closed group without detailed data, only one box for the sum */
|
|
Piwik_Transitions.prototype.renderClosedGroup = function (groupName, side, onlyBg) {
|
|
var self = this;
|
|
|
|
var isHighlighted = groupName == this.highlightedGroup;
|
|
var gradient = this.canvas.createHorizontalGradient('closed-group', side, isHighlighted);
|
|
|
|
var nbTransitionsVarName = groupName + 'NbTransitions';
|
|
|
|
if (self.model[nbTransitionsVarName] == 0) {
|
|
return;
|
|
}
|
|
|
|
self.canvas.renderBox({
|
|
side: side,
|
|
onlyBg: onlyBg,
|
|
share: self.model.getPercentage(nbTransitionsVarName),
|
|
gradient: gradient,
|
|
boxText: self.model.getGroupTitle(groupName),
|
|
boxTextNumLines: 1,
|
|
boxTextCssClass: 'SingleLine',
|
|
boxIcon: 'plugins/Morpheus/images/plus_blue.png',
|
|
smallBox: true,
|
|
onClick: function () {
|
|
self.unHighlightGroup(groupName, side);
|
|
self.openGroup(side, groupName);
|
|
},
|
|
onMouseOver: function () {
|
|
self.highlightGroup(groupName, side);
|
|
},
|
|
onMouseOut: function () {
|
|
self.unHighlightGroup(groupName, side);
|
|
}
|
|
});
|
|
};
|
|
|
|
/** Reload the entire popover for a different URL */
|
|
Piwik_Transitions.prototype.reloadPopover = function (url) {
|
|
if (this.rowAction) {
|
|
this.rowAction.openPopover(this.actionType + ':' + url);
|
|
} else {
|
|
this.reset(this.actionType, url);
|
|
this.showPopover();
|
|
}
|
|
};
|
|
|
|
/** Redraw the left or right sides with a different group opened */
|
|
Piwik_Transitions.prototype.openGroup = function (side, groupName) {
|
|
|
|
this.canvas.clearSide(side);
|
|
|
|
if (side == 'left') {
|
|
this.leftOpenGroup = groupName;
|
|
this.renderLeftSide();
|
|
} else {
|
|
this.rightOpenGroup = groupName;
|
|
this.renderRightSide();
|
|
}
|
|
|
|
this.renderLoops();
|
|
|
|
this.canvas.truncateVisibleBoxTexts();
|
|
};
|
|
|
|
/** Highlight a group: change curve color and highlight metric in the center box */
|
|
Piwik_Transitions.prototype.highlightGroup = function (groupName, side) {
|
|
if (this.highlightedGroup == groupName) {
|
|
return;
|
|
}
|
|
if (this.highlightedGroup !== false) {
|
|
this.unHighlightGroup(this.highlightedGroup, this.highlightedGroupSide);
|
|
}
|
|
|
|
this.highlightedGroup = groupName;
|
|
this.highlightedGroupSide = side;
|
|
|
|
var cssClass = 'Transitions_' + groupName.charAt(0).toUpperCase() + groupName.slice(1);
|
|
this.highlightedGroupCenterEl = this.canvas.container.find('.' + cssClass);
|
|
this.highlightedGroupCenterEl.addClass('Transitions_Highlighted');
|
|
|
|
this.canvas.clearSide(side, true);
|
|
if (side == 'left') {
|
|
this.renderLeftSide(true);
|
|
} else {
|
|
this.renderRightSide(true);
|
|
}
|
|
this.renderLoops();
|
|
};
|
|
|
|
/** Remove highlight after using highlightGroup() */
|
|
Piwik_Transitions.prototype.unHighlightGroup = function (groupName, side) {
|
|
if (this.highlightedGroup === false) {
|
|
return;
|
|
}
|
|
|
|
this.highlightedGroupCenterEl.removeClass('Transitions_Highlighted');
|
|
|
|
this.highlightedGroup = false;
|
|
this.highlightedGroupSide = false;
|
|
this.highlightedGroupCenterEl = false;
|
|
|
|
this.canvas.clearSide(side, true);
|
|
if (side == 'left') {
|
|
this.renderLeftSide(true);
|
|
} else {
|
|
this.renderRightSide(true);
|
|
}
|
|
this.renderLoops();
|
|
};
|
|
|
|
// --------------------------------------
|
|
// CANVAS
|
|
// --------------------------------------
|
|
|
|
function Piwik_Transitions_Canvas(canvasBgLeft, canvasBgRight, canvasLeft, canvasRight, canvasLoops, width, height) {
|
|
|
|
if (typeof window.G_vmlCanvasManager != "undefined") {
|
|
window.G_vmlCanvasManager.initElement(canvasBgLeft);
|
|
window.G_vmlCanvasManager.initElement(canvasBgRight);
|
|
window.G_vmlCanvasManager.initElement(canvasLeft);
|
|
window.G_vmlCanvasManager.initElement(canvasRight);
|
|
window.G_vmlCanvasManager.initElement(canvasLoops);
|
|
}
|
|
|
|
if (!canvasBgLeft.getContext) {
|
|
alert('Your browser is not supported.');
|
|
return;
|
|
}
|
|
|
|
/** DOM element that contains the canvas */
|
|
this.container = $(canvasBgLeft).parent().parent();
|
|
|
|
/** Drawing context of the canvases */
|
|
this.contextBgLeft = canvasBgLeft.getContext('2d');
|
|
this.contextBgRight = canvasBgRight.getContext('2d');
|
|
this.contextLeft = canvasLeft.getContext('2d');
|
|
this.contextRight = canvasRight.getContext('2d');
|
|
this.contextLoops = canvasLoops.getContext('2d');
|
|
|
|
/** Width of the entire canvas */
|
|
this.width = width;
|
|
/** Height of the entire canvas */
|
|
this.height = height;
|
|
|
|
/** Current Y positions */
|
|
this.leftBoxPositionY = this.originalBoxPositionY = 0;
|
|
this.leftCurvePositionY = this.originalCurvePositionY = 110;
|
|
this.rightBoxPositionY = this.originalBoxPositionY;
|
|
this.rightCurvePositionY = this.originalCurvePositionY;
|
|
|
|
/** Width of the rectangular box */
|
|
this.boxWidth = 175;
|
|
/** Height of the rectangular box */
|
|
this.boxHeight = 53;
|
|
/** Height of a smaller rectangular box */
|
|
this.smallBoxHeight = 30;
|
|
/** Width of the curve that connects the boxes to the center */
|
|
this.curveWidth = 170;
|
|
/** Line-height of the text */
|
|
this.lineHeight = 14;
|
|
/** Spacing between rectangular boxes */
|
|
this.boxSpacing = 7;
|
|
/** Spacing between the curves where they connect to the center */
|
|
this.curveSpacing = 1.5;
|
|
|
|
/** The total net height (without curve spacing) of the curves as they connect to the center */
|
|
this.totalHeightOfConnections = 205;
|
|
|
|
/** X positions of the left box - begin means left, end means right */
|
|
this.leftBoxBeginX = 0;
|
|
this.leftCurveBeginX = this.leftBoxBeginX + this.boxWidth;
|
|
this.leftCurveEndX = this.leftCurveBeginX + this.curveWidth;
|
|
|
|
/** X positions of the right box - begin means left, end means right */
|
|
this.rightBoxEndX = this.width;
|
|
this.rightBoxBeginX = this.rightCurveEndX = this.rightBoxEndX - this.boxWidth;
|
|
this.rightCurveBeginX = this.rightCurveEndX - this.curveWidth;
|
|
|
|
// load gradient colors from CSS
|
|
this.colors = {};
|
|
|
|
var transitionsColorNamespaces = ['entries', 'exits', 'background', 'closed-group', 'items', 'others', 'loops'];
|
|
var gradientColorNames = ['light', 'dark', 'light-highlighted', 'dark-highlighted'];
|
|
for (var i = 0; i != transitionsColorNamespaces.length; ++i) {
|
|
var namespace = 'transition-' + transitionsColorNamespaces[i];
|
|
this.colors[namespace] = piwik.ColorManager.getColors(namespace, gradientColorNames);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Activate narrow mode: draw groups a bit more compact in order to save space
|
|
* for more than 3 referrer groups.
|
|
*/
|
|
Piwik_Transitions_Canvas.prototype.narrowMode = function () {
|
|
this.smallBoxHeight = 26;
|
|
this.boxSpacing = 4;
|
|
this.narrowMode = true;
|
|
};
|
|
|
|
Piwik_Transitions_Canvas.prototype.isNarrowMode = function () {
|
|
return typeof this.narrowMode != 'undefined';
|
|
};
|
|
|
|
/**
|
|
* Helper to create horizontal gradients
|
|
*
|
|
* @param {String} colorGroup
|
|
* @param {String} position left|right
|
|
* @param {Boolean} isHighlighted
|
|
*/
|
|
Piwik_Transitions_Canvas.prototype.createHorizontalGradient = function (colorGroup, position, isHighlighted) {
|
|
var fromX, toX, fromColor, toColor, lightColor, darkColor;
|
|
|
|
colorGroup = 'transition-' + colorGroup;
|
|
if (isHighlighted) {
|
|
lightColor = this.colors[colorGroup]['light-highlighted'];
|
|
darkColor = this.colors[colorGroup]['dark-highlighted'];
|
|
} else {
|
|
lightColor = this.colors[colorGroup]['light'];
|
|
darkColor = this.colors[colorGroup]['dark'];
|
|
}
|
|
|
|
if (position == 'left') {
|
|
// gradient is used to fill a box on the left
|
|
fromX = this.leftBoxBeginX + 50;
|
|
toX = this.leftCurveEndX - 20;
|
|
fromColor = lightColor;
|
|
toColor = darkColor;
|
|
} else {
|
|
// gradient is used to fill a box on the right
|
|
fromX = this.rightCurveBeginX + 20;
|
|
toX = this.rightBoxEndX - 50;
|
|
fromColor = darkColor;
|
|
toColor = lightColor;
|
|
}
|
|
|
|
var gradient = this.contextBgLeft.createLinearGradient(fromX, 0, toX, 0);
|
|
gradient.addColorStop(0, fromColor);
|
|
gradient.addColorStop(1, toColor);
|
|
|
|
return gradient;
|
|
};
|
|
|
|
/** Render text using a div inside the container */
|
|
Piwik_Transitions_Canvas.prototype.renderText = function (text, x, y, cssClass, onClick, icon, maxLines) {
|
|
var div = this.addDomElement('div', 'Text');
|
|
div.css({
|
|
left: x + 'px',
|
|
top: y + 'px'
|
|
});
|
|
if (icon) {
|
|
div.addClass('Transitions_HasBackground');
|
|
div.css({backgroundImage: 'url(' + icon + ')'});
|
|
}
|
|
if (cssClass) {
|
|
if (typeof cssClass == 'object') {
|
|
for (var i = 0; i < cssClass.length; i++) {
|
|
div.addClass('Transitions_' + cssClass[i]);
|
|
}
|
|
} else {
|
|
div.addClass('Transitions_' + cssClass);
|
|
}
|
|
}
|
|
var textContainer = div;
|
|
if (onClick) {
|
|
if (typeof onClick == 'function') {
|
|
div.css('cursor', 'pointer').hover(function () {
|
|
$(this).addClass('Transitions_Hover');
|
|
},function () {
|
|
$(this).removeClass('Transitions_Hover');
|
|
}).click(onClick);
|
|
} else {
|
|
var a = $(document.createElement('a'));
|
|
a.attr('href', onClick);
|
|
a.attr('rel', 'noreferrer noopener');
|
|
a.attr('target', '_blank');
|
|
div.append(a);
|
|
textContainer = a;
|
|
}
|
|
}
|
|
if (maxLines) {
|
|
textContainer.addClass('Transitions_ApplyTextAndTruncate').data('text', text);
|
|
} else {
|
|
textContainer.html(text);
|
|
}
|
|
return div;
|
|
};
|
|
|
|
/** Add a DOM element inside the container (as a sibling of the canvas) */
|
|
Piwik_Transitions_Canvas.prototype.addDomElement = function (tagName, cssClass) {
|
|
var el = $(document.createElement('div')).addClass('Transitions_' + cssClass);
|
|
this.container.append(el);
|
|
return el;
|
|
};
|
|
|
|
/**
|
|
* Truncate box texts by replacing the middle part with ...
|
|
* This method looks for the class Transitions_ApplyTextAndTruncate.
|
|
* It then looks up data-text and truncates it until it fits.
|
|
*/
|
|
Piwik_Transitions_Canvas.prototype.truncateVisibleBoxTexts = function () {
|
|
this.container.find('.Transitions_ApplyTextAndTruncate').each(function () {
|
|
var container = $(this).html('<span>');
|
|
var span = container.find('span');
|
|
|
|
var text = container.data('text');
|
|
span.html(piwikHelper.addBreakpointsToUrl(text));
|
|
|
|
var divHeight = container.innerHeight();
|
|
if (container.data('maxLines')) {
|
|
divHeight = container.data('maxLines') * (parseInt(container.css('lineHeight'), 10) + .2);
|
|
}
|
|
|
|
var leftPart = false;
|
|
var rightPart = false;
|
|
|
|
while (divHeight < span.outerHeight()) {
|
|
if (leftPart === false) {
|
|
var middle = Math.round(text.length / 2);
|
|
leftPart = text.substring(0, middle);
|
|
rightPart = text.substring(middle, text.length);
|
|
}
|
|
leftPart = leftPart.substring(0, leftPart.length - 2);
|
|
rightPart = rightPart.substring(2, rightPart.length);
|
|
text = leftPart + '...' + rightPart;
|
|
span.html(piwikHelper.addBreakpointsToUrl(text));
|
|
}
|
|
|
|
span.removeClass('Transitions_Truncate');
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Render a box.
|
|
* This method automatically keeps track of the current position.
|
|
*
|
|
* PARAMS (pass as object):
|
|
* side: left or right
|
|
* share: of the box in the total amount of incoming transitions
|
|
* gradient: for filling the box
|
|
* boxText: to be placed inside the box (optional)
|
|
* boxTextNumLines: the number of lines to be placed in the box (optional)
|
|
* boxTextCssClass: for divs containing the texts (optional)
|
|
* boxTextTooltip: text for a tooltip this is when hovering the box text (optional)
|
|
* curveText: to be placed where the curve begins (optional)
|
|
* curveTextTooltip: text for a tooltip that is shown when hovering the curve text (optional)
|
|
* smallBox: use this.smallBoxHeight instead of this.boxHeight (optional)
|
|
* boxIcon: path to an icon that is put in front of the text (optional)
|
|
* onClick: click callback for the text in the box (optional)
|
|
* onMouseOver: mouse over callback for the text in the box (optional)
|
|
* onMouseOut: mouse over callback for the text in the box (optional)
|
|
* onlyBg: render only the background, not the text; used for highlighting (optional)
|
|
*
|
|
* Only used for background:
|
|
* curveHeight: fix height in px instead of share
|
|
* boxHeight: fix box height in px
|
|
* bgCanvas: true to draw on background canvas
|
|
*/
|
|
Piwik_Transitions_Canvas.prototype.renderBox = function (params) {
|
|
var curveHeight = params.curveHeight ? params.curveHeight :
|
|
Math.round(this.totalHeightOfConnections * params.share);
|
|
curveHeight = Math.max(curveHeight, 1);
|
|
|
|
var boxHeight = this.boxHeight;
|
|
if (params.smallBox) {
|
|
boxHeight = this.smallBoxHeight;
|
|
}
|
|
if (params.boxHeight) {
|
|
boxHeight = params.boxHeight;
|
|
}
|
|
|
|
var context;
|
|
if (params.bgCanvas) {
|
|
context = params.side == 'left' ? this.contextBgLeft : this.contextBgRight;
|
|
} else {
|
|
context = params.side == 'left' ? this.contextLeft : this.contextRight;
|
|
}
|
|
|
|
// background
|
|
context.fillStyle = params.gradient;
|
|
context.beginPath();
|
|
if (params.side == 'left') {
|
|
this.renderLeftBoxBg(context, boxHeight, curveHeight);
|
|
} else {
|
|
this.renderRightBoxBg(context, boxHeight, curveHeight);
|
|
}
|
|
if (typeof context.endPath == 'function') {
|
|
context.endPath();
|
|
}
|
|
|
|
// text inside the box
|
|
if (params.boxText && !params.onlyBg) {
|
|
var onClick = params.onClick;
|
|
var boxTextLeft, boxTextTop, el;
|
|
if (params.side == 'left') {
|
|
boxTextLeft = this.leftBoxBeginX + 10;
|
|
boxTextTop = this.leftBoxPositionY + boxHeight / 2 - params.boxTextNumLines * this.lineHeight / 2;
|
|
el = this.renderText(params.boxText, boxTextLeft, boxTextTop, 'BoxTextLeft', onClick, params.boxIcon,
|
|
params.boxTextNumLines);
|
|
} else {
|
|
boxTextLeft = this.rightBoxBeginX;
|
|
boxTextTop = this.rightBoxPositionY + boxHeight / 2 - params.boxTextNumLines * this.lineHeight / 2;
|
|
el = this.renderText(params.boxText, boxTextLeft, boxTextTop, 'BoxTextRight', onClick, params.boxIcon,
|
|
params.boxTextNumLines);
|
|
}
|
|
if (params.boxTextCssClass) {
|
|
el.addClass('Transitions_' + params.boxTextCssClass);
|
|
}
|
|
// tooltip
|
|
if (params.boxTextTooltip) {
|
|
var tip = piwikHelper.addBreakpointsToUrl(params.boxTextTooltip);
|
|
el.tooltip({
|
|
track: true,
|
|
content: tip,
|
|
items: '*',
|
|
tooltipClass: 'Transitions_Tooltip_Small',
|
|
show: false,
|
|
hide: false
|
|
});
|
|
}
|
|
if (typeof params.onMouseOver == 'function') {
|
|
el.mouseenter(params.onMouseOver);
|
|
}
|
|
if (typeof params.onMouseOut == 'function') {
|
|
el.mouseleave(params.onMouseOut);
|
|
}
|
|
}
|
|
|
|
// text at the beginning of the curve
|
|
if (params.curveText && !params.onlyBg) {
|
|
var curveTextLeft, curveTextTop;
|
|
if (params.side == 'left') {
|
|
curveTextLeft = this.leftBoxBeginX + this.boxWidth + 3;
|
|
curveTextTop = this.leftBoxPositionY + boxHeight / 2 - this.lineHeight / 2;
|
|
} else {
|
|
curveTextLeft = this.rightBoxBeginX - 37;
|
|
curveTextTop = this.rightBoxPositionY + boxHeight / 2 - this.lineHeight / 2;
|
|
}
|
|
var textDiv = this.renderText(params.curveText, curveTextLeft, curveTextTop,
|
|
params.side == 'left' ? 'CurveTextLeft' : 'CurveTextRight');
|
|
// tooltip
|
|
if (params.curveTextTooltip) {
|
|
textDiv.tooltip({
|
|
track: true,
|
|
content: params.curveTextTooltip,
|
|
items: '*',
|
|
tooltipClass: 'Transitions_Tooltip_Small',
|
|
show: false,
|
|
hide: false
|
|
});
|
|
}
|
|
}
|
|
|
|
if (params.side == 'left') {
|
|
this.leftBoxPositionY += boxHeight + this.boxSpacing;
|
|
this.leftCurvePositionY += curveHeight + this.curveSpacing;
|
|
} else {
|
|
this.rightBoxPositionY += boxHeight + this.boxSpacing;
|
|
this.rightCurvePositionY += curveHeight + this.curveSpacing;
|
|
}
|
|
};
|
|
|
|
Piwik_Transitions_Canvas.prototype.renderLeftBoxBg = function (context, boxHeight, curveHeight) {
|
|
// derive coordinates for ths curve
|
|
var leftUpper = {x: this.leftCurveBeginX, y: this.leftBoxPositionY};
|
|
var leftLower = {x: this.leftCurveBeginX, y: this.leftBoxPositionY + boxHeight};
|
|
var rightUpper = {x: this.leftCurveEndX, y: this.leftCurvePositionY};
|
|
var rightLower = {x: this.leftCurveEndX, y: this.leftCurvePositionY + curveHeight};
|
|
|
|
// derive control points for bezier curve
|
|
var center = (this.leftCurveBeginX + this.leftCurveEndX) / 2;
|
|
var cp1Upper = {x: center, y: leftUpper.y};
|
|
var cp2Upper = {x: center, y: rightUpper.y};
|
|
var cp1Lower = {x: center, y: rightLower.y};
|
|
var cp2Lower = {x: center, y: leftLower.y};
|
|
|
|
// the flow
|
|
context.moveTo(leftUpper.x, leftUpper.y);
|
|
context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
|
|
context.lineTo(rightLower.x, rightLower.y);
|
|
context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
|
|
|
|
// the box
|
|
context.lineTo(leftLower.x - this.boxWidth + 2, leftLower.y);
|
|
context.lineTo(leftLower.x - this.boxWidth, leftUpper.y);
|
|
context.lineTo(leftUpper.x, leftUpper.y);
|
|
context.fill();
|
|
};
|
|
|
|
Piwik_Transitions_Canvas.prototype.renderRightBoxBg = function (context, boxHeight, curveHeight) {
|
|
// derive coordinates for curve
|
|
var leftUpper = {x: this.rightCurveBeginX, y: this.rightCurvePositionY};
|
|
var leftLower = {x: this.rightCurveBeginX, y: this.rightCurvePositionY + curveHeight};
|
|
var rightUpper = {x: this.rightCurveEndX, y: this.rightBoxPositionY};
|
|
var rightLower = {x: this.rightCurveEndX, y: this.rightBoxPositionY + boxHeight};
|
|
|
|
// derive control points for bezier curve
|
|
var center = (this.rightCurveBeginX + this.rightCurveEndX) / 2;
|
|
var cp1Upper = {x: center, y: leftUpper.y};
|
|
var cp2Upper = {x: center, y: rightUpper.y};
|
|
var cp1Lower = {x: center, y: rightLower.y};
|
|
var cp2Lower = {x: center, y: leftLower.y};
|
|
|
|
// the flow part 1
|
|
context.moveTo(leftUpper.x, leftUpper.y);
|
|
context.bezierCurveTo(cp1Upper.x, cp1Upper.y, cp2Upper.x, cp2Upper.y, rightUpper.x, rightUpper.y);
|
|
|
|
// the box
|
|
context.lineTo(rightUpper.x + this.boxWidth, rightUpper.y);
|
|
context.lineTo(rightLower.x + this.boxWidth - 2, rightLower.y);
|
|
context.lineTo(rightLower.x, rightLower.y);
|
|
|
|
// the flow part 2
|
|
context.bezierCurveTo(cp1Lower.x, cp1Lower.y, cp2Lower.x, cp2Lower.y, leftLower.x, leftLower.y);
|
|
context.lineTo(leftUpper.x, leftUpper.y);
|
|
context.fill();
|
|
};
|
|
|
|
/** Add spacing after the current box */
|
|
Piwik_Transitions_Canvas.prototype.addBoxSpacing = function (spacing, side) {
|
|
if (side == 'left') {
|
|
this.leftBoxPositionY += spacing;
|
|
} else {
|
|
this.rightBoxPositionY += spacing;
|
|
}
|
|
};
|
|
|
|
Piwik_Transitions_Canvas.prototype.renderLoops = function (share) {
|
|
var curveHeight = Math.round(this.totalHeightOfConnections * share);
|
|
curveHeight = Math.max(curveHeight, 1);
|
|
|
|
// create gradient
|
|
var gradient = this.contextLoops.createLinearGradient(this.leftCurveEndX - 50, 0, this.rightCurveBeginX + 50, 0);
|
|
var light = this.colors['transition-loops']['light'];
|
|
var dark = this.colors['transition-loops']['dark'];
|
|
gradient.addColorStop(0, dark);
|
|
gradient.addColorStop(.5, light);
|
|
gradient.addColorStop(1, dark);
|
|
|
|
this.contextLoops.fillStyle = gradient;
|
|
|
|
this.contextLoops.beginPath();
|
|
|
|
// curve from the upper left connection to the center box to the lower left connection to the text box
|
|
var point1 = {x: this.leftCurveEndX, y: this.leftCurvePositionY};
|
|
var point2 = {x: this.leftCurveEndX, y: 470};
|
|
|
|
var cpLeftX = (this.leftCurveBeginX + this.leftCurveEndX) / 2 + 30;
|
|
var cp1 = {x: cpLeftX, y: point1.y};
|
|
var cp2 = {x: cpLeftX, y: point2.y};
|
|
|
|
this.contextLoops.moveTo(point1.x, point1.y);
|
|
this.contextLoops.bezierCurveTo(cp1.x, cp1.y, cp2.x, cp2.y, point2.x, point2.y);
|
|
|
|
// lower line of text box
|
|
var point3 = {x: this.rightCurveBeginX, y: point2.y};
|
|
this.contextLoops.lineTo(point3.x, point3.y);
|
|
|
|
// curve to upper right connection to the center box
|
|
var point4 = {x: this.rightCurveBeginX, y: this.rightCurvePositionY};
|
|
var cpRightX = (this.rightCurveBeginX + this.rightCurveEndX) / 2 - 30;
|
|
var cp3 = {x: cpRightX, y: point3.y};
|
|
var cp4 = {x: cpRightX, y: point4.y};
|
|
this.contextLoops.bezierCurveTo(cp3.x, cp3.y, cp4.x, cp4.y, point4.x, point4.y);
|
|
|
|
// line to lower right connection to the center box
|
|
var point5 = {x: point4.x, y: point4.y + curveHeight};
|
|
this.contextLoops.lineTo(point5.x, point5.y);
|
|
|
|
// curve to upper right connection to the text box
|
|
var point6 = {x: point5.x, y: point2.y - 25};
|
|
cpRightX -= 30;
|
|
var cp5 = {x: cpRightX, y: point5.y};
|
|
var cp6 = {x: cpRightX, y: point6.y};
|
|
this.contextLoops.bezierCurveTo(cp5.x, cp5.y, cp6.x, cp6.y, point6.x, point6.y);
|
|
|
|
// upper line of the text box
|
|
var point7 = {x: point1.x, y: point6.y};
|
|
this.contextLoops.lineTo(point7.x, point7.y);
|
|
|
|
// line to lower left connection to the center box
|
|
var point8 = {x: point1.x, y: point1.y + curveHeight};
|
|
cpLeftX += 30;
|
|
var cp7 = {x: cpLeftX, y: point7.y};
|
|
var cp8 = {x: cpLeftX, y: point8.y};
|
|
this.contextLoops.bezierCurveTo(cp7.x, cp7.y, cp8.x, cp8.y, point8.x, point8.y);
|
|
|
|
this.contextLoops.fill();
|
|
|
|
if (typeof this.contextLoops.endPath == 'function') {
|
|
this.contextLoops.endPath();
|
|
}
|
|
|
|
};
|
|
|
|
/** Clear one side for redrawing */
|
|
Piwik_Transitions_Canvas.prototype.clearSide = function (side, onlyBg) {
|
|
if (side == 'left') {
|
|
this.contextBgLeft.clearRect(0, 0, this.width, this.height);
|
|
this.contextLeft.clearRect(0, 0, this.width, this.height);
|
|
} else {
|
|
this.contextBgRight.clearRect(0, 0, this.width, this.height);
|
|
this.contextRight.clearRect(0, 0, this.width, this.height);
|
|
}
|
|
this.contextLoops.clearRect(0, 0, this.width, this.height);
|
|
|
|
if (side == 'left') {
|
|
if (!onlyBg) {
|
|
this.container.find('.Transitions_BoxTextLeft').remove();
|
|
this.container.find('.Transitions_CurveTextLeft').remove();
|
|
}
|
|
this.leftBoxPositionY = this.originalBoxPositionY;
|
|
this.leftCurvePositionY = this.originalCurvePositionY;
|
|
} else {
|
|
if (!onlyBg) {
|
|
this.container.find('.Transitions_BoxTextRight').remove();
|
|
this.container.find('.Transitions_CurveTextRight').remove();
|
|
}
|
|
this.rightBoxPositionY = this.originalBoxPositionY;
|
|
this.rightCurvePositionY = this.originalCurvePositionY;
|
|
}
|
|
};
|
|
|
|
// --------------------------------------
|
|
// MODEL
|
|
// --------------------------------------
|
|
|
|
function Piwik_Transitions_Model(ajax) {
|
|
this.ajax = ajax;
|
|
|
|
this.groupTitles = {};
|
|
}
|
|
|
|
Piwik_Transitions_Model.prototype.htmlLoaded = function () {
|
|
this.groupTitles.previousPages = Piwik_Transitions_Translations.fromPreviousPages;
|
|
this.groupTitles.previousSiteSearches = Piwik_Transitions_Translations.fromPreviousSiteSearches;
|
|
this.groupTitles.followingPages = Piwik_Transitions_Translations.toFollowingPages;
|
|
this.groupTitles.followingSiteSearches = Piwik_Transitions_Translations.toFollowingSiteSearches;
|
|
this.groupTitles.outlinks = Piwik_Transitions_Translations.outlinks;
|
|
this.groupTitles.downloads = Piwik_Transitions_Translations.downloads;
|
|
|
|
this.shareInGroupTexts = {
|
|
previousPages: Piwik_Transitions_Translations.fromPreviousPagesInline,
|
|
previousSiteSearches: Piwik_Transitions_Translations.fromPreviousSiteSearchesInline,
|
|
followingPages: Piwik_Transitions_Translations.toFollowingPagesInline,
|
|
followingSiteSearches: Piwik_Transitions_Translations.toFollowingSiteSearchesInline,
|
|
searchEngines: Piwik_Transitions_Translations.fromSearchEnginesInline,
|
|
socialNetworks: Piwik_Transitions_Translations.fromSocialNetworksInline,
|
|
websites: Piwik_Transitions_Translations.fromWebsitesInline,
|
|
campaigns: Piwik_Transitions_Translations.fromCampaignsInline,
|
|
outlinks: Piwik_Transitions_Translations.outlinksInline,
|
|
downloads: Piwik_Transitions_Translations.downloadsInline
|
|
};
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.loadData = function (actionType, actionName, overrideParams, callback) {
|
|
var self = this;
|
|
|
|
this.pageviews = 0;
|
|
this.exits = 0;
|
|
this.loops = 0;
|
|
|
|
this.directEntries = 0;
|
|
|
|
this.searchEnginesNbTransitions = 0;
|
|
this.searchEngines = [];
|
|
|
|
this.socialNetworksNbTransitions = 0;
|
|
this.socialNetworks = [];
|
|
|
|
this.websitesNbTransitions = 0;
|
|
this.websites = [];
|
|
|
|
this.campaignsNbTransitions = 0;
|
|
this.campaigns = [];
|
|
|
|
this.previousPagesNbTransitions = 0;
|
|
this.previousPages = [];
|
|
|
|
this.followingPagesNbTransitions = 0;
|
|
this.followingPages = [];
|
|
|
|
this.downloadsNbTransitions = 0;
|
|
this.downloads = [];
|
|
|
|
this.outlinksNbTransitions = 0;
|
|
this.outlinks = [];
|
|
|
|
this.previousSiteSearchesNbTransitions = 0;
|
|
this.previousSiteSearches = [];
|
|
|
|
this.followingSiteSearchesNbTransitions = 0;
|
|
this.followingSiteSearches = [];
|
|
|
|
this.date = '';
|
|
|
|
var params = {
|
|
actionType: actionType,
|
|
actionName: actionName,
|
|
expanded: 1
|
|
};
|
|
if (overrideParams) {
|
|
$.extend(params, overrideParams);
|
|
}
|
|
|
|
this.ajax.callApi('Transitions.getTransitionsForAction', params,
|
|
function (report) {
|
|
self.date = report.date;
|
|
|
|
// load page metrics
|
|
self.pageviews = report.pageMetrics.pageviews;
|
|
self.loops = report.pageMetrics.loops;
|
|
self.exits = report.pageMetrics.exits;
|
|
|
|
// load referrers: split direct entries and others
|
|
for (var i = 0; i < report.referrers.length; i++) {
|
|
var referrer = report.referrers[i];
|
|
if (referrer.shortName == 'direct') {
|
|
self.directEntries = referrer.visits;
|
|
} else if (referrer.shortName == 'search') {
|
|
self.searchEnginesNbTransitions = referrer.visits;
|
|
self.searchEngines = referrer.details;
|
|
self.groupTitles.searchEngines = referrer.label;
|
|
} else if (referrer.shortName == 'social') {
|
|
self.socialNetworksNbTransitions = referrer.visits;
|
|
self.socialNetworks = referrer.details;
|
|
self.groupTitles.socialNetworks = referrer.label;
|
|
} else if (referrer.shortName == 'website') {
|
|
self.websitesNbTransitions = referrer.visits;
|
|
self.websites = referrer.details;
|
|
self.groupTitles.websites = referrer.label;
|
|
} else if (referrer.shortName == 'campaign') {
|
|
self.campaignsNbTransitions = referrer.visits;
|
|
self.campaigns = referrer.details;
|
|
self.groupTitles.campaigns = referrer.label;
|
|
}
|
|
}
|
|
|
|
self.loadAndSumReport(report, 'previousPages');
|
|
self.loadAndSumReport(report, 'previousSiteSearches');
|
|
self.loadAndSumReport(report, 'followingPages');
|
|
self.loadAndSumReport(report, 'followingSiteSearches');
|
|
self.loadAndSumReport(report, 'downloads');
|
|
self.loadAndSumReport(report, 'outlinks');
|
|
|
|
if (typeof Piwik_Transitions_Model.totalNbPageviews == 'undefined') {
|
|
Piwik_Transitions_Model.totalNbPageviews = false;
|
|
self.ajax.loadTotalNbPageviews(function (nbPageviews) {
|
|
Piwik_Transitions_Model.totalNbPageviews = nbPageviews;
|
|
});
|
|
}
|
|
|
|
callback();
|
|
});
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.loadAndSumReport = function (apiData, reportName) {
|
|
var data = this[reportName] = apiData[reportName];
|
|
var sumVarName = reportName + 'NbTransitions';
|
|
|
|
this[sumVarName] = 0;
|
|
for (var i = 0; i < data.length; i++) {
|
|
this[sumVarName] += data[i].referrals;
|
|
}
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.getTotalNbPageviews = function () {
|
|
if (typeof Piwik_Transitions_Model.totalNbPageviews == 'undefined') {
|
|
return false;
|
|
}
|
|
return Piwik_Transitions_Model.totalNbPageviews;
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.getGroupTitle = function (groupName) {
|
|
if (typeof this.groupTitles[groupName] != 'undefined') {
|
|
return this.groupTitles[groupName];
|
|
}
|
|
return groupName;
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.getShareInGroupTooltip = function (share, groupName) {
|
|
var tip = this.shareInGroupTexts[groupName];
|
|
return sprintf(tip, share);
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.getDetailsForGroup = function (groupName) {
|
|
return this.addPercentagesToData(this[groupName]);
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.getPercentage = function (metric, formatted) {
|
|
var percentage = (this.pageviews == 0 ? 0 : this[metric] / this.pageviews);
|
|
|
|
if (formatted) {
|
|
|
|
percentage = this.roundPercentage(percentage);
|
|
return NumberFormatter.formatPercent(percentage);
|
|
}
|
|
|
|
return percentage;
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.addPercentagesToData = function (data) {
|
|
var total = 0;
|
|
|
|
for (var i = 0; i < data.length; i++) {
|
|
total += parseInt(data[i].referrals, 10);
|
|
}
|
|
|
|
for (i = 0; i < data.length; i++) {
|
|
data[i].percentage = this.roundPercentage(data[i].referrals / total);
|
|
}
|
|
|
|
return data;
|
|
};
|
|
|
|
Piwik_Transitions_Model.prototype.roundPercentage = function (value) {
|
|
if (value < .1) {
|
|
return Math.round(value * 1000) / 10.0;
|
|
} else {
|
|
return Math.round(value * 100);
|
|
}
|
|
};
|
|
|
|
// --------------------------------------
|
|
// AJAX
|
|
// --------------------------------------
|
|
|
|
function Piwik_Transitions_Ajax() {
|
|
}
|
|
|
|
Piwik_Transitions_Ajax.prototype.loadTotalNbPageviews = function (callback) {
|
|
this.callApi('Actions.get', {
|
|
columns: 'nb_pageviews'
|
|
}, function (response) {
|
|
var value = typeof response.value != 'undefined' ? response.value : false;
|
|
callback(value);
|
|
});
|
|
};
|
|
|
|
Piwik_Transitions_Ajax.prototype.callTransitionsController = function (action, callback) {
|
|
var ajaxRequest = new ajaxHelper();
|
|
ajaxRequest.addParams({
|
|
module: 'Transitions',
|
|
action: action
|
|
}, 'get');
|
|
ajaxRequest.setCallback(callback);
|
|
ajaxRequest.setFormat('html');
|
|
ajaxRequest.send();
|
|
};
|
|
|
|
Piwik_Transitions_Ajax.prototype.callApi = function (method, params, callback) {
|
|
var self = this;
|
|
|
|
params.format = 'JSON';
|
|
params.module = 'API';
|
|
params.method = method;
|
|
params.filter_limit = '-1';
|
|
|
|
var ajaxRequest = new ajaxHelper();
|
|
ajaxRequest.addParams(params, 'get');
|
|
ajaxRequest.useCallbackInCaseOfError();
|
|
ajaxRequest.setCallback(
|
|
function (result) {
|
|
if (typeof result.result != 'undefined' && result.result == 'error') {
|
|
var errorName = result.message;
|
|
var showError = function () {
|
|
var errorTitle, errorMessage, errorBack;
|
|
if (typeof Piwik_Transitions_Translations[errorName] == 'undefined') {
|
|
errorTitle = 'Exception';
|
|
errorMessage = errorName;
|
|
errorBack = '<<<';
|
|
} else {
|
|
errorTitle = Piwik_Transitions_Translations[errorName];
|
|
errorMessage = Piwik_Transitions_Translations[errorName + 'Details'];
|
|
errorBack = Piwik_Transitions_Translations[errorName + 'Back'];
|
|
}
|
|
|
|
if (typeof params.actionName != 'undefined') {
|
|
var url = params.actionName;
|
|
url = piwikHelper.addBreakpointsToUrl(url);
|
|
errorTitle = sprintf(errorTitle, '<span>' + url + '</span>');
|
|
}
|
|
|
|
errorMessage = sprintf(errorMessage, '<br />');
|
|
var inlineErrorNode = $('#Transitions_Error_Container');
|
|
if (inlineErrorNode.length) {
|
|
// viewing it as report, not popover
|
|
inlineErrorNode.html('');
|
|
var theContentNode = $(document.createElement('div')).addClass('Piwik_Popover_Error');
|
|
|
|
var p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Title');
|
|
theContentNode.append(p.html(errorTitle));
|
|
|
|
if (errorMessage) {
|
|
p = $(document.createElement('p')).addClass('Piwik_Popover_Error_Message');
|
|
theContentNode.append(p.html(errorMessage));
|
|
}
|
|
inlineErrorNode.append(theContentNode);
|
|
inlineErrorNode.show();
|
|
$('#transitions_report .popoverContainer').hide();
|
|
} else {
|
|
Piwik_Popover.showError(errorTitle, errorMessage, errorBack);
|
|
}
|
|
|
|
$('#transitions_inline_loading').hide();
|
|
};
|
|
|
|
if (typeof Piwik_Transitions_Translations == 'undefined') {
|
|
self.callApi('Transitions.getTranslations', {}, function (response) {
|
|
if (typeof response == 'object') {
|
|
Piwik_Transitions_Translations = response;
|
|
} else {
|
|
Piwik_Transitions_Translations = {};
|
|
}
|
|
showError();
|
|
});
|
|
} else {
|
|
showError();
|
|
}
|
|
}
|
|
else {
|
|
callback(result);
|
|
}
|
|
}
|
|
);
|
|
ajaxRequest.send();
|
|
};
|
|
|
|
// --------------------------------------
|
|
// STATIC UTIL FUNCTIONS
|
|
// --------------------------------------
|
|
|
|
Piwik_Transitions_Util = {
|
|
|
|
/**
|
|
* Removes protocol, www and trailing slashes from a URL.
|
|
* If removeDomain is set, the domain is removed as well.
|
|
*/
|
|
shortenUrl: function (url, removeDomain) {
|
|
if (url == 'Others') {
|
|
return url;
|
|
}
|
|
|
|
var urlBackup = url;
|
|
url = url.replace(/http(s)?:\/\/(www\.)?/, '');
|
|
|
|
if (urlBackup == url) {
|
|
return url;
|
|
}
|
|
|
|
if (removeDomain) {
|
|
url = url.replace(/[^\/]*/, '');
|
|
if (url == '/') {
|
|
url = urlBackup;
|
|
}
|
|
}
|
|
|
|
url = url.replace(/\/$/, '');
|
|
|
|
return url;
|
|
},
|
|
|
|
/**
|
|
* Replaces a %s placeholder in the HTML.
|
|
* The special feature is that it can be called multiple times, replacing the already
|
|
* replaced placeholder again. It creates a span that can be assigned a class using the
|
|
* spanClass parameter. The default class is 'Transitions_Metric'.
|
|
*/
|
|
replacePlaceholderInHtml: function (container, value, spanClass) {
|
|
var span = container.find('span');
|
|
if (!span.length) {
|
|
var html = container.html().replace(/%s/, '<span></span>');
|
|
span = container.html(html).find('span');
|
|
if (!spanClass) {
|
|
spanClass = 'Transitions_Metric';
|
|
}
|
|
span.addClass(spanClass);
|
|
}
|
|
if ($.browser.msie && parseFloat($.browser.version) < 8) {
|
|
// ie7 fix
|
|
value += ' ';
|
|
}
|
|
span.html(value);
|
|
}
|
|
|
|
};
|