| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| function rowEvolutionGetMetricNameFromRow(tr) |
| { |
| return $(tr).find('td [data-name]').text().trim(); |
| } |
|
|
| function getOrCreateLegendFooter($dataTable) |
| { |
| var $legendFooter = $dataTable.find('.jqplot-legend-footer'); |
| if ($legendFooter.length) { |
| return $legendFooter; |
| } |
|
|
| $legendFooter = $('<div class="jqplot-legend-footer"><div class="jqplot-legend-items"></div></div>'); |
| var $dataTableFeatures = $dataTable.find('.dataTableFeatures'); |
| var $footerNavigation = $dataTableFeatures.find('.dataTableFooterNavigation'); |
|
|
| if ($footerNavigation.length) { |
| $legendFooter.insertBefore($footerNavigation); |
| } else if ($dataTableFeatures.length) { |
| $dataTableFeatures.append($legendFooter); |
| } else { |
| $dataTable.append($legendFooter); |
| } |
|
|
| return $legendFooter; |
| } |
|
|
| |
| |
| |
| |
| |
| var FOOTER_STACK_MAX_WIDTH = 321; |
|
|
| function updateLegendFooterStacking($legendFooter) |
| { |
| if (!$legendFooter || !$legendFooter.length) { |
| return; |
| } |
|
|
| |
| var width = $legendFooter[0].clientWidth; |
| if (width > 0) { |
| $legendFooter.toggleClass('is-narrow', width < FOOTER_STACK_MAX_WIDTH); |
| } |
| } |
|
|
| var MAX_FOOTER_LEGEND_ROWS = 2; |
| var FOOTER_LEGEND_ROW_TOLERANCE = 1; |
| var FOOTER_LEGEND_EXPORT_GRAPH_GAP = 12; |
|
|
| function resetLegendItems($legendItems) |
| { |
| $legendItems |
| .removeClass('jqplot-legend-item-hidden jqplot-legend-item-overflow') |
| .each(function () { |
| var $label = $(this).find('.jqplot-legend-label'); |
| var originalLabel = $label.attr('data-original-label'); |
|
|
| if (typeof originalLabel !== 'undefined') { |
| $label.text(originalLabel); |
| } |
| }); |
| } |
|
|
| function getLegendRows($legendItems) |
| { |
| var rowTops = []; |
| var rows = []; |
|
|
| $legendItems.each(function () { |
| var $item = $(this); |
| var itemTop; |
| var rowIndex = -1; |
|
|
| if ($item.hasClass('jqplot-legend-item-hidden')) { |
| return; |
| } |
|
|
| itemTop = Math.round($item.position().top); |
|
|
| for (var i = 0; i < rowTops.length; i++) { |
| if (Math.abs(rowTops[i] - itemTop) <= FOOTER_LEGEND_ROW_TOLERANCE) { |
| rowIndex = i; |
| break; |
| } |
| } |
|
|
| if (rowIndex === -1) { |
| rowTops.push(itemTop); |
| rows.push([]); |
| rowIndex = rows.length - 1; |
| } |
|
|
| rows[rowIndex].push(this); |
| }); |
|
|
| return rows; |
| } |
|
|
| function limitLegendRows($legendContainer, maxRows) |
| { |
| var $legendItems = $legendContainer.children('.jqplot-legend-item'); |
| var rows; |
| var overflowSource; |
| var hideItems = false; |
|
|
| resetLegendItems($legendItems); |
|
|
| if (!maxRows || maxRows < 1) { |
| return; |
| } |
|
|
| rows = getLegendRows($legendItems); |
|
|
| |
| $legendContainer.closest('.jqplot-legend-footer') |
| .toggleClass('is-multi-row', rows.length >= 2); |
|
|
| if (rows.length <= maxRows) { |
| return; |
| } |
|
|
| |
| |
| overflowSource = rows[maxRows - 1] && rows[maxRows - 1][rows[maxRows - 1].length - 1]; |
|
|
| if (!overflowSource) { |
| return; |
| } |
|
|
| $(overflowSource) |
| .addClass('jqplot-legend-item-overflow') |
| .find('.jqplot-legend-label') |
| .text('…'); |
|
|
| $legendItems.each(function () { |
| if (this === overflowSource) { |
| hideItems = true; |
| return; |
| } |
|
|
| if (hideItems) { |
| $(this).addClass('jqplot-legend-item-hidden'); |
| } |
| }); |
| } |
|
|
| function getLegendLabelTextForExport(ctx, labelElement, originalLabel, maxWidth) |
| { |
| var labelText = labelElement.textContent || ''; |
|
|
| if (!originalLabel || maxWidth <= 0 || labelElement.scrollWidth <= labelElement.clientWidth) { |
| return labelText; |
| } |
|
|
| var ellipsis = '…'; |
| var ellipsisWidth = ctx.measureText(ellipsis).width; |
| if (ellipsisWidth >= maxWidth) { |
| return ellipsis; |
| } |
|
|
| var low = 0; |
| var high = originalLabel.length; |
| var bestFit = ''; |
|
|
| while (low <= high) { |
| var middle = Math.floor((low + high) / 2); |
| var candidate = originalLabel.slice(0, middle) + ellipsis; |
|
|
| if (ctx.measureText(candidate).width <= maxWidth) { |
| bestFit = candidate; |
| low = middle + 1; |
| } else { |
| high = middle - 1; |
| } |
| } |
|
|
| return bestFit || ellipsis; |
| } |
|
|
| |
| |
| |
| |
| |
| function applyFooterLegendRowLimit($dataTable) |
| { |
| var $legendContainer = $dataTable.find('.jqplot-legend-footer.has-legend .jqplot-legend-items'); |
| if ($legendContainer.length) { |
| limitLegendRows($legendContainer, MAX_FOOTER_LEGEND_ROWS); |
| } |
| } |
|
|
| (function ($, require) { |
| var exports = require('piwik/UI'), |
| DataTable = exports.DataTable, |
| dataTablePrototype = DataTable.prototype, |
| getLabelFontFamily = function () { |
| if (!window.piwik.jqplotLabelFont) { |
| window.piwik.jqplotLabelFont = $('<p/>').hide().appendTo('body').css('font-family'); |
| } |
|
|
| return window.piwik.jqplotLabelFont || 'Arial'; |
| } |
| ; |
|
|
| exports.getLabelFontFamily = getLabelFontFamily; |
|
|
| function getPlotLinesSeriesColorNames() { |
| var seriesColorNames = []; |
|
|
| for (var seriesIndex = 0; seriesIndex < 8; seriesIndex++) { |
| seriesColorNames.push('series' + seriesIndex); |
| } |
|
|
| for (var shade = 1; shade <= 3; shade++) { |
| for (var shadedSeriesIndex = 0; shadedSeriesIndex < 8; shadedSeriesIndex++) { |
| seriesColorNames.push('series' + shadedSeriesIndex + '-shade' + shade); |
| } |
| } |
|
|
| return seriesColorNames; |
| } |
|
|
| |
| |
| |
| |
| |
| exports.JqplotGraphDataTable = function (element) { |
| DataTable.call(this, element); |
| }; |
|
|
| $.extend(exports.JqplotGraphDataTable.prototype, dataTablePrototype, { |
|
|
| |
| |
| |
| init: function () { |
| dataTablePrototype.init.call(this); |
|
|
| var graphElement = $('.piwik-graph', this.$element); |
| if (!graphElement.length) { |
| return; |
| } |
|
|
| this._lang = { |
| noData: _pk_translate('General_NoDataForGraph'), |
| exportTitle: _pk_translate('General_ExportAsImage'), |
| exportText: _pk_translate('General_SaveImageOnYourComputer'), |
| metricsToPlot: _pk_translate('General_MetricsToPlot'), |
| metricToPlot: _pk_translate('General_MetricToPlot'), |
| recordsToPlot: _pk_translate('General_RecordsToPlot'), |
| incompletePeriod: _pk_translate('General_IncompletePeriod'), |
| invalidatedPeriod: _pk_translate('General_InvalidatedPeriod') |
| }; |
|
|
| |
| this.targetDivId = this.workingDivId + 'Chart'; |
| graphElement.attr('id', this.targetDivId); |
|
|
| try { |
| var graphData = JSON.parse(graphElement.attr('data-data')); |
| } catch (e) { |
| console.error('JSON.parse Error: "' + e + "\" in:\n" + graphElement.attr('data-data')); |
| return; |
| } |
|
|
| this.data = graphData.data; |
| this._setJqplotParameters(graphData.params); |
| this._setDataStates(graphData.dataStates); |
|
|
| if (this.props.display_percentage_in_tooltip) { |
| this._setTooltipPercentages(); |
| } |
|
|
| this._bindEvents(); |
|
|
| |
| if (this.props.external_series_toggle) { |
| this.addExternalSeriesToggle( |
| window[this.props.external_series_toggle], |
| this.props.external_series_toggle_show_all == 1 |
| ); |
| } |
|
|
| |
| |
| var self = this; |
| setTimeout(function () { self.render(); }, 1); |
| }, |
|
|
| _setDataStates: function (dataStates) { |
| this.jqplotParams.dataStates = []; |
|
|
| if (Array.isArray(dataStates)) { |
| this.jqplotParams.dataStates = dataStates; |
| } |
| }, |
|
|
| _setJqplotParameters: function (params) { |
| defaultParams = { |
| grid: { |
| borderWidth: 0, |
| shadow: false |
| }, |
| title: { |
| show: false |
| }, |
| axesDefaults: { |
| pad: 1.0, |
| tickRenderer: $.jqplot.CanvasAxisTickRenderer, |
| tickOptions: { |
| showMark: false, |
| fontSize: '11px', |
| fontFamily: getLabelFontFamily() |
| }, |
| rendererOptions: { |
| drawBaseline: false |
| } |
| }, |
| axes: { |
| yaxis: { |
| tickOptions: { |
| formatString: '%s', |
| formatter: $.jqplot.NumberFormatter |
| } |
| }, |
| } |
| }; |
|
|
| this.jqplotParams = $.extend(true, {}, defaultParams, params); |
|
|
| for (var i = 2; typeof this.jqplotParams.axes['y' + i + 'axis'] != 'undefined'; i++) { |
| this.jqplotParams.axes['y' + i + 'axis'].tickOptions = $.extend(true, {}, { |
| formatString: '%s', |
| formatter: $.jqplot.NumberFormatter |
| }, this.jqplotParams.axes['y' + i + 'axis'].tickOptions); |
| } |
|
|
| this._setColors(); |
| }, |
|
|
| _setTooltipPercentages: function () { |
| this.tooltip = {percentages: []}; |
|
|
| for (var seriesIdx = 0; seriesIdx != this.data.length; ++seriesIdx) { |
| var series = this.data[seriesIdx]; |
| var sum = 0; |
|
|
| $.each(series, function(index, value) { |
| if ($.isArray(value) && value[1]) { |
| sum = sum + value[1]; |
| } else if (!$.isArray(value)) { |
| sum = sum + value; |
| } |
| }); |
|
|
| var percentages = this.tooltip.percentages[seriesIdx] = []; |
| for (var valueIdx = 0; valueIdx != series.length; ++valueIdx) { |
| var value = series[valueIdx]; |
| if ($.isArray(value) && value[1]) { |
| value = value[1]; |
| } |
|
|
| percentages[valueIdx] = sum > 0 ? Math.round(100 * value / sum) : 0; |
| } |
| } |
| }, |
|
|
| _bindEvents: function () { |
| var self = this; |
| var target = $('#' + this.targetDivId); |
|
|
| |
| target.on('jqplotDataHighlight', function (e, seriesIndex, valueIndex) { |
| self._showDataPointTooltip(this, seriesIndex, valueIndex); |
| }) |
| .on('jqplotDataUnhighlight', function () { |
| self._destroyDataPointTooltip($(this)); |
| }); |
|
|
| |
| this._plotWidth = target.innerWidth(); |
| target.on('resizeGraph', function () { |
| self._resizeGraph(); |
| }); |
|
|
| |
| target.on('piwikExportAsImage', function () { |
| self.exportAsImage(target, self._lang); |
| }); |
|
|
| |
| target.on('piwikDestroyPlot', function () { |
| if (self._resizeListener) { |
| $(window).off('resize', self._resizeListener); |
| } |
| self._plot.destroy(); |
| for (var i = 0; i < $.jqplot.visiblePlots.length; i++) { |
| if ($.jqplot.visiblePlots[i] === self) { |
| $.jqplot.visiblePlots[i] = null; |
| } |
| } |
| }); |
|
|
| this.$element.closest('.widgetContent').on('widget:resize', function () { |
| self._resizeGraph(); |
| }); |
|
|
| this._themeModeChangeListener = function () { |
| self.refreshTheme(); |
| }; |
| window.addEventListener('themeModeChange', this._themeModeChangeListener); |
| }, |
|
|
| _resizeGraph: function () { |
| var width = $('#' + this.targetDivId).innerWidth(); |
| if (width > 0 && Math.abs(this._plotWidth - width) >= 5) { |
| this._plotWidth = width; |
| this.render(); |
| } |
| }, |
|
|
| _setWindowResizeListener: function () { |
| var self = this; |
|
|
| var timeout = false; |
| this._resizeListener = function () { |
| if (timeout) { |
| window.clearTimeout(timeout); |
| } |
|
|
| timeout = window.setTimeout(function () { $('#' + self.targetDivId).trigger('resizeGraph'); }, 300); |
| }; |
| $(window).on('resize', this._resizeListener); |
| }, |
|
|
| _destroyDataPointTooltip: function ($element) { |
| if ($element.is( ":data('ui-tooltip')" )) { |
| $element.tooltip('destroy'); |
| } |
| }, |
|
|
| _showDataPointTooltip: function (element, seriesIndex, valueIndex) { |
| |
| }, |
|
|
| changeSeries: function (columns, rows) { |
| this.showLoading(); |
|
|
| columns = columns || []; |
| if (typeof columns == 'string') { |
| columns = columns.split(','); |
| } |
|
|
| rows = rows || []; |
| if (typeof rows == 'string') { |
| rows = rows.split(','); |
| } |
|
|
| var dataTable = $('#' + this.workingDivId).data('uiControlObject'); |
| dataTable.param.columns = columns.join(','); |
| dataTable.param.rows = rows.join(','); |
| delete dataTable.param.filter_limit; |
| delete dataTable.param.totalRows; |
| if (dataTable.param.filter_sort_column != 'label') { |
| dataTable.param.filter_sort_column = columns[0]; |
| } |
| dataTable.param.disable_generic_filters = '0'; |
| dataTable.reloadAjaxDataTable(false); |
| }, |
|
|
| destroyPlot: function () { |
| var target = $('#' + this.targetDivId); |
| var dataTable = target.closest('.dataTable'); |
| var legendFooter = dataTable.find('.jqplot-legend-footer'); |
|
|
| |
| if (this._plot && this._plot.plugins && this._plot.plugins.seriesPicker) { |
| this._plot.plugins.seriesPicker.destroy(); |
| } |
|
|
| target.trigger('piwikDestroyPlot'); |
| if (legendFooter.length) { |
| legendFooter |
| .removeClass('has-legend') |
| .find('.jqplot-legend-items') |
| .empty(); |
| } |
| if (target.data('oldHeight') > 0) { |
| |
| target.height(target.data('oldHeight')); |
| target.data('oldHeight', 0); |
| target.innerHTML = ''; |
| } |
| }, |
|
|
| _destroy: function () { |
| if (this._themeModeChangeListener) { |
| window.removeEventListener('themeModeChange', this._themeModeChangeListener); |
| } |
|
|
| if (this._plot) { |
| this.destroyPlot(); |
| } |
|
|
| dataTablePrototype._destroy.call(this); |
| }, |
|
|
| showLoading: function () { |
| var target = $('#' + this.targetDivId); |
|
|
| var loading = $(document.createElement('div')).addClass('jqplot-loading'); |
| loading.append('<span class="matomo-loader"><span></span><span></span><span></span></span>'); |
| loading.css({ |
| width: target.innerWidth() + 'px', |
| height: target.innerHeight() + 'px', |
| backgroundColor: (this.jqplotParams.grid && this.jqplotParams.grid.background) || '', |
| opacity: 0 |
| }); |
| target.prepend(loading); |
| loading.css({opacity: .7}); |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| _checkTicksWidth: function($targetDiv){ |
| if(typeof this.jqplotParams.axes.xaxis.ticksOriginal === 'undefined' || this.jqplotParams.axes.xaxis.ticksOriginal === {}){ |
| this.jqplotParams.axes.xaxis.ticksOriginal = this.jqplotParams.axes.xaxis.ticks.slice(); |
| } |
|
|
| var ticks = this.jqplotParams.axes.xaxis.ticks = this.jqplotParams.axes.xaxis.ticksOriginal.slice(); |
|
|
| var divWidth = $targetDiv.width(); |
| var tickOptions = $.extend(true, {}, this.jqplotParams.axesDefaults.tickOptions, this.jqplotParams.axes.xaxis.tickOptions); |
| var gutter = tickOptions.gutter || 5; |
| var sumWidthOfTicks = Number.MAX_VALUE; |
| var $labelTestChamber = {}; |
| var tick = ""; |
| var $body = $("body"); |
| var maxRunsFailsafe = 20; |
| var ticksCount = 0; |
| var key = 0; |
|
|
| while(sumWidthOfTicks > divWidth && maxRunsFailsafe > 0) { |
| sumWidthOfTicks = 0; |
| for (key = 0; key < ticks.length; key++) { |
| tick = ticks[key]; |
| if (tick !== " " && tick !== "") { |
| $labelTestChamber = $("<span/>", { |
| style: 'font-size: ' + (tickOptions.fontSize || '11px') + '; font-family: ' + (tickOptions.fontFamily || 'Arial, Helvetica, sans-serif') + ';' + (tickOptions.fontWeight || 'normal') + ';' + 'clear: both; float: none;', |
| text: tick |
| }).appendTo($body); |
| sumWidthOfTicks += ($labelTestChamber.width() + gutter*2); |
| $labelTestChamber.remove(); |
| } |
| } |
|
|
| ticksCount = 0; |
| if (sumWidthOfTicks > divWidth) { |
| for (key = 0; key < ticks.length; key++) { |
| tick = ticks[key]; |
| if (tick !== " " && tick !== "") { |
| if (ticksCount % 2 == 1) { |
| ticks[key] = " "; |
| } |
| ticksCount++; |
| } |
| } |
| } |
| maxRunsFailsafe--; |
| } |
| }, |
|
|
| |
| render: function () { |
| if (this.data.length == 0) { |
| return; |
| } |
|
|
| var targetDivId = this.workingDivId + 'Chart'; |
| var lang = this._lang; |
| var dataTableDiv = $('#' + this.workingDivId); |
|
|
| |
| var target = $('#' + targetDivId); |
| if (target.find('canvas').length > 0) { |
| this.destroyPlot(); |
| } |
|
|
| |
| |
| |
| |
| var self = this; |
|
|
| |
| |
| if( this.param.viewDataTable === "graphBar" |
| || this.param.viewDataTable === "graphVerticalBar" |
| || this.param.viewDataTable === "graphEvolution" ) { |
| self._checkTicksWidth(target); |
| } |
|
|
| |
| try { |
| var plot = self._plot = $.jqplot(targetDivId, this.data, this.jqplotParams); |
| } catch (e) { |
| |
| if (e != "No plot target specified") { |
| throw e; |
| } |
| } |
|
|
| self._setWindowResizeListener(); |
|
|
| var self = this; |
|
|
| |
| if (typeof $.jqplot.visiblePlots == 'undefined') { |
| $.jqplot.visiblePlots = []; |
| window.CoreHome.Matomo.on('matomoPageChange', function () { |
| for (var i = 0; i < $.jqplot.visiblePlots.length; i++) { |
| if ($.jqplot.visiblePlots[i] == null) { |
| continue; |
| } |
| $.jqplot.visiblePlots[i].destroyPlot(); |
| } |
| $.jqplot.visiblePlots = []; |
| }); |
| } |
|
|
| if (typeof plot != 'undefined') { |
| $.jqplot.visiblePlots.push(self); |
| } |
| }, |
|
|
| |
| exportAsImage: function (container, lang) { |
| var pixelRatio = window.devicePixelRatio || 1; |
| var dataTable = container.closest('.dataTable'); |
| var legendFooter = dataTable.find('.jqplot-legend-footer.has-legend'); |
| var hasFooterLegend = legendFooter.length > 0; |
| var legendHeight = hasFooterLegend ? legendFooter[0].getBoundingClientRect().height : 0; |
| var legendGraphGap = hasFooterLegend ? FOOTER_LEGEND_EXPORT_GRAPH_GAP : 0; |
| var exportCanvas = document.createElement('canvas'); |
| var exportWidth = container.outerWidth(); |
| var dataTableWidth = dataTable.innerWidth(); |
| if (dataTableWidth) { |
| exportWidth = Math.max(exportWidth, dataTableWidth); |
| } |
| exportCanvas.width = Math.round(exportWidth * pixelRatio); |
| exportCanvas.height = Math.round((container.height() + legendGraphGap + legendHeight) * pixelRatio); |
|
|
| if (!exportCanvas.getContext) { |
| alert("Sorry, not supported in your browser. Please upgrade your browser :)"); |
| return; |
| } |
| var exportCtx = exportCanvas.getContext('2d'); |
| exportCtx.fillStyle = (this.jqplotParams.grid && this.jqplotParams.grid.background) || '#ffffff'; |
| exportCtx.fillRect(0, 0, exportCanvas.width, exportCanvas.height); |
|
|
| var canvases = container.find('canvas'); |
|
|
| for (var i = 0; i < canvases.length; i++) { |
| var canvas = canvases.eq(i); |
| var position = canvas.position(); |
| var parent = canvas.parent(); |
| if (parent.hasClass('jqplot-axis')) { |
| var addPosition = parent.position(); |
| position.left += addPosition.left; |
| position.top += addPosition.top + parseInt(parent.css('marginTop'), 10); |
| } |
| exportCtx.drawImage(canvas[0], Math.round(position.left * pixelRatio), Math.round(position.top * pixelRatio)); |
| } |
|
|
| if (hasFooterLegend) { |
| this.drawLegendForExport( |
| exportCtx, |
| legendFooter, |
| exportWidth, |
| container.height() + legendGraphGap, |
| pixelRatio |
| ); |
| } |
|
|
| var exported = exportCanvas.toDataURL("image/png"); |
|
|
| var img = document.createElement('img'); |
| img.src = exported; |
|
|
| img = $(img).css({ |
| width: Math.round(exportCanvas.width / pixelRatio) + 'px', |
| height: Math.round(exportCanvas.height / pixelRatio) + 'px' |
| }); |
|
|
| var popover = $(document.createElement('div')); |
|
|
| popover.append('<div style="font-size: 13px; margin-bottom: 10px;">' |
| + lang.exportText + '</div>').append($(img)); |
|
|
| popover.dialog({ |
| title: lang.exportTitle, |
| modal: true, |
| width: 'auto', |
| resizable: false, |
| autoOpen: true, |
| open: function (event, ui) { |
| $('.ui-widget-overlay').on('click.popover', function () { |
| popover.dialog('close'); |
| }); |
| }, |
| close: function (event, ui) { |
| $(this).dialog("destroy").remove(); |
| } |
| }); |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| drawLegendForExport: function (ctx, legendFooter, exportWidth, topOffset, pixelRatio) { |
| var footerRect = legendFooter[0].getBoundingClientRect(); |
| var $items = legendFooter.find('.jqplot-legend-item').filter(function () { |
| var $item = $(this); |
| return !$item.hasClass('jqplot-legend-item-hidden') && $item.css('display') !== 'none'; |
| }); |
|
|
| if (!$items.length) { |
| return; |
| } |
|
|
| |
| |
| |
| |
| var contentLeft = Infinity; |
| var contentRight = -Infinity; |
| $items.each(function () { |
| var rect = this.getBoundingClientRect(); |
| contentLeft = Math.min(contentLeft, rect.left); |
| contentRight = Math.max(contentRight, rect.right); |
| }); |
| var offsetX = Math.max(0, (exportWidth - (contentRight - contentLeft)) / 2); |
|
|
| $items.each(function () { |
| var $item = $(this); |
|
|
| var $swatch = $item.find('.jqplot-legend-swatch'); |
| var $label = $item.find('.jqplot-legend-label'); |
| var labelElement = $label[0]; |
| var originalLabel = $label.attr('data-original-label') || $label.text(); |
| var labelText = $label.text(); |
| if (!$swatch.length || !$label.length || !labelText) { |
| return; |
| } |
|
|
| |
| var swatchRect = $swatch[0].getBoundingClientRect(); |
| ctx.save(); |
| ctx.fillStyle = $swatch.css('background-color'); |
| ctx.beginPath(); |
| ctx.arc( |
| (offsetX + (swatchRect.left - contentLeft) + swatchRect.width / 2) * pixelRatio, |
| (topOffset + (swatchRect.top - footerRect.top) + swatchRect.height / 2) * pixelRatio, |
| (Math.min(swatchRect.width, swatchRect.height) / 2) * pixelRatio, |
| 0, |
| Math.PI * 2 |
| ); |
| ctx.fill(); |
| ctx.restore(); |
|
|
| |
| |
| var labelRect = $label[0].getBoundingClientRect(); |
| var labelLeft = offsetX + (labelRect.left - contentLeft); |
| var labelTop = topOffset + (labelRect.top - footerRect.top); |
| var fontSize = parseFloat($label.css('font-size')) || 12; |
|
|
| ctx.save(); |
| ctx.beginPath(); |
| ctx.rect( |
| labelLeft * pixelRatio, |
| labelTop * pixelRatio, |
| labelRect.width * pixelRatio, |
| labelRect.height * pixelRatio |
| ); |
| ctx.clip(); |
| ctx.font = ($label.css('font-weight') || '400') + ' ' |
| + Math.round(fontSize * pixelRatio) + 'px ' |
| + ($label.css('font-family') || require('piwik/UI').getLabelFontFamily()); |
| ctx.fillStyle = $label.css('color') || '#666666'; |
| ctx.textBaseline = 'middle'; |
| labelText = getLegendLabelTextForExport( |
| ctx, |
| labelElement, |
| originalLabel, |
| labelRect.width * pixelRatio |
| ); |
| ctx.fillText( |
| labelText, |
| labelLeft * pixelRatio, |
| (labelTop + labelRect.height / 2) * pixelRatio |
| ); |
| ctx.restore(); |
| }); |
| }, |
|
|
| |
| |
| |
|
|
| |
| setYTicks: function () { |
| |
| this.setYTicksForAxis('yaxis', this.jqplotParams.axes.yaxis); |
|
|
| |
| for (var i = 2; typeof this.jqplotParams.axes['y' + i + 'axis'] != 'undefined'; i++) { |
| this.setYTicksForAxis('y' + i + 'axis', this.jqplotParams.axes['y' + i + 'axis']); |
| } |
| }, |
|
|
| setYTicksForAxis: function (axisName, axis) { |
| |
| var maxCrossDataSets = 0; |
| for (var i = 0; i < this.data.length; i++) { |
| if (this.jqplotParams.series[i].yaxis == axisName) { |
| var maxValue = Math.max.apply(Math, this.data[i]); |
| if (maxValue > maxCrossDataSets) { |
| maxCrossDataSets = maxValue; |
| } |
| maxCrossDataSets = parseFloat(maxCrossDataSets); |
| } |
| } |
|
|
| |
| maxCrossDataSets += Math.max(1, Math.round(maxCrossDataSets * .03)); |
|
|
| |
| if (maxCrossDataSets > 15) { |
| maxCrossDataSets = maxCrossDataSets + 10 - maxCrossDataSets % 10; |
| } |
|
|
| if (maxCrossDataSets == 0) { |
| maxCrossDataSets = 1; |
| } |
|
|
| |
| if ( |
| axis.tickOptions |
| && axis.tickOptions.formatString |
| && axis.tickOptions.formatString.endsWith('%') |
| && maxCrossDataSets > 100 |
| ) { |
| maxCrossDataSets = 100; |
| } |
|
|
| |
| var ticks = []; |
| var numberOfTicks = 2; |
| var tickDistance = Math.ceil(maxCrossDataSets / numberOfTicks); |
| for (var i = 0; i <= numberOfTicks; i++) { |
| ticks.push(i * tickDistance); |
| } |
| axis.ticks = ticks; |
| }, |
|
|
| |
| formatY: function (value, seriesIndex) { |
| var floatVal = parseFloat(value); |
| var intVal = parseInt(value, 10); |
| if (Math.abs(floatVal - intVal) >= 0.005) { |
| value = Math.round(floatVal * 100) / 100; |
| } else if (parseFloat(intVal) == floatVal) { |
| value = intVal; |
| } else { |
| value = floatVal; |
| } |
|
|
| var axisId = this.jqplotParams.series[seriesIndex].yaxis; |
| var formatString = this.jqplotParams.axes[axisId].tickOptions.formatString; |
|
|
| return $.jqplot.NumberFormatter(formatString, value); |
| }, |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| addExternalSeriesToggle: function (seriesPickerClass, initiallyShowAll) { |
| new seriesPickerClass(this.targetDivId, this, initiallyShowAll); |
|
|
| if (!initiallyShowAll) { |
|
|
| var initialMetrics = 0; |
| var $rowEvolution = $('#'+this.targetDivId).closest('.rowevolution'); |
|
|
| var newData = []; |
| var newSeries = []; |
| if ($rowEvolution.data('initialMetrics')) { |
| initialMetrics = $rowEvolution.data('initialMetrics'); |
|
|
| if (Array.isArray(initialMetrics)) { |
| for (var j = 0; j < initialMetrics.length; j++) { |
| |
| for (var k = 0; k < this.jqplotParams.series.length; k++) { |
| if (this.jqplotParams.series[k] |
| && this.jqplotParams.series[k].label |
| && this.jqplotParams.series[k].label === initialMetrics[j]) { |
|
|
| newData.push(this.data[k]); |
| newSeries.push(this.jqplotParams.series[k]); |
| break; |
| } |
| } |
| } |
| } |
| } |
|
|
| if (newData.length) { |
| |
| this.data = newData; |
| this.jqplotParams.series = newSeries; |
| } else { |
| |
| this.data = [this.data[0]]; |
| this.jqplotParams.series = [this.jqplotParams.series[0]]; |
| } |
|
|
| this.setYTicks(); |
| } |
| }, |
|
|
| |
| |
| |
| _setColors: function () { |
| var colorManager = piwik.ColorManager; |
|
|
| var viewDataTable = $('#' + this.workingDivId).data('uiControlObject').param['viewDataTable']; |
|
|
| var graphType = ''; |
| if (viewDataTable == 'graphEvolution' || viewDataTable == 'graphStackedBarEvolution') { |
| graphType = 'evolution'; |
| } else if (viewDataTable == 'graphPie') { |
| graphType = 'pie'; |
| } else if (viewDataTable == 'graphVerticalBar') { |
| graphType = 'bar'; |
| } |
|
|
| var namespace = graphType + '-graph-colors'; |
|
|
| this._setSeriesColors(namespace); |
|
|
| this.jqplotParams.grid.background = colorManager.getColor(namespace, 'grid-background'); |
| this.jqplotParams.grid.borderColor = colorManager.getColor(namespace, 'grid-border'); |
| this.tickColor = colorManager.getColor(namespace, 'ticks'); |
|
|
| |
| if (graphType === 'evolution' || graphType === 'bar') { |
| var TICK_OPACITY = 0.5; |
| var tickRgb = colorManager.getRgb(this.tickColor); |
| this.tickColor = 'rgba(' + tickRgb[0] + ', ' + tickRgb[1] + ', ' |
| + tickRgb[2] + ', ' + TICK_OPACITY + ')'; |
|
|
| |
| this.jqplotParams.grid.gridLineColor = this.tickColor; |
| } |
|
|
| this.singleMetricColor = colorManager.getColor(namespace, 'single-metric-label'); |
|
|
| if (this.jqplotParams.pieLegend) { |
| this.jqplotParams.pieLegend.labelColor = this.singleMetricColor; |
| } |
|
|
| if (this.jqplotParams.canvasLegend |
| && this.jqplotParams.canvasLegend.singleMetric |
| ) { |
| this.jqplotParams.canvasLegend.singleMetricColor = this.singleMetricColor; |
| } |
| }, |
|
|
| _setSeriesColors: function (namespace) { |
| var colorManager = piwik.ColorManager, |
| seriesColorNames; |
|
|
| var comparisonService = window.CoreHome.ComparisonsStoreInstance; |
| if (comparisonService.isComparing() && typeof this.jqplotParams.series[0].seriesIndex !== 'undefined') { |
| |
| namespace = 'comparison-series-color'; |
|
|
| seriesColorNames = []; |
| this.jqplotParams.series.forEach(function (s) { |
| var seriesColorName = comparisonService.getSeriesColorName(s.seriesIndex, s.metricIndex); |
| seriesColorNames.push(seriesColorName); |
| }); |
| } else if (namespace === 'evolution-graph-colors' |
| || namespace === 'bar-graph-colors' |
| || namespace === 'pie-graph-colors') { |
| seriesColorNames = getPlotLinesSeriesColorNames(); |
| } else { |
| seriesColorNames = ['series0', 'series1', 'series2', 'series3', 'series4', 'series5', |
| 'series6', 'series7', 'series8', 'series9', 'series10']; |
| } |
|
|
| this.jqplotParams.seriesColors = colorManager.getColors(namespace, seriesColorNames, true); |
| }, |
|
|
| refreshTheme: function () { |
| if (!this.data || !this.data.length || !this.$element |
| || !$.contains(document.documentElement, this.$element[0])) { |
| return; |
| } |
| this._setColors(); |
| this.render(); |
| } |
| }); |
|
|
| DataTable.registerFooterIconHandler('graphPie', DataTable.switchToGraph); |
| DataTable.registerFooterIconHandler('graphVerticalBar', DataTable.switchToGraph); |
| DataTable.registerFooterIconHandler('graphEvolution', DataTable.switchToGraph); |
|
|
| })(jQuery, require); |
|
|
| |
| |
| |
| |
|
|
| function JQPlotExternalSeriesToggle(targetDivId, jqplotObject, initiallyShowAll) { |
| this.init(targetDivId, originalConfig, initiallyShowAll); |
| } |
|
|
| JQPlotExternalSeriesToggle.prototype = { |
|
|
| init: function (targetDivId, jqplotObject, initiallyShowAll) { |
| this.targetDivId = targetDivId; |
| this.jqplotObject = jqplotObject; |
| this.originalData = jqplotObject.data; |
| this.originalSeries = jqplotObject.jqplotParams.series; |
| this.originalAxes = jqplotObject.jqplotParams.axes; |
| this.originalParams = jqplotObject.jqplotParams; |
| this.originalSeriesColors = jqplotObject.jqplotParams.seriesColors; |
| this.initiallyShowAll = initiallyShowAll; |
|
|
| this.activated = []; |
| this.target = $('#' + targetDivId); |
|
|
| this.attachEvents(); |
| }, |
|
|
| |
| attachEvents: function () {}, |
|
|
| |
| showSeries: function (i) { |
| this.activated = [i]; |
| this.replot(); |
| }, |
|
|
| |
| toggleSeries: function (i) { |
| if (this.activated.indexOf(i) > -1) { |
| |
| if (this.activated.length > 1) { |
| |
| this.activated.splice(this.activated.indexOf(i), 1); |
| } |
| } else { |
| this.activated.push(i); |
| } |
| this.replot(); |
| }, |
|
|
| replot: function () { |
| this.beforeReplot(); |
|
|
| |
| var usedAxes = []; |
| var config = {data: this.originalData, params: this.originalParams}; |
| config.data = []; |
| config.params.series = []; |
| config.params.axes = {xaxis: this.originalAxes.xaxis}; |
| config.params.seriesColors = []; |
|
|
| for (var j = 0; j < this.activated.length; j++) { |
| |
| for (var k = 0; k < this.originalSeries.length; k++) { |
| if (this.originalSeries[k] |
| && this.originalSeries[k].label |
| && ( |
| this.originalSeries[k].label === this.activated[j] |
| || piwikHelper.htmlDecode(this.originalSeries[k].label) === this.activated[j] |
| ) |
| ) { |
| config.data.push(this.originalData[k]); |
| config.params.seriesColors.push(this.originalSeriesColors[k]); |
| config.params.series.push($.extend(true, {}, this.originalSeries[k])); |
| |
| var axis = this.originalSeries[k].yaxis; |
| if ($.inArray(axis, usedAxes) == -1) { |
| usedAxes.push(axis); |
| } |
| break; |
| } |
| } |
| } |
|
|
| |
| var replaceAxes = {}; |
| for (j = 0; j < usedAxes.length; j++) { |
| var originalAxisName = usedAxes[j]; |
| var newAxisName = (j == 0 ? 'yaxis' : 'y' + (j + 1) + 'axis'); |
| replaceAxes[originalAxisName] = newAxisName; |
| config.params.axes[newAxisName] = this.originalAxes[originalAxisName]; |
| } |
|
|
| |
| for (j = 0; j < config.params.series.length; j++) { |
| var series = config.params.series[j]; |
| series.yaxis = replaceAxes[series.yaxis]; |
| } |
|
|
| this.jqplotObject.data = config.data; |
| this.jqplotObject.jqplotParams = config.params; |
| this.jqplotObject.setYTicks(); |
| this.jqplotObject.render(); |
| }, |
|
|
| |
| beforeReplot: function () {} |
|
|
| }; |
|
|
| |
|
|
| function RowEvolutionSeriesToggle(targetDivId, jqplotData, initiallyShowAll) { |
| this.init(targetDivId, jqplotData, initiallyShowAll); |
| } |
|
|
| RowEvolutionSeriesToggle.prototype = JQPlotExternalSeriesToggle.prototype; |
|
|
| RowEvolutionSeriesToggle.prototype.attachEvents = function () { |
| var self = this; |
|
|
| var $rowEvolution = this.target.closest('.rowevolution'); |
| this.seriesPickers = $rowEvolution.find('table.metrics tr'); |
|
|
| var initialMetrics = []; |
|
|
| if ($rowEvolution.data('initialMetrics')) { |
| initialMetrics = []; |
| var savedMetrics = $rowEvolution.data('initialMetrics'); |
| var existingMetricsInSeries = []; |
| var m = 0; |
| for (m = 0; m < this.originalSeries.length; m++) { |
| existingMetricsInSeries.push(this.originalSeries[m].label); |
| } |
| for (m = 0; m < savedMetrics.length; m++) { |
| if (existingMetricsInSeries.indexOf(savedMetrics[m]) > -1) { |
| |
| |
| initialMetrics.push(savedMetrics[m]); |
| } |
| } |
| } |
|
|
| this.seriesPickers.each(function (i) { |
| var el = $(this); |
|
|
| el.off('click').on('click', function (e) { |
| var metricName = rowEvolutionGetMetricNameFromRow(this); |
| |
| |
| if (e.shiftKey) { |
| self.toggleSeries(metricName); |
| document.getSelection().removeAllRanges(); |
| } else { |
| self.showSeries(metricName); |
| } |
| $rowEvolution.data('initialMetrics', self.activated); |
| return false; |
| }); |
|
|
| var label = rowEvolutionGetMetricNameFromRow(el); |
| var metricExists = false; |
| for (var k = 0; k < self.originalSeries.length; k++) { |
| if (self.originalSeries[k] && labelMatches(self.originalSeries[k].label, label)) { |
| metricExists = true; |
| } |
| } |
|
|
| if (!metricExists) { |
| el.hide(); |
| } else if ( |
| (initialMetrics.length === 0 && i == 0) |
| || (initialMetrics.length > 0 && initialMetrics.indexOf(label) > -1) |
| || self.initiallyShowAll) { |
| |
| |
| if (!el.hasClass('hiddenByDefault')) { |
| el.show(); |
| } |
| el.find('td').css('opacity', ''); |
| self.activated.push(rowEvolutionGetMetricNameFromRow(el)); |
| } else { |
| if (!el.hasClass('hiddenByDefault')) { |
| el.show(); |
| } |
| |
| el.find('td').css('opacity', .5); |
| } |
|
|
| |
| |
| function labelMatches(lhs, rhs) { |
| return lhs === rhs || piwikHelper.htmlDecode(lhs) === rhs || lhs === piwikHelper.htmlDecode(rhs); |
| } |
| }); |
| }; |
|
|
| RowEvolutionSeriesToggle.prototype.beforeReplot = function () { |
| var self = this; |
| |
| this.seriesPickers.find('td').css('opacity', .5); |
| this.seriesPickers.each(function (i) { |
| var name = rowEvolutionGetMetricNameFromRow(this); |
| if (self.activated.indexOf(name) > -1) { |
| $(this).find('td').css('opacity', 1); |
| } |
| }); |
| }; |
|
|
| |
| |
| |
| (function($){ |
|
|
| $.jqplot.NumberFormatter = function (format, value) { |
|
|
| if (!$.isNumeric(value)) { |
| return format.replace(/%s/, value); |
| } |
| return format.replace(/%s/, NumberFormatter.formatNumber(value)); |
| } |
|
|
| })(jQuery); |
|
|
|
|
| |
| |
| |
| |
|
|
| (function ($) { |
|
|
| $.jqplot.PiwikTicks = function (options) { |
| |
| this.piwikTicksCanvas = null; |
| |
| this.piwikHighlightCanvas = null; |
| |
| this.markerRenderer = new $.jqplot.MarkerRenderer({ |
| shadow: false |
| }); |
| |
| this.currentXTick = false; |
| |
| this.showHighlight = false; |
| |
| this.showGrid = false; |
| |
| this.showTicks = false; |
|
|
| $.extend(true, this, options); |
| }; |
|
|
| $.jqplot.PiwikTicks.init = function (target, data, opts) { |
| |
| var options = opts || {}; |
| this.plugins.piwikTicks = new $.jqplot.PiwikTicks(options.piwikTicks); |
|
|
| if (typeof $.jqplot.PiwikTicks.init.eventsBound == 'undefined') { |
| $.jqplot.PiwikTicks.init.eventsBound = true; |
| $.jqplot.eventListenerHooks.push(['jqplotMouseMove', handleMouseMove]); |
| $.jqplot.eventListenerHooks.push(['jqplotMouseLeave', handleMouseLeave]); |
| } |
| }; |
|
|
| |
| |
| $.jqplot.PiwikTicks.postDraw = function () { |
| var c = this.plugins.piwikTicks; |
|
|
| |
| if (c.showHighlight) { |
| c.piwikHighlightCanvas = new $.jqplot.GenericCanvas(); |
|
|
| this.eventCanvas._elem.before(c.piwikHighlightCanvas.createElement( |
| this._gridPadding, 'jqplot-piwik-highlight-canvas', this._plotDimensions, this)); |
| c.piwikHighlightCanvas.setContext(); |
| } |
|
|
| |
| if (c.showTicks) { |
| var dimensions = this._plotDimensions; |
| dimensions.height += 6; |
| c.piwikTicksCanvas = new $.jqplot.GenericCanvas(); |
| this.series[0].shadowCanvas._elem.before(c.piwikTicksCanvas.createElement( |
| this._gridPadding, 'jqplot-piwik-ticks-canvas', dimensions, this)); |
| c.piwikTicksCanvas.setContext(); |
|
|
| var ctx = c.piwikTicksCanvas._ctx; |
|
|
| var ticks = this.data[0]; |
| var totalWidth = ctx.canvas.width; |
| var tickWidth = totalWidth / ticks.length; |
|
|
| var xaxisLabels = this.axes.xaxis.ticks; |
|
|
| for (var i = 0; i < ticks.length; i++) { |
| var pos = Math.round(i * tickWidth + tickWidth / 2); |
| var full = xaxisLabels[i] && xaxisLabels[i] != ' '; |
| drawLine(ctx, pos, full, c.showGrid, c.tickColor); |
| } |
| } |
| }; |
|
|
| $.jqplot.preInitHooks.push($.jqplot.PiwikTicks.init); |
| $.jqplot.postDrawHooks.push($.jqplot.PiwikTicks.postDraw); |
|
|
| |
| function drawLine(ctx, x, full, showGrid, color) { |
| ctx.save(); |
| ctx.strokeStyle = color; |
|
|
| ctx.beginPath(); |
| ctx.lineWidth = 2; |
| var top = 0; |
| if ((full && !showGrid) || !full) { |
| top = ctx.canvas.height - 5; |
| } |
| ctx.moveTo(x, top); |
| ctx.lineTo(x, full ? ctx.canvas.height : ctx.canvas.height - 2); |
| ctx.stroke(); |
|
|
| |
| ctx.clearRect(x, 0, x + 1, ctx.canvas.height); |
|
|
| ctx.restore(); |
| } |
|
|
| |
| |
| function handleMouseMove(ev, gridpos, datapos, neighbor, plot) { |
| var c = plot.plugins.piwikTicks; |
|
|
| var tick = Math.floor(datapos.xaxis + 0.5) - 1; |
| if (tick !== c.currentXTick) { |
| c.currentXTick = tick; |
| plot.target.trigger('jqplotPiwikTickOver', [tick]); |
| highlight(plot, tick); |
| } |
| } |
|
|
| function handleMouseLeave(ev, gridpos, datapos, neighbor, plot) { |
| unHighlight(plot); |
| plot.plugins.piwikTicks.currentXTick = false; |
| } |
|
|
| |
| function highlight(plot, tick) { |
| var c = plot.plugins.piwikTicks; |
|
|
| if (!c.showHighlight) { |
| return; |
| } |
|
|
| unHighlight(plot); |
|
|
| for (var i = 0; i < plot.series.length; i++) { |
| var series = plot.series[i]; |
| var seriesMarkerRenderer = series.markerRenderer; |
|
|
| c.markerRenderer.style = seriesMarkerRenderer.style; |
| c.markerRenderer.size = 8; |
|
|
| var rgba = $.jqplot.getColorComponents(seriesMarkerRenderer.color); |
| var newrgb = [rgba[0], rgba[1], rgba[2]]; |
| var alpha = rgba[3]; |
| c.markerRenderer.color = 'rgba(' + newrgb[0] + ',' + newrgb[1] + ',' + newrgb[2] + ',' + alpha + ')'; |
| c.markerRenderer.init(); |
|
|
| var position = series.gridData[tick]; |
| if (typeof position !== 'undefined') { |
| c.markerRenderer.draw(position[0], position[1], c.piwikHighlightCanvas._ctx); |
| } |
| } |
| } |
|
|
| function unHighlight(plot) { |
| var canvas = plot.plugins.piwikTicks.piwikHighlightCanvas; |
| if (canvas !== null) { |
| var ctx = canvas._ctx; |
| ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); |
| } |
| } |
|
|
| })(jQuery); |
|
|
| |
| |
| |
| |
|
|
| (function ($) { |
|
|
| $.jqplot.CanvasLegendRenderer = function (options) { |
| |
| this.singleMetric = false; |
| |
| this.show = false; |
|
|
| $.extend(true, this, options); |
| }; |
|
|
| $.jqplot.CanvasLegendRenderer.init = function (target, data, opts) { |
| |
| var options = opts || {}; |
| this.plugins.canvasLegend = new $.jqplot.CanvasLegendRenderer(options.canvasLegend); |
|
|
| if (this.plugins.canvasLegend.show) { |
| options.gridPadding = $.extend({}, options.gridPadding, { |
| top: Math.max((options.gridPadding && options.gridPadding.top) || 0, 21) |
| }); |
| } |
| }; |
|
|
| |
| $.jqplot.CanvasLegendRenderer.postDraw = function () { |
| var plot = this; |
| var legend = plot.plugins.canvasLegend; |
|
|
| if (!legend.show) { |
| return; |
| } |
|
|
| var $target = $(plot.targetId); |
| var $dataTable = $target.closest('.dataTable'); |
| var $legendFooter = getOrCreateLegendFooter($dataTable); |
| var $legendContainer = $legendFooter.find('.jqplot-legend-items'); |
| if (!$legendContainer.length) { |
| return; |
| } |
|
|
| $legendFooter.removeClass('has-legend'); |
| $legendContainer.empty(); |
|
|
| var series = plot.legend && plot.legend._series ? plot.legend._series : []; |
| for (var i = 0; i < series.length; i++) { |
| var s = series[i]; |
| var label = ''; |
| if (legend.labels && legend.labels[i]) { |
| label = legend.labels[i]; |
| } else if (typeof s.label !== 'undefined' && s.label !== null) { |
| label = s.label.toString(); |
| } |
|
|
| if (!label) { |
| continue; |
| } |
|
|
| var color = s.color; |
| if (legend.singleMetric && legend.singleMetricColor) { |
| color = legend.singleMetricColor; |
| } |
|
|
| $('<div/>', {'class': 'jqplot-legend-item'}) |
| .append( |
| $('<span/>', {'class': 'jqplot-legend-swatch'}).css('background-color', color), |
| $('<span/>', { |
| 'class': 'jqplot-legend-label', |
| 'data-original-label': label |
| }).text(label) |
| ) |
| .appendTo($legendContainer); |
| } |
|
|
| if ($legendContainer.children().length) { |
| $legendFooter.addClass('has-legend'); |
| limitLegendRows($legendContainer, MAX_FOOTER_LEGEND_ROWS); |
| } |
|
|
| updateLegendFooterStacking($legendFooter); |
| }; |
|
|
| $.jqplot.preInitHooks.push($.jqplot.CanvasLegendRenderer.init); |
| $.jqplot.postDrawHooks.push($.jqplot.CanvasLegendRenderer.postDraw); |
|
|
| })(jQuery); |
|
|
| |
| |
| |
|
|
| (function ($, require) { |
| $.jqplot.preInitHooks.push(function (target, data, options) { |
| |
| var dataTable = $('#' + target).closest('.dataTable').data('uiControlObject'); |
| if (!dataTable) { |
| return; |
| } |
|
|
| var SeriesPicker = require('piwik/DataTableVisualizations/Widgets').SeriesPicker; |
| var seriesPicker = new SeriesPicker(dataTable); |
|
|
| |
| |
| seriesPicker.useChooseMetricsButton = true; |
|
|
| |
| var plot = this; |
| $(seriesPicker).bind('placeSeriesPicker', function () { |
| var $dataTable = $(plot.targetId).closest('.dataTable'); |
| var $legendFooter = getOrCreateLegendFooter($dataTable); |
| var $pickerSlot = $legendFooter.find('.jqplot-legend-picker'); |
| if (!$pickerSlot.length) { |
| $pickerSlot = $('<div class="jqplot-legend-picker"></div>').prependTo($legendFooter); |
| } |
| |
| $pickerSlot.empty().append(this.domElem); |
| $legendFooter.addClass('has-picker'); |
| updateLegendFooterStacking($legendFooter); |
| }); |
|
|
| |
| $(seriesPicker).bind('seriesPicked', function (e, columns, rows) { |
| dataTable.changeSeries(columns, rows); |
| }); |
|
|
| this.plugins.seriesPicker = seriesPicker; |
| }); |
|
|
| $.jqplot.postDrawHooks.push(function () { |
| this.plugins.seriesPicker.init(); |
|
|
| |
| |
| |
| |
| applyFooterLegendRowLimit($(this.targetId).closest('.dataTable')); |
| }); |
| })(jQuery, require); |
|
|
| |
| |
| |
| |
|
|
| (function ($) { |
|
|
| $.jqplot.PieLegend = function (options) { |
| |
| this.pieLegendCanvas = null; |
| |
| this.show = false; |
|
|
| $.extend(true, this, options); |
| }; |
|
|
| $.jqplot.PieLegend.init = function (target, data, opts) { |
| |
| var options = opts || {}; |
| this.plugins.pieLegend = new $.jqplot.PieLegend(options.pieLegend); |
| }; |
|
|
| |
| $.jqplot.PieLegend.postDraw = function () { |
| var plot = this; |
| var legend = plot.plugins.pieLegend; |
|
|
| if (!legend.show) { |
| return; |
| } |
|
|
| var series = plot.series[0]; |
| var angles = series._sliceAngles; |
| var radius = series._diameter / 2; |
| var center = series._center; |
| var colors = this.seriesColors; |
|
|
| |
| var lineAngles = []; |
| for (var i = 0; i < angles.length; i++) { |
| lineAngles.push((angles[i][0] + angles[i][1]) / 2 + Math.PI / 2); |
| } |
|
|
| |
| var labels = []; |
| var data = series._plotData; |
| for (i = 0; i < data.length; i++) { |
| labels.push(data[i][0]); |
| } |
|
|
| |
| legend.pieLegendCanvas = new $.jqplot.GenericCanvas(); |
| plot.series[0].canvas._elem.before(legend.pieLegendCanvas.createElement( |
| plot._gridPadding, 'jqplot-pie-legend-canvas', plot._plotDimensions, plot)); |
| legend.pieLegendCanvas.setContext(); |
|
|
| var ctx = legend.pieLegendCanvas._ctx; |
| ctx.save(); |
|
|
| ctx.font = '11px ' + require('piwik/UI').getLabelFontFamily() |
|
|
| |
| var height = legend.pieLegendCanvas._elem.height(); |
| var x1, x2, y1, y2, lastY2 = false, right, lastRight = false; |
| for (i = 0; i < labels.length; i++) { |
| var label = labels[i]; |
|
|
| ctx.strokeStyle = colors[i % colors.length]; |
| ctx.lineCap = 'round'; |
| ctx.lineWidth = 1; |
|
|
| |
| x1 = center[0] + Math.sin(lineAngles[i]) * (radius); |
| y1 = center[1] - Math.cos(lineAngles[i]) * (radius); |
|
|
| x2 = center[0] + Math.sin(lineAngles[i]) * (radius + 7); |
| y2 = center[1] - Math.cos(lineAngles[i]) * (radius + 7); |
|
|
| right = x2 > center[0]; |
|
|
| |
| if (lastY2 !== false && lastRight == right && ( |
| (right && y2 - lastY2 < 13) || |
| (!right && lastY2 - y2 < 13))) { |
|
|
| if (x1 > center[0]) { |
| |
| y2 = lastY2 + 13; |
| } else { |
| |
| y2 = lastY2 - 13; |
| } |
| } |
|
|
| if (y2 < 4 || y2 + 4 > height) { |
| continue; |
| } |
|
|
| ctx.beginPath(); |
| ctx.moveTo(x1, y1); |
| ctx.lineTo(x2, y2); |
|
|
| ctx.closePath(); |
| ctx.stroke(); |
|
|
| |
| ctx.beginPath(); |
| ctx.moveTo(x2, y2); |
| if (right) { |
| ctx.lineTo(x2 + 5, y2); |
| } else { |
| ctx.lineTo(x2 - 5, y2); |
| } |
|
|
| ctx.closePath(); |
| ctx.stroke(); |
|
|
| lastY2 = y2; |
| lastRight = right; |
|
|
| |
| if (right) { |
| var x = x2 + 9; |
| } else { |
| var x = x2 - 9 - ctx.measureText(label).width; |
| } |
|
|
| ctx.fillStyle = legend.labelColor; |
| ctx.fillText(label, x, y2 + 3); |
| } |
|
|
| ctx.restore(); |
| }; |
|
|
| $.jqplot.preInitHooks.push($.jqplot.PieLegend.init); |
| $.jqplot.postDrawHooks.push($.jqplot.PieLegend.postDraw); |
|
|
| })(jQuery, require); |
|
|
| |
| |
| |
| |
|
|
| (function ($) { |
|
|
| $.jqplot.LineRenderer.prototype.draw = function(ctx, gd, options, plot) { |
| var i; |
| |
| var opts = $.extend(true, {}, options); |
| var shadow = (opts.shadow != undefined) ? opts.shadow : this.shadow; |
| var showLine = (opts.showLine != undefined) ? opts.showLine : this.showLine; |
| var fill = (opts.fill != undefined) ? opts.fill : this.fill; |
| var fillAndStroke = (opts.fillAndStroke != undefined) ? opts.fillAndStroke : this.fillAndStroke; |
| var xmin, ymin, xmax, ymax; |
|
|
| |
| if (plot.options.hasOwnProperty('dataStates')) { |
| opts.dataStates = plot.options.dataStates; |
| } |
|
|
| if (!Array.isArray(opts.dataStates)) { |
| opts.dataStates = []; |
| } |
|
|
| ctx.save(); |
| if (gd.length) { |
| if (showLine) { |
| |
| if (fill) { |
| if (this.fillToZero) { |
| |
| var negativeColor = this.negativeColor; |
| if (! this.useNegativeColors) { |
| negativeColor = opts.fillStyle; |
| } |
| var isnegative = false; |
| var posfs = opts.fillStyle; |
|
|
| |
| if (fillAndStroke) { |
| var fasgd = gd.slice(0); |
| } |
| |
| if (this.index == 0 || !this._stack) { |
|
|
| var tempgd = []; |
| var pd = (this.renderer.smooth) ? this.renderer._smoothedPlotData : this._plotData; |
| this._areaPoints = []; |
| var pyzero = this._yaxis.series_u2p(this.fillToValue); |
| var pxzero = this._xaxis.series_u2p(this.fillToValue); |
|
|
| opts.closePath = true; |
|
|
| if (this.fillAxis == 'y') { |
| tempgd.push([gd[0][0], pyzero]); |
| this._areaPoints.push([gd[0][0], pyzero]); |
|
|
| for (var i=0; i<gd.length-1; i++) { |
| tempgd.push(gd[i]); |
| this._areaPoints.push(gd[i]); |
| |
| if (pd[i][1] * pd[i+1][1] <= 0) { |
| if (pd[i][1] < 0) { |
| isnegative = true; |
| opts.fillStyle = negativeColor; |
| } |
| else { |
| isnegative = false; |
| opts.fillStyle = posfs; |
| } |
|
|
| var xintercept = gd[i][0] + (gd[i+1][0] - gd[i][0]) * (pyzero-gd[i][1])/(gd[i+1][1] - gd[i][1]); |
| tempgd.push([xintercept, pyzero]); |
| this._areaPoints.push([xintercept, pyzero]); |
| |
| if (shadow) { |
| this.renderer.shadowRenderer.draw(ctx, tempgd, opts); |
| } |
| this.renderer.shapeRenderer.draw(ctx, tempgd, opts); |
| |
| tempgd = [[xintercept, pyzero]]; |
| |
| } |
| } |
| if (pd[gd.length-1][1] < 0) { |
| isnegative = true; |
| opts.fillStyle = negativeColor; |
| } |
| else { |
| isnegative = false; |
| opts.fillStyle = posfs; |
| } |
| tempgd.push(gd[gd.length-1]); |
| this._areaPoints.push(gd[gd.length-1]); |
| tempgd.push([gd[gd.length-1][0], pyzero]); |
| this._areaPoints.push([gd[gd.length-1][0], pyzero]); |
| } |
| |
| if (shadow) { |
| this.renderer.shadowRenderer.draw(ctx, tempgd, opts); |
| } |
| this.renderer.shapeRenderer.draw(ctx, tempgd, opts); |
|
|
| } |
| |
| else { |
| var prev = this._prevGridData; |
| for (var i=prev.length; i>0; i--) { |
| gd.push(prev[i-1]); |
| |
| } |
| if (shadow) { |
| this.renderer.shadowRenderer.draw(ctx, gd, opts); |
| } |
| this._areaPoints = gd; |
| this.renderer.shapeRenderer.draw(ctx, gd, opts); |
| } |
| } |
| |
| |
| |
| else { |
| |
| if (fillAndStroke) { |
| var fasgd = gd.slice(0); |
| } |
| |
| if (this.index == 0 || !this._stack) { |
| |
| var gridymin = ctx.canvas.height; |
| |
| gd.unshift([gd[0][0], gridymin]); |
| var len = gd.length; |
| gd.push([gd[len - 1][0], gridymin]); |
| } |
| |
| else { |
| var prev = this._prevGridData; |
| for (var i=prev.length; i>0; i--) { |
| gd.push(prev[i-1]); |
| } |
| } |
| this._areaPoints = gd; |
|
|
| if (shadow) { |
| this.renderer.shadowRenderer.draw(ctx, gd, opts); |
| } |
|
|
| this.renderer.shapeRenderer.draw(ctx, gd, opts); |
| } |
| if (fillAndStroke) { |
| var fasopts = $.extend(true, {}, opts, {fill:false, closePath:false}); |
| this.renderer.shapeRenderer.draw(ctx, fasgd, fasopts); |
| |
| if (this.markerRenderer.show) { |
| if (this.renderer.smooth) { |
| fasgd = this.gridData; |
| } |
| for (i=0; i<fasgd.length; i++) { |
| this.markerRenderer.draw(fasgd[i][0], fasgd[i][1], ctx, opts.markerOptions); |
| } |
| } |
| } |
| } |
| else { |
|
|
| if (this.renderer.bands.show) { |
| var bdat; |
| var bopts = $.extend(true, {}, opts); |
|
|
| if (this.renderer.bands.showLines) { |
| bdat = (this.renderer.smooth) ? this.renderer._hiBandSmoothedData : this.renderer._hiBandGridData; |
| this.renderer.shapeRenderer.draw(ctx, bdat, opts); |
| bdat = (this.renderer.smooth) ? this.renderer._lowBandSmoothedData : this.renderer._lowBandGridData; |
| this.renderer.shapeRenderer.draw(ctx, bdat, bopts); |
| } |
|
|
| if (this.renderer.bands.fill) { |
| if (this.renderer.smooth) { |
| bdat = this.renderer._hiBandSmoothedData.concat(this.renderer._lowBandSmoothedData.reverse()); |
| } |
| else { |
| bdat = this.renderer._hiBandGridData.concat(this.renderer._lowBandGridData.reverse()); |
| } |
| this._areaPoints = bdat; |
| bopts.closePath = true; |
| bopts.fill = true; |
| bopts.fillStyle = this.renderer.bands.fillColor; |
| this.renderer.shapeRenderer.draw(ctx, bdat, bopts); |
| } |
| } |
|
|
| if (shadow) { |
| this.renderer.shadowRenderer.draw(ctx, gd, opts); |
| } |
|
|
| this.renderer.shapeRenderer.draw(ctx, gd, opts); |
| } |
| } |
| |
| var xmin = xmax = ymin = ymax = null; |
| for (i=0; i<this._areaPoints.length; i++) { |
| var p = this._areaPoints[i]; |
| if (xmin > p[0] || xmin == null) { |
| xmin = p[0]; |
| } |
| if (ymax < p[1] || ymax == null) { |
| ymax = p[1]; |
| } |
| if (xmax < p[0] || xmax == null) { |
| xmax = p[0]; |
| } |
| if (ymin > p[1] || ymin == null) { |
| ymin = p[1]; |
| } |
| } |
|
|
| if (this.type === 'line' && this.renderer.bands.show) { |
| ymax = this._yaxis.series_u2p(this.renderer.bands._min); |
| ymin = this._yaxis.series_u2p(this.renderer.bands._max); |
| } |
|
|
| this._boundingBox = [[xmin, ymax], [xmax, ymin]]; |
|
|
| |
| if (this.markerRenderer.show && !fill) { |
| if (this.renderer.smooth) { |
| gd = this.gridData; |
| } |
| for (i = 0; i < gd.length; i++) { |
| if (gd[i][0] === null || gd[i][1] === null) { |
| continue; |
| } |
|
|
| const markerOptions = opts.markerOptions || {}; |
|
|
| markerOptions.isIncomplete = opts.dataStates[i] && opts.dataStates[i] !== 'complete'; |
| markerOptions.incompleteFillColor = plot.grid.background; |
|
|
| this.markerRenderer.draw(gd[i][0], gd[i][1], ctx, markerOptions); |
| } |
| } |
| } |
|
|
| ctx.restore(); |
| }; |
|
|
| $.jqplot.ShapeRenderer.prototype.draw = function(ctx, points, options) { |
| ctx.save(); |
| var opts = (options != null) ? options : {}; |
| var fill = (opts.fill != null) ? opts.fill : this.fill; |
| var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; |
| var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; |
| var strokeRect = (opts.strokeRect != null) ? opts.strokeRect : this.strokeRect; |
| var clearRect = (opts.clearRect != null) ? opts.clearRect : this.clearRect; |
| var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; |
| var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; |
| var ctxPattern = $.jqplot.LinePattern(ctx, linePattern); |
| ctx.lineWidth = opts.lineWidth || this.lineWidth; |
| ctx.lineJoin = opts.lineJoin || this.lineJoin; |
| ctx.lineCap = opts.lineCap || this.lineCap; |
| ctx.strokeStyle = (opts.strokeStyle || opts.color) || this.strokeStyle; |
| ctx.fillStyle = opts.fillStyle || this.fillStyle; |
| ctx.beginPath(); |
|
|
| let dataStates = []; |
|
|
| if (!closePath && !fill && Array.isArray(opts.dataStates)) { |
| |
| dataStates = opts.dataStates; |
| } |
|
|
| if (isarc) { |
| ctx.arc(points[0], points[1], points[2], points[3], points[4], true); |
|
|
| if (closePath) { |
| ctx.closePath(); |
| } |
|
|
| if (fill) { |
| ctx.fill(); |
| } |
| else { |
| ctx.stroke(); |
| } |
|
|
| if (opts.isIncomplete && opts.incompleteFillColor) { |
| |
| |
| ctx.beginPath(); |
| ctx.arc(points[0], points[1], points[2] / 8, points[3], points[4], true); |
| ctx.strokeStyle = opts.incompleteFillColor; |
| ctx.stroke(); |
| ctx.closePath(); |
| } |
|
|
| ctx.restore(); |
| return; |
| } |
| else if (clearRect) { |
| ctx.clearRect(points[0], points[1], points[2], points[3]); |
| ctx.restore(); |
| return; |
| } |
| else if (fillRect || strokeRect) { |
| if (fillRect) { |
| ctx.fillRect(points[0], points[1], points[2], points[3]); |
| } |
| if (strokeRect) { |
| ctx.strokeRect(points[0], points[1], points[2], points[3]); |
| ctx.restore(); |
| return; |
| } |
| } |
|
|
| if (!points || !points.length) { |
| return; |
| } |
|
|
| let move = true; |
|
|
| for (let i = 0; i < points.length; i++) { |
| |
| if (null === points[i][0] && null === points[i][1]) { |
| continue; |
| } |
|
|
| if (move) { |
| move = false; |
|
|
| ctxPattern.moveTo(points[i][0], points[i][1]); |
| continue; |
| } |
|
|
| |
| if (dataStates[i] && 'complete' !== dataStates[i]) { |
| ctxPattern.moveTo(points[i][0], points[i][1]); |
| } else { |
| ctxPattern.lineTo(points[i][0], points[i][1]); |
| } |
| } |
|
|
| if (closePath) { |
| ctxPattern.closePath(); |
| } |
|
|
| if (fill) { |
| ctx.fill(); |
| } else { |
| ctx.stroke(); |
| } |
|
|
| |
| ctx.beginPath(); |
| ctx.setLineDash([3, 3]); |
|
|
| move = true; |
|
|
| for (let i = 0; i < points.length; i++) { |
| |
| if (points[i][0] === null && points[i][1] === null) { |
| continue; |
| } |
|
|
| if (move) { |
| move = false; |
|
|
| ctxPattern.moveTo(points[i][0], points[i][1]); |
| continue; |
| } |
|
|
| |
| if (!dataStates[i] || 'complete' === dataStates[i]) { |
| ctxPattern.moveTo(points[i][0], points[i][1]); |
| } else { |
| ctxPattern.lineTo(points[i][0], points[i][1]); |
| } |
| } |
|
|
| ctx.stroke(); |
| ctx.closePath(); |
| ctx.restore(); |
| }; |
|
|
| |
| $.jqplot.ShadowRenderer.prototype.draw = function(ctx, points, options) { |
| ctx.save(); |
| var opts = (options != null) ? options : {}; |
| var fill = (opts.fill != null) ? opts.fill : this.fill; |
| var fillRect = (opts.fillRect != null) ? opts.fillRect : this.fillRect; |
| var closePath = (opts.closePath != null) ? opts.closePath : this.closePath; |
| var offset = (opts.offset != null) ? opts.offset : this.offset; |
| var alpha = (opts.alpha != null) ? opts.alpha : this.alpha; |
| var depth = (opts.depth != null) ? opts.depth : this.depth; |
| var isarc = (opts.isarc != null) ? opts.isarc : this.isarc; |
| var linePattern = (opts.linePattern != null) ? opts.linePattern : this.linePattern; |
| ctx.lineWidth = (opts.lineWidth != null) ? opts.lineWidth : this.lineWidth; |
| ctx.lineJoin = (opts.lineJoin != null) ? opts.lineJoin : this.lineJoin; |
| ctx.lineCap = (opts.lineCap != null) ? opts.lineCap : this.lineCap; |
| ctx.strokeStyle = opts.strokeStyle || this.strokeStyle || 'rgba(0,0,0,'+alpha+')'; |
| ctx.fillStyle = opts.fillStyle || this.fillStyle || 'rgba(0,0,0,'+alpha+')'; |
|
|
| let dataStates = []; |
|
|
| if (!closePath && !fill && Array.isArray(opts.dataStates)) { |
| |
| dataStates = opts.dataStates; |
| } |
|
|
| for (let j= 0; j < depth; j++) { |
| const ctxPattern = $.jqplot.LinePattern(ctx, linePattern); |
|
|
| ctx.translate(Math.cos(this.angle*Math.PI/180)*offset, Math.sin(this.angle*Math.PI/180)*offset); |
| ctxPattern.beginPath(); |
|
|
| if (isarc) { |
| ctx.arc(points[0], points[1], points[2], points[3], points[4], true); |
| } |
| else if (fillRect) { |
| ctx.fillRect(points[0], points[1], points[2], points[3]); |
| } |
| else if (points && points.length) { |
| let move = true; |
|
|
| for (let i = 0; i < points.length; i++) { |
| |
| if (points[i][0] === null && points[i][1] === null) { |
| continue; |
| } |
|
|
| if (move) { |
| move = false; |
|
|
| ctxPattern.moveTo(points[i][0], points[i][1]); |
| continue; |
| } |
|
|
| |
| if (dataStates[i] && 'complete' !== dataStates[i]) { |
| ctxPattern.moveTo(points[i][0], points[i][1]); |
| } else { |
| ctxPattern.lineTo(points[i][0], points[i][1]); |
| } |
| } |
| } |
|
|
| if (closePath) { |
| ctxPattern.closePath(); |
| } |
|
|
| if (fill) { |
| ctx.fill(); |
| } |
| else { |
| ctx.stroke(); |
| } |
| } |
|
|
| ctx.restore(); |
| }; |
| })(jQuery); |
|
|