diff --git a/js/chart.js b/js/chart.js
new file mode 100644
index 0000000000..9e86f2506b
--- /dev/null
+++ b/js/chart.js
@@ -0,0 +1,532 @@
+/**
+ * Chart type enumerations
+ */
+var ChartType = {
+ LINE : 'line',
+ SPLINE : 'spline',
+ AREA : 'area',
+ BAR : 'bar',
+ COLUMN : 'column',
+ PIE : 'pie',
+ TIMELINE: 'timeline'
+};
+
+/**
+ * Abstract chart factory which defines the contract for chart factories
+ */
+var ChartFactory = function() {
+};
+ChartFactory.prototype = {
+ createChart : function(type, options) {
+ throw new Error("createChart must be implemented by a subclass");
+ }
+};
+
+/**
+ * Abstract chart which defines the contract for charts
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var Chart = function(elementId) {
+ this.elementId = elementId;
+};
+Chart.prototype = {
+ draw : function(data, options) {
+ throw new Error("draw must be implemented by a subclass");
+ },
+ redraw : function(options) {
+ throw new Error("redraw must be implemented by a subclass");
+ },
+ destroy : function() {
+ throw new Error("destroy must be implemented by a subclass");
+ }
+};
+
+/**
+ * Abstract representation of charts that operates on DataTable where,
+ *
+ *
First column provides index to the data.
+ *
Each subsequent columns are of type
+ * ColumnType.NUMBER and represents a data series.
+ *
+ * Line chart, area chart, bar chart, column chart are typical examples.
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var BaseChart = function(elementId) {
+ Chart.call(this, elementId);
+};
+BaseChart.prototype = new Chart();
+BaseChart.prototype.constructor = BaseChart;
+BaseChart.prototype.validateColumns = function(dataTable) {
+ var columns = dataTable.getColumns();
+ if (columns.length < 2) {
+ throw new Error("Minimum of two columns are required for this chart");
+ }
+ for ( var i = 1; i < columns.length; i++) {
+ if (columns[i].type != ColumnType.NUMBER) {
+ throw new Error("Column " + (i + 1) + " should be of type 'Number'");
+ }
+ }
+ return true;
+};
+
+/**
+ * Abstract pie chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var PieChart = function(elementId) {
+ BaseChart.call(this, elementId);
+};
+PieChart.prototype = new BaseChart();
+PieChart.prototype.constructor = PieChart;
+PieChart.prototype.validateColumns = function(dataTable) {
+ var columns = dataTable.getColumns();
+ if (columns.length > 2) {
+ throw new Error("Pie charts can draw only one series");
+ }
+ return BaseChart.prototype.validateColumns.call(this, dataTable);
+};
+
+/**
+ * Abstract timeline chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var TimelineChart = function(elementId) {
+ BaseChart.call(this, elementId);
+};
+TimelineChart.prototype = new BaseChart();
+TimelineChart.prototype.constructor = TimelineChart;
+TimelineChart.prototype.validateColumns = function(dataTable) {
+ var result = BaseChart.prototype.validateColumns.call(this, dataTable);
+ if (result) {
+ var columns = dataTable.getColumns();
+ if (columns[0].type != ColumnType.DATE) {
+ throw new Error("First column of timeline chart need to be a date column");
+ }
+ }
+ return result;
+};
+
+/**
+ * The data table contains column information and data for the chart.
+ */
+var DataTable = function() {
+ var columns = [];
+ var data;
+
+ this.addColumn = function(type, name) {
+ columns.push({
+ 'type' : type,
+ 'name' : name
+ });
+ };
+
+ this.getColumns = function() {
+ return columns;
+ };
+
+ this.setData = function(rows) {
+ data = rows;
+ fillMissingValues();
+ };
+
+ this.getData = function() {
+ return data;
+ };
+
+ var fillMissingValues = function() {
+ if (columns.length == 0) {
+ throw new Error("Set columns first");
+ }
+ var row, column;
+ for ( var i = 0; i < data.length; i++) {
+ row = data[i];
+ if (row.length > columns.length) {
+ row.splice(columns.length - 1, row.length - columns.length);
+ } else if (row.length < columns.length) {
+ for ( var j = row.length; j < columns.length; j++) {
+ row.push(null);
+ }
+ }
+ }
+ };
+};
+
+/**
+ * Column type enumeration
+ */
+var ColumnType = {
+ STRING : 'string',
+ NUMBER : 'number',
+ BOOLEAN : 'boolean',
+ DATE : 'date'
+};
+
+/*******************************************************************************
+ * JQPlot specifc code
+ ******************************************************************************/
+
+/**
+ * Chart factory that returns JQPlotCharts
+ */
+var JQPlotChartFactory = function() {
+};
+JQPlotChartFactory.prototype = new ChartFactory();
+JQPlotChartFactory.prototype.createChart = function(type, elementId) {
+ var chart;
+ switch (type) {
+ case ChartType.LINE:
+ chart = new JQPlotLineChart(elementId);
+ break;
+ case ChartType.SPLINE:
+ chart = new JQPlotSplineChart(elementId);
+ break;
+ case ChartType.TIMELINE:
+ chart = new JQPlotTimelineChart(elementId);
+ break;
+ case ChartType.AREA:
+ chart = new JQPlotAreaChart(elementId);
+ break;
+ case ChartType.BAR:
+ chart = new JQPlotBarChart(elementId);
+ break;
+ case ChartType.COLUMN:
+ chart = new JQPlotColumnChart(elementId);
+ break;
+ case ChartType.PIE:
+ chart = new JQPlotPieChart(elementId);
+ break;
+ }
+
+ return chart;
+};
+
+/**
+ * Abstract JQplot chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotChart = function(elementId) {
+ Chart.call(this, elementId);
+ this.plot;
+ this.validator;
+};
+JQPlotChart.prototype = new Chart();
+JQPlotChart.prototype.constructor = JQPlotChart;
+JQPlotChart.prototype.draw = function(data, options) {
+ if (this.validator.validateColumns(data)) {
+ this.plot = $.jqplot(this.elementId, this.prepareData(data), this
+ .populateOptions(data, options));
+ }
+};
+JQPlotChart.prototype.destroy = function() {
+ if (this.plot != null) {
+ this.plot.destroy();
+ }
+};
+JQPlotChart.prototype.redraw = function(options) {
+ if (this.plot != null) {
+ this.plot.replot(options);
+ }
+};
+JQPlotChart.prototype.populateOptions = function(dataTable, options) {
+ throw new Error("populateOptions must be implemented by a subclass");
+};
+JQPlotChart.prototype.prepareData = function(dataTable) {
+ throw new Error("prepareData must be implemented by a subclass");
+};
+
+/**
+ * JQPlot line chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotLineChart = function(elementId) {
+ JQPlotChart.call(this, elementId);
+ this.validator = BaseChart.prototype;
+};
+JQPlotLineChart.prototype = new JQPlotChart();
+JQPlotLineChart.prototype.constructor = JQPlotLineChart;
+
+JQPlotLineChart.prototype.populateOptions = function(dataTable, options) {
+ var columns = dataTable.getColumns();
+ var optional = {
+ axes : {
+ xaxis : {
+ label : columns[0].name,
+ renderer : $.jqplot.CategoryAxisRenderer,
+ ticks : []
+ },
+ yaxis : {
+ label : (columns.length == 2 ? columns[1].name : 'Values'),
+ labelRenderer : $.jqplot.CanvasAxisLabelRenderer
+ }
+ },
+ series : []
+ };
+ $.extend(true, optional, options);
+
+ if (optional.series.length == 0) {
+ for ( var i = 1; i < columns.length; i++) {
+ optional.series.push({
+ label : columns[i].name.toString()
+ });
+ }
+ }
+ if (optional.axes.xaxis.ticks.length == 0) {
+ var data = dataTable.getData();
+ for ( var i = 0; i < data.length; i++) {
+ optional.axes.xaxis.ticks.push(data[i][0].toString());
+ }
+ }
+ return optional;
+};
+
+JQPlotLineChart.prototype.prepareData = function(dataTable) {
+ var data = dataTable.getData(), row;
+ var retData = [], retRow;
+ for ( var i = 0; i < data.length; i++) {
+ row = data[i];
+ for ( var j = 1; j < row.length; j++) {
+ retRow = retData[j - 1];
+ if (retRow == null) {
+ retRow = [];
+ retData[j - 1] = retRow;
+ }
+ retRow.push(row[j]);
+ }
+ }
+ return retData;
+};
+
+/**
+ * JQPlot spline chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotSplineChart = function(elementId) {
+ JQPlotLineChart.call(this, elementId);
+};
+JQPlotSplineChart.prototype = new JQPlotLineChart();
+JQPlotSplineChart.prototype.constructor = JQPlotSplineChart;
+
+JQPlotSplineChart.prototype.populateOptions = function(dataTable, options) {
+ var optional = {};
+ var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
+ options);
+ var compulsory = {
+ seriesDefaults : {
+ rendererOptions : {
+ smooth : true
+ }
+ }
+ };
+ $.extend(true, optional, opt, compulsory);
+ return optional;
+};
+
+/**
+ * JQPlot timeline chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotTimelineChart = function(elementId) {
+ JQPlotLineChart.call(this, elementId);
+ this.validator = TimelineChart.prototype;
+};
+JQPlotTimelineChart.prototype = new JQPlotLineChart();
+JQPlotTimelineChart.prototype.constructor = JQPlotAreaChart;
+
+JQPlotTimelineChart.prototype.populateOptions = function(dataTable, options) {
+ var optional = {
+ axes : {
+ xaxis : {
+ tickOptions : {
+ formatString:'%b %#d, %y'
+ }
+ }
+ }
+ };
+ var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable, options);
+ var compulsory = {
+ axes : {
+ xaxis : {
+ renderer : $.jqplot.DateAxisRenderer
+ }
+ }
+ };
+ $.extend(true, optional, opt, compulsory);
+ return optional;
+};
+
+JQPlotTimelineChart.prototype.prepareData = function(dataTable) {
+ var data = dataTable.getData(), row, d;
+ var retData = [], retRow;
+ for ( var i = 0; i < data.length; i++) {
+ row = data[i];
+ d = row[0];
+ for ( var j = 1; j < row.length; j++) {
+ retRow = retData[j - 1];
+ if (retRow == null) {
+ retRow = [];
+ retData[j - 1] = retRow;
+ }
+ if (d != null) {
+ retRow.push([d.getTime(), row[j]]);
+ }
+ }
+ }
+ return retData;
+};
+
+/**
+ * JQPlot area chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotAreaChart = function(elementId) {
+ JQPlotLineChart.call(this, elementId);
+};
+JQPlotAreaChart.prototype = new JQPlotLineChart();
+JQPlotAreaChart.prototype.constructor = JQPlotAreaChart;
+
+JQPlotAreaChart.prototype.populateOptions = function(dataTable, options) {
+ var optional = {};
+ var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
+ options);
+ var compulsory = {
+ seriesDefaults : {
+ fill : true
+ }
+ };
+ $.extend(true, optional, opt, compulsory);
+ return optional;
+};
+
+/**
+ * JQPlot column chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotColumnChart = function(elementId) {
+ JQPlotLineChart.call(this, elementId);
+};
+JQPlotColumnChart.prototype = new JQPlotLineChart();
+JQPlotColumnChart.prototype.constructor = JQPlotColumnChart;
+
+JQPlotColumnChart.prototype.populateOptions = function(dataTable, options) {
+ var optional = {};
+ var opt = JQPlotLineChart.prototype.populateOptions.call(this, dataTable,
+ options);
+ var compulsory = {
+ seriesDefaults : {
+ fillToZero : true,
+ renderer : $.jqplot.BarRenderer
+ }
+ };
+ $.extend(true, optional, opt, compulsory);
+ return optional;
+};
+
+/**
+ * JQPlot bar chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotBarChart = function(elementId) {
+ JQPlotLineChart.call(this, elementId);
+};
+JQPlotBarChart.prototype = new JQPlotLineChart();
+JQPlotBarChart.prototype.constructor = JQPlotBarChart;
+
+JQPlotBarChart.prototype.populateOptions = function(dataTable, options) {
+ var columns = dataTable.getColumns();
+ var optional = {
+ axes : {
+ yaxis : {
+ label : columns[0].name,
+ labelRenderer : $.jqplot.CanvasAxisLabelRenderer,
+ renderer : $.jqplot.CategoryAxisRenderer,
+ ticks : []
+ },
+ xaxis : {
+ label : (columns.length == 2 ? columns[1].name : 'Values'),
+ labelRenderer : $.jqplot.CanvasAxisLabelRenderer
+ }
+ },
+ series : []
+ };
+ var compulsory = {
+ seriesDefaults : {
+ fillToZero : true,
+ renderer : $.jqplot.BarRenderer,
+ rendererOptions : {
+ barDirection : 'horizontal'
+ }
+ }
+ };
+ $.extend(true, optional, options, compulsory);
+
+ if (optional.axes.yaxis.ticks.length == 0) {
+ var data = dataTable.getData();
+ for ( var i = 0; i < data.length; i++) {
+ optional.axes.yaxis.ticks.push(data[i][0].toString());
+ }
+ }
+ if (optional.series.length == 0) {
+ for ( var i = 1; i < columns.length; i++) {
+ optional.series.push({
+ label : columns[i].name.toString()
+ });
+ }
+ }
+ return optional;
+};
+
+/**
+ * JQPlot pie chart
+ *
+ * @param elementId
+ * id of the div element the chart is drawn in
+ */
+var JQPlotPieChart = function(elementId) {
+ JQPlotChart.call(this, elementId);
+ this.validator = PieChart.prototype;
+};
+JQPlotPieChart.prototype = new JQPlotChart();
+JQPlotPieChart.prototype.constructor = JQPlotPieChart;
+
+JQPlotPieChart.prototype.populateOptions = function(dataTable, options) {
+ var optional = {};
+ var compulsory = {
+ seriesDefaults : {
+ renderer : $.jqplot.PieRenderer
+ }
+ };
+ $.extend(true, optional, options, compulsory);
+ return optional;
+};
+
+JQPlotPieChart.prototype.prepareData = function(dataTable) {
+ var data = dataTable.getData(), row;
+ var retData = [];
+ for ( var i = 0; i < data.length; i++) {
+ row = data[i];
+ retData.push([ row[0], row[1] ]);
+ }
+ return [ retData ];
+};
diff --git a/js/messages.php b/js/messages.php
index de9b941c6c..4a0df5271d 100644
--- a/js/messages.php
+++ b/js/messages.php
@@ -44,8 +44,8 @@ $js_messages['strEditIndex'] = __('Edit Index');
$js_messages['strAddToIndex'] = __('Add %s column(s) to index');
/* Charts */
-/* l10n: Default description for the y-Axis of Charts */
-$js_messages['strTotalCount'] = __('Total count');
+/* l10n: Default label for the y-Axis of Charts */
+$js_messages['strYValues'] = __('Y Values');
/* For server_privileges.js */
$js_messages['strHostEmpty'] = __('The host name is empty!');
diff --git a/js/tbl_chart.js b/js/tbl_chart.js
index 235e5d50c9..7e9582c45f 100644
--- a/js/tbl_chart.js
+++ b/js/tbl_chart.js
@@ -1,12 +1,9 @@
/* vim: set expandtab sw=4 ts=4 sts=4: */
-var chart_xaxis_idx = -1;
-var chart_series;
-var chart_data = null;
+var chart_data = {};
var temp_chart_title;
-var y_values_text;
+
var currentChart = null;
-var nonJqplotSettings = null;
var currentSettings = null;
/**
@@ -20,89 +17,63 @@ AJAX.registerTeardown('tbl_chart.js', function() {
$('select[name="chartSeries"]').unbind('change');
$('input[name="xaxis_label"]').unbind('keyup');
$('input[name="yaxis_label"]').unbind('keyup');
+ $('#resizer').unbind('resizestop');
});
AJAX.registerOnload('tbl_chart.js', function() {
- chart_series = $('select[name="chartSeries"]').val();
- // If no series is selected null is returned.
- // In such case initialize chart_series to empty array.
- if (chart_series == null) {
- chart_series = new Array();
- }
- chart_xaxis_idx = $('select[name="chartXAxis"]').val();
- y_values_text = $('input[name="yaxis_label"]').val();
// from jQuery UI
$('#resizer').resizable({
- minHeight:240,
- minWidth:300
- });
+ minHeight: 240,
+ minWidth: 300
+ })
+ .width($('#div_view_options').width() - 50);
- $('#resizer').bind('resizestop', function(event,ui) {
+ $('#resizer').bind('resizestop', function(event, ui) {
// make room so that the handle will still appear
$('#querychart').height($('#resizer').height() * 0.96);
$('#querychart').width($('#resizer').width() * 0.96);
- currentChart.replot( {resetAxes: true})
+ currentChart.redraw({
+ resetAxes : true
+ });
});
- nonJqplotSettings = {
- chart: {
- type: 'line',
- width: $('#resizer').width() - 20,
- height: $('#resizer').height() - 20
- }
- }
-
currentSettings = {
- grid: {
- drawBorder: false,
- shadow: false,
- background: 'rgba(0,0,0,0)'
- },
- axes: {
- xaxis: {
- label: $('input[name="xaxis_label"]').val(),
- labelRenderer: $.jqplot.CanvasAxisLabelRenderer
- },
- yaxis: {
- label: $('input[name="yaxis_label"]').val(),
- labelRenderer: $.jqplot.CanvasAxisLabelRenderer
- }
- },
- title: {
- text: $('input[name="chartTitle"]').attr('value'),
- escapeHtml: true
- //margin:20
- },
- legend: {
- show: true,
- placement: 'outsideGrid',
- location: 'se'
- }
+ type : 'line',
+ width : $('#resizer').width() - 20,
+ height : $('#resizer').height() - 20,
+ xaxisLabel : $('input[name="xaxis_label"]').val(),
+ yaxisLabel : $('input[name="yaxis_label"]').val(),
+ title : $('input[name="chartTitle"]').val(),
+ stackSeries : false,
+ mainAxis : parseInt($('select[name="chartXAxis"]').val()),
+ selectedSeries : getSelectedSeries()
};
-
+ // handle chart type changes
$('input[name="chartType"]').click(function() {
- nonJqplotSettings.chart.type = $(this).val();
-
+ currentSettings.type = $(this).val();
drawChart();
-
- if ($(this).val() == 'bar' || $(this).val() == 'column') {
+ if ($(this).val() == 'bar' || $(this).val() == 'column'
+ || $(this).val() == 'line' || $(this).val() == 'area'
+ || $(this).val() == 'timeline' || $(this).val() == 'spline') {
$('span.barStacked').show();
} else {
$('span.barStacked').hide();
}
});
+ // handle stacking for bar, column and area charts
$('input[name="barStacked"]').click(function() {
if (this.checked) {
- $.extend(true,currentSettings,{ stackSeries: true });
+ $.extend(true, currentSettings, {stackSeries : true});
} else {
- $.extend(true,currentSettings,{ stackSeries: false });
+ $.extend(true, currentSettings, {stackSeries : false});
}
drawChart();
});
+ // handle changes in chart title
$('input[name="chartTitle"]').focus(function() {
temp_chart_title = $(this).val();
});
@@ -120,44 +91,62 @@ AJAX.registerOnload('tbl_chart.js', function() {
}
});
+ var dateTimeCols = [];
+ var vals = $('input[name="dateTimeCols"]').val().split(' ');
+ $.each(vals, function(i, v) {
+ dateTimeCols.push(parseInt(v));
+ });
+
+ // handle changing the x-axis
$('select[name="chartXAxis"]').change(function() {
- chart_xaxis_idx = $(this).val();
+ currentSettings.mainAxis = parseInt($(this).val());
+ if (dateTimeCols.indexOf(currentSettings.mainAxis) != -1) {
+ $('span.span_timeline').show();
+ } else {
+ $('span.span_timeline').hide();
+ if (currentSettings.type == 'timeline') {
+ $('input#radio_line').prop('checked', true);
+ currentSettings.type = 'line';
+ }
+ }
var xaxis_title = $(this).children('option:selected').text();
$('input[name="xaxis_label"]').val(xaxis_title);
- currentSettings.axes.xaxis.label = xaxis_title;
+ currentSettings.xaxisLabel = xaxis_title;
drawChart();
});
- $('select[name="chartSeries"]').change(function() {
- chart_series = $(this).val();
- if (chart_series.length == 1) {
+ // handle changing the selected data series
+ $('select[name="chartSeries"]').change(function() {
+ currentSettings.selectedSeries = getSelectedSeries();
+ var yaxis_title;
+ if (currentSettings.selectedSeries.length == 1) {
$('span.span_pie').show();
- var yaxis_title = $(this).children('option:selected').text();
+ yaxis_title = $(this).children('option:selected').text();
} else {
$('span.span_pie').hide();
- if (nonJqplotSettings.chart.type == 'pie') {
+ if (currentSettings.type == 'pie') {
$('input#radio_line').prop('checked', true);
- nonJqplotSettings.chart.type = 'line';
+ currentSettings.type = 'line';
}
- var yaxis_title = y_values_text;
+ yaxis_title = PMA_messages['strYValues'];
}
$('input[name="yaxis_label"]').val(yaxis_title);
- currentSettings.axes.yaxis.label = yaxis_title;
-
+ currentSettings.yaxisLabel = yaxis_title;
drawChart();
});
- /* Sucks, we cannot just set axis labels, we have to redraw the chart completely */
+ // handle manual changes to the chart axis labels
$('input[name="xaxis_label"]').keyup(function() {
- currentSettings.axes.xaxis.label = $(this).val();
+ currentSettings.xaxisLabel = $(this).val();
drawChart();
});
$('input[name="yaxis_label"]').keyup(function() {
- currentSettings.axes.yaxis.label = $(this).val();
+ currentSettings.yaxisLabel = $(this).val();
drawChart();
});
-});
+ $("#tblchartform").submit();
+});
/**
* Ajax Event handler for 'Go' button click
@@ -165,12 +154,12 @@ AJAX.registerOnload('tbl_chart.js', function() {
*/
$("#tblchartform").live('submit', function(event) {
if (!checkFormElementInRange(this, 'session_max_rows', PMA_messages['strNotValidRowNumber'], 1)
- || !checkFormElementInRange(this, 'pos', PMA_messages['strNotValidRowNumber'], 0-1)) {
+ || !checkFormElementInRange(this, 'pos', PMA_messages['strNotValidRowNumber'], 0 - 1)) {
return false;
- }
+ }
var $form = $(this);
- if (! checkSqlQuery($form[0])) {
+ if (!checkSqlQuery($form[0])) {
return false;
}
// remove any div containing a previous error message
@@ -178,7 +167,7 @@ $("#tblchartform").live('submit', function(event) {
var $msgbox = PMA_ajaxShowMessage();
PMA_prepareForAjaxRequest($form);
- $.post($form.attr('action'), $form.serialize() , function(data) {
+ $.post($form.attr('action'), $form.serialize(), function(data) {
if (data.success == true) {
$('.success').fadeOut();
if (typeof data.chartData != 'undefined') {
@@ -186,7 +175,9 @@ $("#tblchartform").live('submit', function(event) {
drawChart();
$('div#querychart').height($('div#resizer').height() * 0.96);
$('div#querychart').width($('div#resizer').width() * 0.96);
- currentChart.replot( {resetAxes: true});
+ currentChart.redraw({
+ resetAxes : true
+ });
$('#querychart').show();
}
} else {
@@ -201,154 +192,122 @@ $("#tblchartform").live('submit', function(event) {
return false;
}); // end
-function isColumnNumeric(columnName)
-{
- var first = true;
- var isNumeric = false;
- $('select[name="chartSeries"] option').each(function() {
- if ($(this).val() == columnName) {
- isNumeric = true;
- return false;
- }
- });
- return isNumeric;
-}
-
function drawChart() {
- nonJqplotSettings.chart.width = $('#resizer').width() - 20;
- nonJqplotSettings.chart.height = $('#resizer').height() - 20;
+ currentSettings.width = $('#resizer').width() - 20;
+ currentSettings.height = $('#resizer').height() - 20;
- // todo: a better way using .replot() ?
+ // todo: a better way using .redraw() ?
if (currentChart != null) {
currentChart.destroy();
}
- currentChart = PMA_queryChart(chart_data, currentSettings, nonJqplotSettings);
+
+ var columnNames = [];
+ $('select[name="chartXAxis"] option').each(function() {
+ columnNames.push($(this).text());
+ });
+ currentChart = PMA_queryChart(chart_data, columnNames, currentSettings);
}
-function PMA_queryChart(data, passedSettings, passedNonJqplotSettings)
-{
+function getSelectedSeries() {
+ var val = $('select[name="chartSeries"]').val() || [];
+ var ret = [];
+ $.each(val, function(i, v) {
+ ret.push(parseInt(v));
+ });
+ return ret;
+}
+
+function extractDate(dateString) {
+ var matches, match;
+ var dateTimeRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/;
+ var dateRegExp = /[0-9]{4}-[0-9]{2}-[0-9]{2}/;
+
+ matches = dateTimeRegExp.exec(dateString);
+ if (matches != null && matches.length > 0) {
+ match = matches[0];
+ return new Date(match.substr(0, 4), match.substr(5, 2), match.substr(8, 2), match.substr(11, 2), match.substr(14, 2), match.substr(17, 2));
+ } else {
+ matches = dateRegExp.exec(dateString);
+ if (matches != null && matches.length > 0) {
+ match = matches[0];
+ return new Date(match.substr(0, 4), match.substr(5, 2), match.substr(8, 2));
+ }
+ }
+ return null;
+}
+
+function PMA_queryChart(data, columnNames, settings) {
if ($('#querychart').length == 0) {
return;
}
- var columnNames = [];
- var series = new Array();
- var xaxis = {
- type: 'linear',
- categories: new Array()
+ jqPlotSettings = {
+ title : {
+ text : settings.title,
+ escapeHtml: true
+ },
+ grid : {
+ drawBorder : false,
+ shadow : false,
+ background : 'rgba(0,0,0,0)'
+ },
+ legend : {
+ show : true,
+ placement : 'outsideGrid',
+ location : 'e'
+ },
+ axes : {
+ xaxis : {
+ label : settings.xaxisLabel
+ },
+ yaxis : {
+ label : settings.yaxisLabel
+ }
+ },
+ stackSeries : settings.stackSeries
};
- var yaxis = new Object();
- $.each(data[0], function(index, element) {
- columnNames.push(index);
+ // create the chart
+ var factory = new JQPlotChartFactory();
+ var chart = factory.createChart(settings.type, "querychart");
+
+ // create the data table and add columns
+ var dataTable = new DataTable();
+ if (settings.type == 'timeline') {
+ dataTable.addColumn(ColumnType.DATE, columnNames[settings.mainAxis]);
+ } else {
+ dataTable.addColumn(ColumnType.STRING, columnNames[settings.mainAxis]);
+ }
+ $.each(settings.selectedSeries, function(index, element) {
+ dataTable.addColumn(ColumnType.NUMBER, columnNames[element]);
});
- switch(passedNonJqplotSettings.chart.type) {
- case 'column':
- case 'spline':
- case 'line':
- case 'bar':
- var j = 0;
- for (var i = 0; i < columnNames.length; i++) {
- if (i != chart_xaxis_idx) {
- series[j] = new Array();
- if ($.inArray(columnNames[i], chart_series) != -1) {
- $.each(data,function(key,value) {
- series[j].push(
- [
- value[columnNames[chart_xaxis_idx]],
- // todo: not always a number?
- parseFloat(value[columnNames[i]])
- ]
- );
- });
- j++;
- }
+ // set data to the data table
+ var columnsToExtract = [ settings.mainAxis ];
+ $.each(settings.selectedSeries, function(index, element) {
+ columnsToExtract.push(element);
+ });
+ var values = [], newRow, row, col;
+ for ( var i = 0; i < data.length; i++) {
+ row = data[i];
+ newRow = [];
+ for ( var j = 0; j < columnsToExtract.length; j++) {
+ col = columnNames[columnsToExtract[j]];
+ if (j == 0) {
+ if (settings.type == 'timeline') { // first column is date type
+ newRow.push(extractDate(row[col]));
+ } else { // first column is string type
+ newRow.push(row[col]);
}
- }
- if (columnNames.length == 2)
- yaxis.title = { text: columnNames[0] };
- break;
-
- case 'pie':
- if (chart_series.length == 1) {
- series[0] = new Array();
- $.each(data,function(key,value) {
- series[0].push(
- [
- value[columnNames[chart_xaxis_idx]],
- parseFloat(value[chart_series])
- ]
- );
- });
- break;
- }
- }
-
- var settings = {
- title: {
- text: '',
- escapeHtml: true
- //margin:20
- }
- };
-
- if (passedNonJqplotSettings.chart.type == 'line') {
- settings.axes = {
- xaxis: {
- },
- yaxis: {
+ } else { // subsequent columns are of type, number
+ newRow.push(parseFloat(row[col]));
}
}
+ values.push(newRow);
}
+ dataTable.setData(values);
- if (passedNonJqplotSettings.chart.type == 'bar') {
- settings.seriesDefaults = {
- renderer: $.jqplot.BarRenderer,
- rendererOptions: {
- barDirection: 'vertical',
- highlightMouseOver: true
- }
- };
- settings.axes = {
- xaxis: {
- renderer: $.jqplot.CategoryAxisRenderer
- },
- yaxis: {
- }
- };
- }
-
- if (passedNonJqplotSettings.chart.type == 'spline') {
- settings.seriesDefaults = {
- rendererOptions: {
- smooth: true
- }
- };
- }
-
- if (passedNonJqplotSettings.chart.type == 'pie') {
- settings.seriesDefaults = {
- renderer: $.jqplot.PieRenderer,
- rendererOptions: {
- showDataLabels: true,
- highlightMouseOver: true,
- showDataLabels: true,
- dataLabels: 'value'
- }
- };
- }
- // Overwrite/Merge default settings with passedsettings
- $.extend(true, settings, passedSettings);
-
- settings.series = new Array();
- for (var i = 0; i < columnNames.length; i++) {
- if (parseInt(chart_xaxis_idx) != i) {
- if ($.inArray(columnNames[i], chart_series) != -1) {
- settings.series.push({ label: columnNames[i] });
- }
- }
- }
-
- return $.jqplot('querychart', series, settings);
+ // draw the chart and return the chart object
+ chart.draw(dataTable, jqPlotSettings);
+ return chart;
}
diff --git a/tbl_chart.php b/tbl_chart.php
index 94e24854fd..52cd18bfa3 100644
--- a/tbl_chart.php
+++ b/tbl_chart.php
@@ -62,12 +62,14 @@ if (isset($_REQUEST['ajax_request'])
$response = PMA_Response::getInstance();
$header = $response->getHeader();
$scripts = $header->getScripts();
+$scripts->addFile('chart.js');
$scripts->addFile('tbl_chart.js');
$scripts->addFile('jqplot/jquery.jqplot.js');
$scripts->addFile('jqplot/plugins/jqplot.barRenderer.js');
$scripts->addFile('jqplot/plugins/jqplot.canvasAxisLabelRenderer.js');
$scripts->addFile('jqplot/plugins/jqplot.canvasTextRenderer.js');
$scripts->addFile('jqplot/plugins/jqplot.categoryAxisRenderer.js');
+$scripts->addFile('jqplot/plugins/jqplot.dateAxisRenderer.js');
$scripts->addFile('jqplot/plugins/jqplot.pointLabels.js');
$scripts->addFile('jqplot/plugins/jqplot.pieRenderer.js');
/* < IE 9 doesn't support canvas natively */
@@ -126,7 +128,7 @@ url_query = '';