/*! * 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 += '
' + Piwik_Transitions_Translations.DateRange + ' ' + self.model.date + ''; var title = '

' + piwikHelper.addBreakpointsToUrl(self.actionName) + '

'; 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, '' + percentage + ''); 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 = '' + sprintf(tooltip, data.referrals, nbTransitions) + ''; 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(''); 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, '' + url + ''); } errorMessage = sprintf(errorMessage, '
'); 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 = 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); } };