; (function($) { $.fn.highchartTable = function() { var allowedGraphTypes = ['column', 'line', 'area', 'spline', 'pie']; var getCallable = function (table, attribute) { var callback = $(table).data(attribute); if (typeof callback != 'undefined') { var infosCallback = callback.split('.'); var callable = window[infosCallback[0]]; for(var i = 1, infosCallbackLength = infosCallback.length; i < infosCallbackLength; i++) { callable = callable[infosCallback[i]]; } return callable; } }; this.each(function() { var table = $(this); var $table = $(table); var nbYaxis = 1; // Retrieve graph title from the table caption var captions = $('caption', table); var graphTitle = captions.length ? $(captions[0]).text() : ''; var graphContainer; if ($table.data('graph-container-before') != 1) { // Retrieve where the graph must be displayed from the graph-container attribute var graphContainerSelector = $table.data('graph-container'); if (!graphContainerSelector) { throw "graph-container data attribute is mandatory"; } if (graphContainerSelector[0] === '#' || graphContainerSelector.indexOf('..')===-1) { // Absolute selector path graphContainer = $(graphContainerSelector); } else { var referenceNode = table; var currentGraphContainerSelector = graphContainerSelector; while (currentGraphContainerSelector.indexOf('..')!==-1) { currentGraphContainerSelector = currentGraphContainerSelector.replace(/^.. /, ''); referenceNode = referenceNode.parent(); } graphContainer = $(currentGraphContainerSelector, referenceNode); } if (graphContainer.length !== 1) { throw "graph-container is not available in this DOM or available multiple times"; } graphContainer = graphContainer[0]; } else { $table.before('
'); graphContainer = $table.prev(); graphContainer = graphContainer[0]; } // Retrieve graph type from graph-type attribute var globalGraphType = $table.data('graph-type'); if (!globalGraphType) { throw "graph-type data attribute is mandatory"; } if ($.inArray(globalGraphType, allowedGraphTypes) == -1) { throw "graph-container data attribute must be one of " + allowedGraphTypes.join(', '); } var stackingType = $table.data('graph-stacking'); if (!stackingType) { stackingType = 'normal'; } var dataLabelsEnabled = $table.data('graph-datalabels-enabled'); var isGraphInverted = $table.data('graph-inverted') == 1; // Retrieve series titles var ths = $('thead th', table); var columns = []; var vlines = []; var skippedColumns = 0; var graphIsStacked = false; ths.each(function(indexTh, th) { var $th = $(th); var columnScale = $th.data('graph-value-scale'); var serieGraphType = $th.data('graph-type'); if($.inArray(serieGraphType, allowedGraphTypes) == -1) { serieGraphType = globalGraphType; } var serieStackGroup = $th.data('graph-stack-group'); if(serieStackGroup) { graphIsStacked = true; } var serieDataLabelsEnabled = $th.data('graph-datalabels-enabled'); if (typeof serieDataLabelsEnabled == 'undefined') { serieDataLabelsEnabled = dataLabelsEnabled; } var yaxis = $th.data('graph-yaxis'); if (typeof yaxis != 'undefined' && yaxis == '1') { nbYaxis = 2; } var isColumnSkipped = $th.data('graph-skip') == 1; if (isColumnSkipped) { skippedColumns = skippedColumns + 1; } var thGraphConfig = { libelle: $th.text(), skip: isColumnSkipped, indexTd: indexTh - skippedColumns - 1, color: $th.data('graph-color'), visible: !$th.data('graph-hidden'), yAxis: typeof yaxis != 'undefined' ? yaxis : 0, dashStyle: $th.data('graph-dash-style') || 'solid', dataLabelsEnabled: serieDataLabelsEnabled == 1, dataLabelsColor: $th.data('graph-datalabels-color') || $table.data('graph-datalabels-color') }; var vlinex = $th.data('graph-vline-x'); if (typeof vlinex == 'undefined') { thGraphConfig.scale = typeof columnScale != 'undefined' ? parseFloat(columnScale) : 1; thGraphConfig.graphType = serieGraphType == 'column' && isGraphInverted ? 'bar' : serieGraphType; thGraphConfig.stack = serieStackGroup; thGraphConfig.unit = $th.data('graph-unit'); columns[indexTh] = thGraphConfig; } else { thGraphConfig.x = vlinex; thGraphConfig.height = $th.data('graph-vline-height'); thGraphConfig.name = $th.data('graph-vline-name'); vlines[indexTh] = thGraphConfig; } }); var series = []; $(columns).each(function(indexColumn, column) { if(indexColumn!=0 && !column.skip) { var serieConfig = { name: column.libelle + (column.unit ? ' (' + column.unit + ')' : ''), data: [], type: column.graphType, stack: column.stack, color: column.color, visible: column.visible, yAxis: column.yAxis, dashStyle: column.dashStyle, marker: { enabled: false }, dataLabels: { enabled: column.dataLabelsEnabled, color: column.dataLabelsColor, align: $table.data('graph-datalabels-align') || (globalGraphType == 'column' && isGraphInverted == 1 ? undefined : 'center') } }; if(column.dataLabelsEnabled) { var callableSerieDataLabelsFormatter = getCallable(table, 'graph-datalabels-formatter'); if (callableSerieDataLabelsFormatter) { serieConfig.dataLabels.formatter = function () { return callableSerieDataLabelsFormatter(this.y); }; } } series.push(serieConfig); } }); $(vlines).each(function(indexColumn, vline) { if (typeof vline != 'undefined' && !vline.skip) { series.push({ name: vline.libelle, data: [{x: vline.x, y:0, name: vline.name}, {x:vline.x, y:vline.height, name: vline.name}], type: 'spline', color: vline.color, visible: vline.visible, marker: { enabled: false } }); } }); var xValues = []; var callablePoint = getCallable(table, 'graph-point-callback'); var isGraphDatetime = $table.data('graph-xaxis-type') == 'datetime'; var rows = $('tbody:first tr', table); rows.each(function(indexRow, row) { if (!!$(row).data('graph-skip')) { return; } var tds = $('td', row); tds.each(function(indexTd, td) { var cellValue; var column = columns[indexTd]; if (column.skip) { return; } var $td = $(td); if (indexTd==0) { cellValue = $td.text(); xValues.push(cellValue); } else { var rawCellValue = $td.text(); var serie = series[column.indexTd]; if (rawCellValue.length==0) { if (!isGraphDatetime) { serie.data.push(null); } } else { var cleanedCellValue = rawCellValue.replace(/\s/g, '').replace(/,/, '.'); var eventOptions = { value: cleanedCellValue, rawValue: rawCellValue, td: $td, tr: $(row), indexTd: indexTd, indexTr: indexRow } $table.trigger('highchartTable.cleanValue', eventOptions); cellValue = Math.round(parseFloat(eventOptions.value) * column.scale * 100) / 100; var dataGraphX = $td.data('graph-x'); if (isGraphDatetime) { dataGraphX = $('td', $(row)).first().text(); var date = parseDate(dataGraphX); dataGraphX = date.getTime() - date.getTimezoneOffset()*60*1000; } var tdGraphName = $td.data('graph-name'); var serieDataItem = { name: typeof tdGraphName != 'undefined' ? tdGraphName : rawCellValue, y: cellValue, x: dataGraphX //undefined if no x defined in table }; if (callablePoint) { serieDataItem.events = { click: function () { return callablePoint(this); } }; } if (column.graphType === 'pie') { if ($td.data('graph-item-highlight')) { serieDataItem.sliced = 1; } } var tdGraphItemColor = $td.data('graph-item-color'); if (typeof tdGraphItemColor != 'undefined') { serieDataItem.color = tdGraphItemColor; } serie.data.push(serieDataItem); } } }); }); var getYaxisAttr = function($table, yAxisNum, name) { var oldConvention = $table.data('graph-yaxis-' + yAxisNum + '-' + name); if (typeof oldConvention != 'undefined') { return oldConvention; } return $table.data('graph-yaxis' + yAxisNum + '-' + name); }; var yAxisConfig = []; var yAxisNum; for (yAxisNum=1 ; yAxisNum <= nbYaxis ; yAxisNum++) { var yAxisConfigCurrentAxis = { title: { text: typeof getYaxisAttr($table, yAxisNum, 'title-text') != 'undefined' ? getYaxisAttr($table, yAxisNum, 'title-text') : null }, max: typeof getYaxisAttr($table, yAxisNum, 'max') != 'undefined' ? getYaxisAttr($table, yAxisNum, 'max') : null, min: typeof getYaxisAttr($table, yAxisNum, 'min') != 'undefined' ? getYaxisAttr($table, yAxisNum, 'min') : null, reversed: getYaxisAttr($table, yAxisNum, 'reversed') == '1', opposite: getYaxisAttr($table, yAxisNum, 'opposite') == '1', tickInterval: getYaxisAttr($table, yAxisNum, 'tick-interval') || null, labels: { rotation: getYaxisAttr($table, yAxisNum, 'rotation') || 0 }, startOnTick: getYaxisAttr($table, yAxisNum, 'start-on-tick') != "0", endOnTick: getYaxisAttr($table, yAxisNum, 'end-on-tick') != "0", stackLabels : { enabled: getYaxisAttr($table, yAxisNum, 'stacklabels-enabled') == '1' }, gridLineInterpolation: getYaxisAttr($table, yAxisNum, 'grid-line-interpolation') || null }; var callableYAxisFormatter = getCallable(table, 'graph-yaxis-'+yAxisNum+'-formatter-callback'); if (!callableYAxisFormatter) { callableYAxisFormatter = getCallable(table, 'graph-yaxis'+yAxisNum+'-formatter-callback'); } if (callableYAxisFormatter) { yAxisConfigCurrentAxis.labels.formatter = function () { return callableYAxisFormatter(this.value); }; } yAxisConfig.push(yAxisConfigCurrentAxis); } var defaultColors = [ '#4572A7', '#AA4643', '#89A54E', '#80699B', '#3D96AE', '#DB843D', '#92A8CD', '#A47D7C', '#B5CA92' ]; var colors = []; var themeColors = typeof Highcharts.theme != 'undefined' && typeof Highcharts.theme.colors != 'undefined' ? Highcharts.theme.colors : []; var lineShadow = $table.data('graph-line-shadow'); var lineWidth = $table.data('graph-line-width') || 2; var nbOfColors = Math.max(defaultColors.length, themeColors.length); for(var i=0; i < nbOfColors; i++) { var dataname = 'graph-color-' + (i+1); colors.push(typeof $table.data(dataname) != 'undefined' ? $table.data(dataname) : typeof themeColors[i] != 'undefined' ? themeColors[i] : defaultColors[i]); } var marginTop = $table.data('graph-margin-top'); var marginRight = $table.data('graph-margin-right'); var marginBottom = $table.data('graph-margin-bottom'); var marginLeft = $table.data('graph-margin-left'); var xAxisLabelsEnabled = $table.data('graph-xaxis-labels-enabled'); var xAxisLabelStyle = {}; var xAxisLabelFontSize = $table.data('graph-xaxis-labels-font-size'); if (typeof xAxisLabelFontSize != 'undefined') { xAxisLabelStyle.fontSize = xAxisLabelFontSize; } var highChartConfig = { colors: colors, chart: { renderTo: graphContainer, inverted: isGraphInverted, marginTop: typeof marginTop != 'undefined' ? marginTop : null, marginRight: typeof marginRight != 'undefined' ? marginRight : null, marginBottom: typeof marginBottom != 'undefined' ? marginBottom : null, marginLeft: typeof marginLeft != 'undefined' ? marginLeft : null, spacingTop: $table.data('graph-spacing-top') || 10, height: $table.data('graph-height') || null, zoomType: $table.data('graph-zoom-type') || null, polar: $table.data('graph-polar') || null }, title: { text: graphTitle }, subtitle: { text: $table.data('graph-subtitle-text') || '' }, legend: { enabled: $table.data('graph-legend-disabled') != '1', layout: $table.data('graph-legend-layout') || 'horizontal', symbolWidth: $table.data('graph-legend-width') || 30, x: $table.data('graph-legend-x') || 15, y: $table.data('graph-legend-y') || 0 }, xAxis: { categories: ($table.data('graph-xaxis-type') != 'datetime') ? xValues : undefined, type: ($table.data('graph-xaxis-type') == 'datetime') ? 'datetime' : undefined, reversed: $table.data('graph-xaxis-reversed') == '1', opposite: $table.data('graph-xaxis-opposite') == '1', showLastLabel: typeof $table.data('graph-xaxis-show-last-label') != 'undefined' ? $table.data('graph-xaxis-show-last-label') : true, tickInterval: $table.data('graph-xaxis-tick-interval') || null, dateTimeLabelFormats: { //by default, we display the day and month on the datetime graphs second: '%e. %b', minute: '%e. %b', hour: '%e. %b', day: '%e. %b', week: '%e. %b', month: '%e. %b', year: '%e. %b' }, labels: { rotation: $table.data('graph-xaxis-rotation') || undefined, align: $table.data('graph-xaxis-align') || undefined, enabled: typeof xAxisLabelsEnabled != 'undefined' ? xAxisLabelsEnabled : true, style: xAxisLabelStyle }, startOnTick: $table.data('graph-xaxis-start-on-tick'), endOnTick: $table.data('graph-xaxis-end-on-tick'), min: getXAxisMinMax(table, 'min'), max: getXAxisMinMax(table, 'max'), alternateGridColor: $table.data('graph-xaxis-alternateGridColor') || null, title: { text: $table.data('graph-xaxis-title-text') || null }, gridLineWidth: $table.data('graph-xaxis-gridLine-width') || 0, gridLineDashStyle: $table.data('graph-xaxis-gridLine-style') || 'ShortDot', tickmarkPlacement: $table.data('graph-xaxis-tickmark-placement') || 'between', lineWidth: $table.data('graph-xaxis-line-width') || 0 }, yAxis: yAxisConfig, tooltip: { formatter: function() { if ($table.data('graph-xaxis-type') == 'datetime') { return ''+ this.series.name +'