2
0
mirror of https://github.com/fhem/fhem-mirror.git synced 2025-04-07 19:04:20 +00:00

see changed file for changes

git-svn-id: https://svn.fhem.de/fhem/trunk@3144 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
johannnes 2013-05-01 15:18:03 +00:00
parent f5011972e5
commit d80a0dbfd9
34 changed files with 344 additions and 19430 deletions

View File

@ -1,3 +1,10 @@
Update vom 01.5.2013
* Performance Verbesserungen der Charts
* Statistikberechnungen nach ZoomIn hinzugefügt
* Highcharts entfernt
* Schatten in Charts entfernt
* Achsenkonfiguration manuell möglich (min/max)
* Achsenbeschriftung verbessert
Update vom 27.4.2013 Update vom 27.4.2013
* Performance Verbesserungen der Charts * Performance Verbesserungen der Charts
* Achsenkonfiguration aufgeräumt * Achsenkonfiguration aufgeräumt

View File

@ -29,45 +29,7 @@ DIR www/frontend/lib/ext-4.2.0.663/images/window
DIR www/frontend/lib/ext-4.2.0.663/images/grid DIR www/frontend/lib/ext-4.2.0.663/images/grid
DIR www/frontend/lib/ext-4.2.0.663/images/util DIR www/frontend/lib/ext-4.2.0.663/images/util
DIR www/frontend/lib/ext-4.2.0.663/images/util/splitter DIR www/frontend/lib/ext-4.2.0.663/images/util/splitter
DIR www/frontend/lib/highcharts UPD 2013-04-26_05:06:42 899 www/frontend/index.html
DIR www/frontend/lib/highcharts/ux
DIR www/frontend/lib/highcharts/ux/Highcharts
DIR www/frontend/lib/jquery
UPD 2013-04-26_05:06:42 1059 www/frontend/index.html
UPD 2013-04-26_05:06:43 616 www/frontend/README.txt
UPD 2013-04-01_07:05:33 613 www/frontend/app/userconfig.js
UPD 2013-04-01_08:00:32 2104 www/frontend/app/resources/loading.png
UPD 2013-03-02_01:53:05 626 www/frontend/app/resources/icons/readme.txt
UPD 2013-03-02_01:53:05 755 www/frontend/app/resources/icons/database_save.png
UPD 2013-03-02_01:53:05 395 www/frontend/app/resources/icons/resultset_next.png
UPD 2013-03-02_01:53:05 700 www/frontend/app/resources/icons/stop.png
UPD 2013-03-02_01:53:05 770 www/frontend/app/resources/icons/database_refresh.png
UPD 2013-03-02_01:53:05 524 www/frontend/app/resources/icons/resultset_last.png
UPD 2013-04-03_07:27:17 781 www/frontend/app/resources/icons/accept.png
UPD 2013-04-03_07:27:17 715 www/frontend/app/resources/icons/delete.png
UPD 2013-04-03_07:27:17 345 www/frontend/app/resources/icons/arrow_left.png
UPD 2013-04-03_07:27:17 733 www/frontend/app/resources/icons/add.png
UPD 2013-04-03_07:27:17 389 www/frontend/app/resources/icons/resultset_previous.png
UPD 2013-04-26_05:05:23 2238 www/frontend/app/app.js
UPD 2013-04-28_02:00:20 22189 www/frontend/app/view/LineChartPanel.js
UPD 2013-04-26_05:05:57 22790 www/frontend/app/view/HighChartsPanel.js
UPD 2013-04-28_02:00:20 1205 www/frontend/app/view/ChartGridPanel.js
UPD 2013-04-03_07:26:40 15793 www/frontend/app/view/DevicePanel.js
UPD 2013-04-26_05:05:57 8922 www/frontend/app/view/Viewport.js
UPD 2013-04-01_07:05:14 2476 www/frontend/app/view/TableDataGridPanel.js
UPD 2013-04-28_01:59:56 64882 www/frontend/app/controller/ChartController.js
UPD 2013-04-26_05:05:41 12806 www/frontend/app/controller/HighChartController.js
UPD 2013-04-27_06:15:02 13892 www/frontend/app/controller/MainController.js
UPD 2013-04-01_07:04:35 202 www/frontend/app/model/ReadingsModel.js
UPD 2013-04-01_07:04:36 338 www/frontend/app/model/SavedChartsModel.js
UPD 2013-04-01_07:04:34 11535 www/frontend/app/model/ChartModel.js
UPD 2013-04-01_07:04:35 198 www/frontend/app/model/DeviceModel.js
UPD 2013-04-01_07:04:34 685 www/frontend/app/model/TableDataModel.js
UPD 2013-04-01_07:04:54 432 www/frontend/app/store/ChartStore.js
UPD 2013-04-01_07:04:54 451 www/frontend/app/store/SavedChartsStore.js
UPD 2013-04-01_07:04:54 426 www/frontend/app/store/ReadingsStore.js
UPD 2013-04-01_07:04:54 1048 www/frontend/app/store/TableDataStore.js
UPD 2013-04-27_06:11:13 439 www/frontend/app/store/DeviceStore.js
UPD 2013-04-01_07:03:30 260475 www/frontend/lib/ext-4.2.0.663/ext-theme-gray-all.css UPD 2013-04-01_07:03:30 260475 www/frontend/lib/ext-4.2.0.663/ext-theme-gray-all.css
UPD 2013-04-01_07:03:30 1434875 www/frontend/lib/ext-4.2.0.663/ext-all.js UPD 2013-04-01_07:03:30 1434875 www/frontend/lib/ext-4.2.0.663/ext-all.js
UPD 2013-04-01_07:03:33 1981 www/frontend/lib/ext-4.2.0.663/images/tools/tools-sprites-trans.gif UPD 2013-04-01_07:03:33 1981 www/frontend/lib/ext-4.2.0.663/images/tools/tools-sprites-trans.gif
@ -294,26 +256,35 @@ UPD 2013-04-01_07:03:33 872 www/frontend/lib/ext-4.2.0.663/images/util/splitter/
UPD 2013-04-01_07:03:33 856 www/frontend/lib/ext-4.2.0.663/images/util/splitter/mini-bottom.gif UPD 2013-04-01_07:03:33 856 www/frontend/lib/ext-4.2.0.663/images/util/splitter/mini-bottom.gif
UPD 2013-04-01_07:03:33 856 www/frontend/lib/ext-4.2.0.663/images/util/splitter/mini-top.gif UPD 2013-04-01_07:03:33 856 www/frontend/lib/ext-4.2.0.663/images/util/splitter/mini-top.gif
UPD 2013-04-01_07:51:34 1482 www/frontend/lib/ext-4.2.0.663/license.txt UPD 2013-04-01_07:51:34 1482 www/frontend/lib/ext-4.2.0.663/license.txt
UPD 2013-04-26_05:06:25 376 www/frontend/lib/highcharts/ux/Highcharts/GaugeSerie.js UPD 2013-04-26_05:06:43 440 www/frontend/README.txt
UPD 2013-04-26_05:06:25 264 www/frontend/lib/highcharts/ux/Highcharts/ColumnSerie.js UPD 2013-04-01_07:05:33 613 www/frontend/app/userconfig.js
UPD 2013-04-26_05:06:25 2502 www/frontend/lib/highcharts/ux/Highcharts/WaterfallSerie.js UPD 2013-04-01_08:00:32 2104 www/frontend/app/resources/loading.png
UPD 2013-04-26_05:06:25 10509 www/frontend/lib/highcharts/ux/Highcharts/PieSerie.js UPD 2013-04-03_07:27:17 781 www/frontend/app/resources/icons/accept.png
UPD 2013-04-26_05:06:25 265 www/frontend/lib/highcharts/ux/Highcharts/SplineSerie.js UPD 2013-03-02_01:53:05 626 www/frontend/app/resources/icons/readme.txt
UPD 2013-04-26_05:06:25 1809 www/frontend/lib/highcharts/ux/Highcharts/BubbleSerie.js UPD 2013-04-03_07:27:17 715 www/frontend/app/resources/icons/delete.png
UPD 2013-04-26_05:06:25 268 www/frontend/lib/highcharts/ux/Highcharts/ScatterSerie.js UPD 2013-03-02_01:53:05 755 www/frontend/app/resources/icons/database_save.png
UPD 2013-04-26_05:06:25 261 www/frontend/lib/highcharts/ux/Highcharts/AreaSerie.js UPD 2013-03-02_01:53:05 395 www/frontend/app/resources/icons/resultset_next.png
UPD 2013-04-26_05:06:25 1266 www/frontend/lib/highcharts/ux/Highcharts/BoxPlotSerie.js UPD 2013-03-02_01:53:05 700 www/frontend/app/resources/icons/stop.png
UPD 2013-04-26_05:06:25 1088 www/frontend/lib/highcharts/ux/Highcharts/FunnelSerie.js UPD 2013-04-03_07:27:17 345 www/frontend/app/resources/icons/arrow_left.png
UPD 2013-04-26_05:06:25 283 www/frontend/lib/highcharts/ux/Highcharts/ErrorBarSerie.js UPD 2013-03-02_01:53:05 770 www/frontend/app/resources/icons/database_refresh.png
UPD 2013-04-26_05:06:25 281 www/frontend/lib/highcharts/ux/Highcharts/AreaSplineSerie.js UPD 2013-03-02_01:53:05 524 www/frontend/app/resources/icons/resultset_last.png
UPD 2013-04-26_05:06:25 11822 www/frontend/lib/highcharts/ux/Highcharts/Serie.js UPD 2013-04-03_07:27:17 733 www/frontend/app/resources/icons/add.png
UPD 2013-04-26_05:06:25 315 www/frontend/lib/highcharts/ux/Highcharts/AreaSplineRangeSerie.js UPD 2013-04-03_07:27:17 389 www/frontend/app/resources/icons/resultset_previous.png
UPD 2013-04-26_05:06:25 2084 www/frontend/lib/highcharts/ux/Highcharts/RangeSerie.js UPD 2013-05-01_05:09:36 2154 www/frontend/app/app.js
UPD 2013-04-26_05:06:25 295 www/frontend/lib/highcharts/ux/Highcharts/ColumnRangeSerie.js UPD 2013-05-01_05:10:56 27200 www/frontend/app/view/LineChartPanel.js
UPD 2013-04-26_05:06:25 252 www/frontend/lib/highcharts/ux/Highcharts/BarSerie.js UPD 2013-04-28_02:00:20 1205 www/frontend/app/view/ChartGridPanel.js
UPD 2013-04-26_05:06:25 257 www/frontend/lib/highcharts/ux/Highcharts/LineSerie.js UPD 2013-04-03_07:26:40 15793 www/frontend/app/view/DevicePanel.js
UPD 2013-04-26_05:06:25 290 www/frontend/lib/highcharts/ux/Highcharts/AreaRangeSerie.js UPD 2013-05-01_05:10:56 8782 www/frontend/app/view/Viewport.js
UPD 2013-04-26_05:06:25 629 www/frontend/lib/highcharts/ux/License UPD 2013-04-01_07:05:14 2476 www/frontend/app/view/TableDataGridPanel.js
UPD 2013-04-26_05:06:25 41709 www/frontend/lib/highcharts/ux/Highcharts.js UPD 2013-05-01_05:10:42 76820 www/frontend/app/controller/ChartController.js
UPD 2013-04-26_05:06:25 426260 www/frontend/lib/highcharts/highcharts.src.js UPD 2013-05-01_05:10:42 13004 www/frontend/app/controller/MainController.js
UPD 2013-04-26_05:06:25 93867 www/frontend/lib/jquery/jquery.min.js UPD 2013-04-01_07:04:35 202 www/frontend/app/model/ReadingsModel.js
UPD 2013-04-01_07:04:36 338 www/frontend/app/model/SavedChartsModel.js
UPD 2013-04-01_07:04:34 11535 www/frontend/app/model/ChartModel.js
UPD 2013-04-01_07:04:35 198 www/frontend/app/model/DeviceModel.js
UPD 2013-04-01_07:04:34 685 www/frontend/app/model/TableDataModel.js
UPD 2013-04-01_07:04:54 432 www/frontend/app/store/ChartStore.js
UPD 2013-04-01_07:04:54 451 www/frontend/app/store/SavedChartsStore.js
UPD 2013-04-01_07:04:54 426 www/frontend/app/store/ReadingsStore.js
UPD 2013-04-01_07:04:54 1048 www/frontend/app/store/TableDataStore.js
UPD 2013-04-27_06:11:13 439 www/frontend/app/store/DeviceStore.js

View File

@ -1,11 +1,9 @@
This is the readme of the new Webfrontend, based on ExtJS / Highcharts / JQuery. This is the readme of the new Webfrontend, based on ExtJS.
As there is no full documentation available at the moment, As there is no full documentation available at the moment,
please refer to this thread on the forums of FHEM to get help, ask questions or get updates: please refer to this thread on the forums of FHEM to get help, ask questions or get updates:
http://forum.fhem.de/index.php?t=msg&th=10439&start=0&rid=0 http://forum.fhem.de/index.php?t=msg&th=10439&start=0&rid=0
The ExtJS Library as well as the application itself are available under the GPLv3 License - http://www.sencha.com/ The ExtJS Library as well as the application itself are available under the GPLv3 License - http://www.sencha.com/
JQuery is available under the GPL License - http://jquery.com/
Highcharts is available under the Creative Commons License - http://www.highcharts.com/
See the license.txt in the lib folder for details See the license.txt in the lib folder for details

View File

@ -6,8 +6,7 @@ Ext.Loader.setConfig({
enabled: true, enabled: true,
disableCaching: false, disableCaching: false,
paths: { paths: {
'FHEM': 'app', 'FHEM': 'app'
'Chart' : 'lib/highcharts/'
} }
}); });
@ -19,8 +18,7 @@ Ext.application({
controllers: [ controllers: [
'FHEM.controller.MainController', 'FHEM.controller.MainController',
'FHEM.controller.ChartController', 'FHEM.controller.ChartController'
'FHEM.controller.HighChartController'
], ],
launch: function() { launch: function() {

View File

@ -151,6 +151,11 @@ Ext.define('FHEM.controller.ChartController', {
chart = me.getChart(); chart = me.getChart();
//cleanup chartpanel //cleanup chartpanel
var existingwins = Ext.ComponentQuery.query('window[name=statisticswin]');
Ext.each(existingwins, function(existingwin) {
existingwin.destroy();
});
var existingchartgrid = Ext.ComponentQuery.query('panel[name=chartgridpanel]')[0]; var existingchartgrid = Ext.ComponentQuery.query('panel[name=chartgridpanel]')[0];
if (!existingchartgrid) { if (!existingchartgrid) {
var chartdatagrid = Ext.create('FHEM.view.ChartGridPanel', { var chartdatagrid = Ext.create('FHEM.view.ChartGridPanel', {
@ -352,6 +357,12 @@ Ext.define('FHEM.controller.ChartController', {
scope: me, scope: me,
handler: function(btn) { handler: function(btn) {
var chart = btn.up().up().down('chart'); var chart = btn.up().up().down('chart');
var existingwins = Ext.ComponentQuery.query('window[name=statisticswin]');
Ext.each(existingwins, function(existingwin) {
existingwin.destroy();
});
chart.restoreZoom(); chart.restoreZoom();
chart.axes.get(0).minimum = chart.up().up().getLastYmin(); chart.axes.get(0).minimum = chart.up().up().getLastYmin();
@ -419,6 +430,7 @@ Ext.define('FHEM.controller.ChartController', {
} }
], ],
animate: false, animate: false,
shadow: false,
store: store, store: store,
enableMask: true, enableMask: true,
mask: true,//'vertical',//true, //'horizontal', mask: true,//'vertical',//true, //'horizontal',
@ -452,6 +464,89 @@ Ext.define('FHEM.controller.ChartController', {
}); });
serie.hideAll(); serie.hideAll();
} else {
//creating statistic windows after zooming
var html,
count = 0,
sum = 0,
average = 0,
min = 99999999,
max = 0,
lastrec,
diffkwh = 0,
winwidth = 125,
winheight = 105;
Ext.each(serie.items, function(item) {
if (Ext.isNumeric(item.value[1])) {
count++;
sum = sum + item.value[1];
if (min > item.value[1]) {
min = item.value[1];
}
if (max < item.value[1]) {
max = item.value[1];
}
if (serie.title.indexOf('actual_kwh') >= 0) {
if (lastrec) {
var diffhrs = Ext.Date.getElapsed(lastrec.value[0], item.value[0]) / 1000 / 3600;
diffkwh = diffkwh + diffhrs * lastrec.value[1];
}
lastrec = item;
winwidth = 165,
winheight = 130;
}
}
});
average = sum / count;
html = '<b>Selected Items: </b>' + count + '<br>';
html += '<b>Sum: </b>' + Ext.util.Format.round(sum, 5) + '<br>';
html += '<b>Average: </b>' + Ext.util.Format.round(average, 5) + '<br>';
html += '<b>Min: </b>' + min + '<br>';
html += '<b>Max: </b>' + max + '<br>';
if (serie.title.indexOf('actual_kwh') >= 0) {
html += '<b>Used kW/h: </b>' + Ext.util.Format.round(diffkwh, 3) + '<br>';
html += '<b>Costs (at 25c/kWh): </b>' + Ext.util.Format.round(diffkwh * 0.25, 2) + '€<br>';
}
var existingwins = Ext.ComponentQuery.query('window[name=statisticswin]'),
matchfound = false,
lastwin;
if (existingwins.length > 0) {
Ext.each(existingwins, function(existingwin) {
lastwin = existingwin;
if (existingwin.title === serie.title) {
existingwin.update(html);
existingwin.showAt(chart.getWidth() - 145, chart.getPosition()[1] + 8);
matchfound = true;
}
});
if (!matchfound) {
var win = Ext.create('Ext.window.Window', {
width: winwidth,
height: winheight,
html: html,
title: serie.title,
name: 'statisticswin',
preventHeader: true,
border: false,
plain: true
});
win.showAt(chart.getWidth() - 145, lastwin.getPosition()[1] + lastwin.getHeight());
}
} else {
var win = Ext.create('Ext.window.Window', {
width: winwidth,
height: winheight,
html: html,
title: serie.title,
name: 'statisticswin',
preventHeader: true,
border: false,
plain: true
});
win.showAt(chart.getWidth() - 145, chart.getPosition()[1] + 8);
}
} }
}); });
} }
@ -766,7 +861,9 @@ Ext.define('FHEM.controller.ChartController', {
* do the final layout of chart after all data is loaded * do the final layout of chart after all data is loaded
*/ */
doFinalChartLayout: function(chart) { doFinalChartLayout: function(chart) {
var me = this; var me = this,
leftaxisconfiguration = Ext.ComponentQuery.query('radiogroup[name=leftaxisconfiguration]')[0].getChecked()[0].inputValue,
rightaxisconfiguration = Ext.ComponentQuery.query('radiogroup[name=rightaxisconfiguration]')[0].getChecked()[0].inputValue;
//remove the old max values of y axis to get a dynamic range //remove the old max values of y axis to get a dynamic range
delete chart.axes.get(0).maximum; delete chart.axes.get(0).maximum;
@ -774,6 +871,7 @@ Ext.define('FHEM.controller.ChartController', {
delete chart.axes.get(1).maximum; delete chart.axes.get(1).maximum;
delete chart.axes.get(1).minimum; delete chart.axes.get(1).minimum;
chart.axes.get(0).maximum = me.maxYValue; chart.axes.get(0).maximum = me.maxYValue;
chart.axes.get(1).maximum = me.maxY2Value; chart.axes.get(1).maximum = me.maxY2Value;
@ -796,6 +894,29 @@ Ext.define('FHEM.controller.ChartController', {
chart.axes.get(1).minimum = me.minY2Value; chart.axes.get(1).minimum = me.minY2Value;
} }
//if user has specified its own range, use it
if (leftaxisconfiguration === "manual") {
var leftaxismin = Ext.ComponentQuery.query('numberfield[name=leftaxisminimum]')[0].getValue(),
leftaxismax = Ext.ComponentQuery.query('numberfield[name=leftaxismaximum]')[0].getValue();
if (Ext.isNumeric(leftaxismin) && Ext.isNumeric(leftaxismax)) {
chart.axes.get(0).minimum = leftaxismin;
chart.axes.get(0).maximum = leftaxismax;
} else {
Ext.Msg.alert("Error", "Please select a valid minimum and maximum for the axis!");
}
}
if (rightaxisconfiguration === "manual") {
var rightaxismin = Ext.ComponentQuery.query('numberfield[name=rightaxisminimum]')[0].getValue(),
rightaxismax = Ext.ComponentQuery.query('numberfield[name=rightaxismaximum]')[0].getValue();
if (Ext.isNumeric(rightaxismin) && Ext.isNumeric(rightaxismax)) {
chart.axes.get(1).minimum = rightaxismin;
chart.axes.get(1).maximum = rightaxismax;
} else {
Ext.Msg.alert("Error", "Please select a valid minimum and maximum for the axis!");
}
}
// set the x axis range dependent on user given timerange // set the x axis range dependent on user given timerange
var starttime = new Date(me.getStarttimepicker().getValue()), var starttime = new Date(me.getStarttimepicker().getValue()),
@ -804,10 +925,22 @@ Ext.define('FHEM.controller.ChartController', {
chart.axes.get(2).fromDate = starttime; chart.axes.get(2).fromDate = starttime;
chart.axes.get(2).toDate = endtime; chart.axes.get(2).toDate = endtime;
chart.axes.get(2).processView();
//collapse chart settings var timediffhrs = Ext.Date.getElapsed(chart.axes.get(2).fromDate, chart.axes.get(2).toDate) / 1000 / 3600;
//me.getChartformpanel().collapse();
if (timediffhrs <= 1) {
chart.axes.get(2).step = [Ext.Date.MINUTE, 10];
} else if (timediffhrs <= 24) {
chart.axes.get(2).step = [Ext.Date.HOUR, 1];
} else if (timediffhrs <= 168) {
chart.axes.get(2).step = [Ext.Date.DAY, 1];
} else if (timediffhrs <= 720) {
chart.axes.get(2).step = [Ext.Date.DAY, 7];
} else if (timediffhrs < 720) {
chart.axes.get(2).step = [Ext.Date.MONTH, 1];
}
chart.axes.get(2).processView();
me.resizeChart(); me.resizeChart();
@ -868,7 +1001,8 @@ Ext.define('FHEM.controller.ChartController', {
fill: fill, fill: fill,
style: { style: {
fill: color, fill: color,
stroke: color stroke: '#808080',
'stroke-width': 2
}, },
markerConfig: { markerConfig: {
type: 'circle', type: 'circle',
@ -966,6 +1100,15 @@ Ext.define('FHEM.controller.ChartController', {
Ext.ComponentQuery.query('datefield[name=starttimepicker]')[0].reset(); Ext.ComponentQuery.query('datefield[name=starttimepicker]')[0].reset();
Ext.ComponentQuery.query('datefield[name=endtimepicker]')[0].reset(); Ext.ComponentQuery.query('datefield[name=endtimepicker]')[0].reset();
Ext.ComponentQuery.query('radiofield[name=generalization]')[1].setValue(true); Ext.ComponentQuery.query('radiofield[name=generalization]')[1].setValue(true);
Ext.ComponentQuery.query('numberfield[name=leftaxisminimum]')[0].reset();
Ext.ComponentQuery.query('numberfield[name=leftaxismaximum]')[0].reset();
Ext.ComponentQuery.query('numberfield[name=rightaxisminimum]')[0].reset();
Ext.ComponentQuery.query('numberfield[name=rightaxismaximum]')[0].reset();
Ext.ComponentQuery.query('radiogroup[name=leftaxisconfiguration]')[0].items.items[0].setValue(true);
Ext.ComponentQuery.query('radiogroup[name=rightaxisconfiguration]')[0].items.items[0].setValue(true);
}, },
/** /**
@ -1037,6 +1180,8 @@ Ext.define('FHEM.controller.ChartController', {
dynamicradio = Ext.ComponentQuery.query('radiogroup[name=dynamictime]')[0], dynamicradio = Ext.ComponentQuery.query('radiogroup[name=dynamictime]')[0],
generalization = Ext.ComponentQuery.query('radio[boxLabel=active]')[0], generalization = Ext.ComponentQuery.query('radio[boxLabel=active]')[0],
generalizationfactor = Ext.ComponentQuery.query('combobox[name=genfactor]')[0].getValue(), generalizationfactor = Ext.ComponentQuery.query('combobox[name=genfactor]')[0].getValue(),
leftaxisconfiguration = Ext.ComponentQuery.query('radiogroup[name=leftaxisconfiguration]')[0].getChecked()[0].inputValue,
rightaxisconfiguration = Ext.ComponentQuery.query('radiogroup[name=rightaxisconfiguration]')[0].getChecked()[0].inputValue,
chart = me.getChart(), chart = me.getChart(),
store = chart.getStore(); store = chart.getStore();
@ -1096,6 +1241,30 @@ Ext.define('FHEM.controller.ChartController', {
jsonConfig += '"generalizationfactor":"' + generalizationfactor + '",'; jsonConfig += '"generalizationfactor":"' + generalizationfactor + '",';
} }
if (leftaxisconfiguration === 'manual') {
var leftaxismin = Ext.ComponentQuery.query('numberfield[name=leftaxisminimum]')[0].getValue(),
leftaxismax = Ext.ComponentQuery.query('numberfield[name=leftaxismaximum]')[0].getValue();
if (Ext.isNumeric(leftaxismin) && Ext.isNumeric(leftaxismax)) {
jsonConfig += '"leftaxismin":"' + leftaxismin + '",';
jsonConfig += '"leftaxismax":"' + leftaxismax + '",';
} else {
Ext.Msg.alert("Error", "Left axis configuration is invalid, values will not be saved!");
}
}
if (rightaxisconfiguration === "manual") {
var rightaxismin = Ext.ComponentQuery.query('numberfield[name=rightaxisminimum]')[0].getValue(),
rightaxismax = Ext.ComponentQuery.query('numberfield[name=rightaxismaximum]')[0].getValue();
if (Ext.isNumeric(rightaxismin) && Ext.isNumeric(rightaxismax)) {
jsonConfig += '"rightaxismin":"' + rightaxismin + '",';
jsonConfig += '"rightaxismax":"' + rightaxismax + '",';
} else {
Ext.Msg.alert("Error", "Right axis configuration is invalid, values will not be saved!");
}
}
var j = 0; var j = 0;
Ext.each(basesstart, function(base) { Ext.each(basesstart, function(base) {
var basestart = basesstart[j].getValue(), var basestart = basesstart[j].getValue(),
@ -1305,6 +1474,18 @@ Ext.define('FHEM.controller.ChartController', {
genbox.setValue(false); genbox.setValue(false);
} }
if (chartdata.leftaxismin && chartdata.leftaxismax) {
Ext.ComponentQuery.query('radiogroup[name=leftaxisconfiguration]')[0].items.items[1].setValue(true);
Ext.ComponentQuery.query('numberfield[name=leftaxisminimum]')[0].setValue(chartdata.leftaxismin);
Ext.ComponentQuery.query('numberfield[name=leftaxismaximum]')[0].setValue(chartdata.leftaxismax);
}
if (chartdata.rightaxismin && chartdata.rightaxismax) {
Ext.ComponentQuery.query('radiogroup[name=rightaxisconfiguration]')[0].items.items[1].setValue(true);
Ext.ComponentQuery.query('numberfield[name=rightaxisminimum]')[0].setValue(chartdata.rightaxismin);
Ext.ComponentQuery.query('numberfield[name=rightaxismaximum]')[0].setValue(chartdata.rightaxismax);
}
this.requestChartData(); this.requestChartData();
this.getLinechartpanel().setTitle(name); this.getLinechartpanel().setTitle(name);
} else { } else {

View File

@ -1,317 +0,0 @@
/**
* Controller handling the charts
*/
Ext.define('FHEM.controller.HighChartController', {
extend: 'Ext.app.Controller',
refs: [
{
selector: 'panel[name=highchartformpanel]',
ref: 'chartformpanel' //this.getChartformpanel()
},
{
selector: 'datefield[name=highchartstarttimepicker]',
ref: 'starttimepicker' //this.getStarttimepicker()
},
{
selector: 'datefield[name=highchartendtimepicker]',
ref: 'endtimepicker' //this.getEndtimepicker()
},
{
selector: 'button[name=highchartrequestchartdata]',
ref: 'requestchartdatabtn' //this.getRequestchartdatabtn()
},
{
selector: 'button[name=highchartsavechartdata]',
ref: 'savechartdatabtn' //this.getSavechartdatabtn()
},
{
selector: 'chart',
ref: 'chart' //this.getChart()
},
{
selector: 'chartformpanel',
ref: 'panel[name=highchartchartformpanel]' //this.getChartformpanel()
},
{
selector: 'highchartspanel',
ref: 'highchartpanel' //this.getHighchartpanel()
}
// {
// selector: 'linechartpanel toolbar',
// ref: 'linecharttoolbar' //this.getLinecharttoolbar()
// },
// {
// selector: 'grid[name=highchartsavedchartsgrid]',
// ref: 'savedchartsgrid' //this.getSavedchartsgrid()
// },
// {
// selector: 'grid[name=highchartchartdata]',
// ref: 'chartdatagrid' //this.getChartdatagrid()
// }
],
/**
* init function to register listeners
*/
init: function() {
this.control({
'button[name=highchartrequestchartdata]': {
click: this.requestChartData
},
// 'button[name=savechartdata]': {
// click: this.saveChartData
// },
// 'button[name=stepback]': {
// click: this.stepchange
// },
// 'button[name=stepforward]': {
// click: this.stepchange
// },
'button[name=highchartresetchartform]': {
click: this.resetFormFields
},
// 'grid[name=savedchartsgrid]': {
// cellclick: this.loadsavedchart
// },
// 'actioncolumn[name=savedchartsactioncolumn]': {
// click: this.deletechart
// },
// 'grid[name=chartdata]': {
// itemclick: this.highlightRecordInChart
// },
'panel[name=highchartchartpanel]': {
collapse: this.resizeChart,
expand: this.resizeChart
},
'panel[name=highchartformpanel]': {
collapse: this.resizeChart,
expand: this.resizeChart
}
// 'panel[name=chartgridpanel]': {
// collapse: this.resizeChart,
// expand: this.resizeChart
// }
});
},
/**
* Triggers a request to FHEM Module to get the data from Database
*/
requestChartData: function(stepchangecalled) {
var me = this;
//show loadmask
me.getHighchartpanel().setLoading(true);
// fit chart
me.resizeChart();
//cleanup
hc = Ext.ComponentQuery.query('highchart')[0];
hc.store.removeAll();
hc.refresh();
//getting the necessary values
var devices = Ext.ComponentQuery.query('combobox[name=highchartdevicecombo]'),
yaxes = Ext.ComponentQuery.query('combobox[name=highchartyaxiscombo]'),
yaxescolorcombos = Ext.ComponentQuery.query('combobox[name=highchartyaxiscolorcombo]'),
yaxesfillchecks = Ext.ComponentQuery.query('checkbox[name=highchartyaxisfillcheck]'),
yaxesstepcheck = Ext.ComponentQuery.query('checkbox[name=highchartyaxisstepcheck]'),
yaxesstatistics = Ext.ComponentQuery.query('combobox[name=highchartyaxisstatisticscombo]'),
axissideradio = Ext.ComponentQuery.query('radiogroup[name=highchartaxisside]');
var starttime = me.getStarttimepicker().getValue(),
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s'),
endtime = me.getEndtimepicker().getValue(),
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s'),
dynamicradio = Ext.ComponentQuery.query('radiogroup[name=highchartdynamictime]')[0],
chartpanel = me.getHighchartpanel(),
chart = me.getChart();
//check if timerange or dynamic time should be used
dynamicradio.eachBox(function(box, idx){
var date = new Date();
if (box.checked && stepchangecalled !== true) {
if (box.inputValue === "year") {
starttime = Ext.Date.parse(date.getUTCFullYear() + "-01-01", "Y-m-d");
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
endtime = Ext.Date.parse(date.getUTCFullYear() + 1 + "-01-01", "Y-m-d");
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s');
} else if (box.inputValue === "month") {
starttime = Ext.Date.getFirstDateOfMonth(date);
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
endtime = Ext.Date.getLastDateOfMonth(date);
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s');
} else if (box.inputValue === "week") {
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
//monday starts with 0 till sat with 5, sund with -1
var dayoffset = date.getDay() - 1,
monday,
nextmonday;
if (dayoffset >= 0) {
monday = Ext.Date.add(date, Ext.Date.DAY, -dayoffset);
} else {
//we have a sunday
monday = Ext.Date.add(date, Ext.Date.DAY, -6);
}
nextmonday = Ext.Date.add(monday, Ext.Date.DAY, 7);
starttime = monday;
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
endtime = nextmonday;
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s');
} else if (box.inputValue === "day") {
date.setHours(0);
date.setMinutes(0);
date.setSeconds(0);
starttime = date;
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
endtime = Ext.Date.add(date, Ext.Date.DAY, 1);
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s');
} else if (box.inputValue === "hour") {
date.setMinutes(0);
date.setSeconds(0);
starttime = date;
dbstarttime = Ext.Date.format(starttime, 'Y-m-d_H:i:s');
endtime = Ext.Date.add(date, Ext.Date.HOUR, 1);
dbendtime = Ext.Date.format(endtime, 'Y-m-d_H:i:s');
} else {
Ext.Msg.alert("Error", "Could not setup the dynamic time.");
}
me.getStarttimepicker().setValue(starttime);
me.getEndtimepicker().setValue(endtime);
}
});
var i = 0;
Ext.each(yaxes, function(y) {
var device = devices[i].getValue(),
yaxis = yaxes[i].getValue(),
yaxiscolorcombo = yaxescolorcombos[i].getValue(),
yaxisfillcheck = yaxesfillchecks[i].checked,
yaxisstepcheck = yaxesstepcheck[i].checked,
yaxisstatistics = yaxesstatistics[i].getValue(),
axisside = axissideradio[i].getChecked()[0].getSubmitValue();
if(yaxis === "" || yaxis === null) {
yaxis = yaxes[i].getRawValue();
}
me.populateAxis(i, yaxes.length, device, yaxis, yaxiscolorcombo, yaxisfillcheck, yaxisstepcheck, axisside, yaxisstatistics, dbstarttime, dbendtime);
i++;
});
},
/**
* resize the chart to fit the centerpanel
*/
resizeChart: function() {
var lcp = Ext.ComponentQuery.query('highchartspanel')[0];
var lcv = Ext.ComponentQuery.query('panel[name=highchartpanel]')[0];
var cfp = Ext.ComponentQuery.query('form[name=highchartformpanel]')[0];
if (lcp && lcv && cfp) {
var chartheight = lcp.getHeight() - cfp.getHeight() -55;
var chartwidth = lcp.getWidth() - 25;
lcv.setHeight(chartheight);
lcv.setWidth(chartwidth);
lcv.down('highchart').setHeight(chartheight);
lcv.down('highchart').setWidth(chartwidth);
lcv.down('highchart').render();
}
},
/**
* fill the axes with data
*/
populateAxis: function(i, axeslength, device, yaxis, yaxiscolorcombo, yaxisfillcheck, yaxisstepcheck, axisside, yaxisstatistics, dbstarttime, dbendtime) {
var me = this,
yseries,
generalization = Ext.ComponentQuery.query('radio[boxLabel=active]')[0],
generalizationfactor = Ext.ComponentQuery.query('combobox[name=highchartgenfactor]')[0].getValue();
var url;
if (!Ext.isDefined(yaxisstatistics) || yaxisstatistics === "none" || Ext.isEmpty(yaxisstatistics)) {
url += '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+';
url +=device + '+timerange+' + "TIMESTAMP" + '+' + yaxis;
url += '&XHR=1';
} else { //setup url to get statistics
url += '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+' + dbstarttime + '+' + dbendtime + '+';
url +=device;
if (yaxisstatistics.indexOf("hour") === 0) {
url += '+hourstats+';
} else if (yaxisstatistics.indexOf("day") === 0) {
url += '+daystats+';
} else if (yaxisstatistics.indexOf("week") === 0) {
url += '+weekstats+';
} else if (yaxisstatistics.indexOf("month") === 0) {
url += '+monthstats+';
} else if (yaxisstatistics.indexOf("year") === 0) {
url += '+yearstats+';
}
url += 'TIMESTAMP' + '+' + yaxis;
url += '&XHR=1';
}
Ext.Ajax.request({
method: 'GET',
async: false,
disableCaching: false,
url: url,
success: function(response){
var json = Ext.decode(response.responseText);
if (json.success && json.success === "false") {
Ext.Msg.alert("Error", "Error an adding Y-Axis number " + i + ", error was: <br>" + json.msg);
} else {
hc = Ext.ComponentQuery.query('highchart')[0];
hc.store.add(json.data);
}
},
failure: function() {
Ext.Msg.alert("Error", "Error an adding Y-Axis number " + i);
}
});
//check if we have added the last dataset
if ((i + 1) === axeslength) {
me.getHighchartpanel().setLoading(false);
}
},
/**
* reset the form fields e.g. when loading a new chart
*/
resetFormFields: function() {
var fieldset = this.getChartformpanel().down('fieldset[name=highchartaxesfieldset]');
fieldset.removeAll();
this.getHighchartpanel().createNewYAxis();
Ext.ComponentQuery.query('radiofield[name=highchartrb]')[0].setValue(true);
Ext.ComponentQuery.query('datefield[name=highchartstarttimepicker]')[0].reset();
Ext.ComponentQuery.query('datefield[name=highchartendtimepicker]')[0].reset();
Ext.ComponentQuery.query('radiofield[name=highchartgeneralization]')[1].setValue(true);
}
});

View File

@ -4,8 +4,7 @@
Ext.define('FHEM.controller.MainController', { Ext.define('FHEM.controller.MainController', {
extend: 'Ext.app.Controller', extend: 'Ext.app.Controller',
requires: [ requires: [
'FHEM.view.DevicePanel', 'FHEM.view.DevicePanel'
'FHEM.view.HighChartsPanel'
], ],
refs: [ refs: [
@ -49,9 +48,6 @@ Ext.define('FHEM.controller.MainController', {
'panel[name=tabledataaccordionpanel]': { 'panel[name=tabledataaccordionpanel]': {
expand: this.showDatabaseTablePanel expand: this.showDatabaseTablePanel
}, },
'panel[name=highchartsaccordionpanel]': {
expand: this.showHighChartsPanel
},
'treepanel[name=maintreepanel]': { 'treepanel[name=maintreepanel]': {
itemclick: this.showDevicePanel itemclick: this.showDevicePanel
}, },
@ -97,7 +93,7 @@ Ext.define('FHEM.controller.MainController', {
if (Ext.isDefined(FHEM.version)) { if (Ext.isDefined(FHEM.version)) {
var sp = this.getStatustextfield(); var sp = this.getStatustextfield();
sp.setText(FHEM.version + "; Frontend Version: 0.61 - 2013-04-27"); sp.setText(FHEM.version + "; Frontend Version: 0.7 - 2013-05-01");
} }
//setup west accordion / treepanel //setup west accordion / treepanel
@ -430,29 +426,6 @@ Ext.define('FHEM.controller.MainController', {
duration: 500, duration: 500,
remove: false remove: false
}); });
}, }
showHighChartsPanel: function() {
var panel = {
xtype: 'highchartspanel',
name: 'highchartspanel',
region: 'center',
layout: 'fit'
};
this.destroyCenterPanels();
this.getMainviewport().add(panel);
// var createdpanel = this.getMainviewport().down('tabledatagridpanel');
//
// createdpanel.getEl().setOpacity(0);
// createdpanel.show();
//
// createdpanel.getEl().animate({
// opacity: 1,
// easing: 'easeIn',
// duration: 1500,
// remove: false
// });
}
}); });

View File

@ -1,524 +0,0 @@
/**
* A Panel containing device specific information
*/
Ext.define('FHEM.view.HighChartsPanel', {
extend : 'Ext.panel.Panel',
alias : 'widget.highchartspanel',
requires: [
'Chart.ux.Highcharts',
'Chart.ux.Highcharts.Serie',
'Chart.ux.Highcharts.SplineSerie',
'FHEM.store.DeviceStore',
'FHEM.store.ReadingsStore',
'Ext.form.Panel',
'Ext.form.field.Radio',
'Ext.form.field.Date',
'Ext.form.RadioGroup'
],
/**
* generating getters and setters
*/
config: {
/**
*
*/
axiscounter: 0
},
/**
*
*/
title : 'Highcharts',
/**
* init function
*/
initComponent : function() {
var me = this;
var chartSettingPanel = Ext.create('Ext.form.Panel', {
title: 'HighChart Settings - Click me to edit',
name: 'highchartformpanel',
maxHeight: 230,
autoScroll: true,
collapsible: true,
titleCollapse: true,
items: [
{
xtype: 'fieldset',
layout: 'column',
title: 'Select data',
name: 'highchartaxesfieldset',
defaults: {
margin: '0 10 10 10'
},
items: [] //get filled in own function
},
{
xtype: 'fieldset',
layout: 'column',
title: 'Select Timerange',
defaults: {
margin: '0 0 0 10'
},
items: [
{
xtype: 'radiofield',
fieldLabel: 'Timerange',
labelWidth: 60,
name: 'highchartrb',
checked: true,
inputValue: 'timerange',
listeners: {
change: function(rb, newval, oldval) {
if (newval === false) {
rb.up().down('datefield[name=highchartstarttimepicker]').setDisabled(true);
rb.up().down('datefield[name=highchartendtimepicker]').setDisabled(true);
} else {
rb.up().down('datefield[name=highchartstarttimepicker]').setDisabled(false);
rb.up().down('datefield[name=highchartendtimepicker]').setDisabled(false);
}
}
}
},
{
xtype: 'datefield',
name: 'highchartstarttimepicker',
format: 'Y-m-d H:i:s',
fieldLabel: 'Starttime',
labelWidth: 70
},
{
xtype: 'datefield',
name: 'highchartendtimepicker',
format: 'Y-m-d H:i:s',
fieldLabel: 'Endtime',
labelWidth: 70
},
{
xtype: 'radiogroup',
name: 'highchartdynamictime',
fieldLabel: 'or select a dynamic time',
labelWidth: 140,
allowBlank: true,
defaults: {
labelWidth: 42,
padding: "0 25px 0 0",
checked: false
},
items: [
{ fieldLabel: 'yearly', name: 'highchartrb', inputValue: 'year' },
{ fieldLabel: 'monthly', name: 'highchartrb', inputValue: 'month' },
{ fieldLabel: 'weekly', name: 'highchartrb', inputValue: 'week' },
{ fieldLabel: 'daily', name: 'highchartrb', inputValue: 'day' },
{ fieldLabel: 'hourly', name: 'highchartrb', inputValue: 'hour' }
]
}
]
},
{
xtype: 'fieldset',
layout: 'column',
defaults: {
margin: '10 10 10 10'
},
items: [
{
xtype: 'button',
width: 100,
text: 'Show Chart',
name: 'highchartrequestchartdata',
icon: 'app/resources/icons/accept.png'
},
{
xtype: 'button',
width: 100,
text: 'Save Chart',
disabled: true,
name: 'highchartsavechartdata',
icon: 'app/resources/icons/database_save.png'
},
{
xtype: 'button',
width: 100,
text: 'Reset Fields',
name: 'highchartresetchartform',
icon: 'app/resources/icons/delete.png'
},
{
xtype: 'radio',
width: 160,
fieldLabel: 'Generalization',
disabled: true,
boxLabel: 'active',
name: 'highchartgeneralization',
listeners: {
change: function(radio, state) {
if (state) {
radio.up().down('combobox[name=highchartgenfactor]').setDisabled(false);
} else {
radio.up().down('combobox[name=highchartgenfactor]').setDisabled(true);
}
}
}
},
{
xtype: 'radio',
width: 80,
boxLabel: 'disabled',
disabled: true,
checked: true,
name: 'highchartgeneralization'
},
{
xtype: 'combo',
width: 120,
name: 'highchartgenfactor',
disabled: true,
fieldLabel: 'Factor',
labelWidth: 50,
store: Ext.create('Ext.data.Store', {
fields: ['displayval', 'val'],
data : [
{"displayval": "10%", "val":"10"},
{"displayval": "20%", "val":"20"},
{"displayval": "30%", "val":"30"},
{"displayval": "40%", "val":"40"},
{"displayval": "50%", "val":"50"},
{"displayval": "60%", "val":"60"},
{"displayval": "70%", "val":"70"},
{"displayval": "80%", "val":"80"},
{"displayval": "90%", "val":"90"}
]
}),
fields: ['displayval', 'val'],
displayField: 'displayval',
valueField: 'val',
value: '30'
}
]
}
]
});
Ext.define('HighChartData', {
extend : 'Ext.data.Model',
fields : [ {
name : 'TIMESTAMP',
type : 'string'
}, {
name : 'VALUE',
type : 'float'
}]
});
var store = Ext.create('Ext.data.Store', {
model : 'HighChartData',
data: [{}]
});
me.callParent(arguments);
//listener used to get correct rendering dimensions
me.on("afterrender", function() {
me.add(chartSettingPanel);
//add the first yaxis line
me.createNewYAxis();
var chartpanel = Ext.create('Ext.panel.Panel', {
title : 'Highchart',
name: 'highchartpanel',
collapsible: true,
titleCollapse: true,
layout : 'fit',
items : [ {
xtype : 'highchart',
id : 'chart',
defaultSeriesType : 'spline',
series : [ {
type : 'spline',
dataIndex : 'VALUE',
name : 'VALUE',
visible : true
}],
store : store,
xField : 'TIMESTAMP',
chartConfig : {
chart : {
marginRight : 130,
marginBottom : 120,
zoomType : 'x',
animation : {
duration : 1500,
easing : 'swing'
}
},
title : {
text : 'Highcharts Testing',
x : -20
},
xAxis : [ {
title : {
text : 'Timestamp',
margin : 20
},
type: 'datetime',
tickInterval : 40 ,
labels : {
rotation : 315,
y : 45
// formatter : function() {
// if (typeof this.value == 'string') {
// var dt = Ext.Date.parse(
// parseInt(this.value) / 1000, "U");
// return Ext.Date.format(dt, "H:i:s");
// } else {
// return this.value;
// }
// }
}
} ],
yAxis : {
title : {
text : 'Value'
},
plotLines : [ {
value : 0,
width : 1,
color : '#808080'
} ]
},
plotOptions : {
series : {
animation : {
duration : 2000,
easing : 'swing'
}
}
},
tooltip : {
formatter : function() {
return '<b>' + this.series.name + '</b><br/>'
+ this.x + ': ' + this.y;
}
},
legend : {
layout : 'vertical',
align : 'right',
verticalAlign : 'top',
x : -10,
y : 100,
borderWidth : 0
}
}
} ]
});
me.add(chartpanel);
});
},
/**
* create a new fieldset for a new chart y axis
*/
createNewYAxis: function() {
var me = this;
me.setAxiscounter(me.getAxiscounter() + 1);
var components =
{
xtype: 'fieldset',
name: 'highchartsinglerowfieldset',
layout: 'column',
defaults: {
margin: '5 5 5 0'
},
items:
[
{
xtype: 'combobox',
name: 'highchartdevicecombo',
fieldLabel: 'Select Device',
labelWidth: 90,
store: Ext.create('FHEM.store.DeviceStore', {
proxy: {
type: 'ajax',
method: 'POST',
url: '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+""+getdevices&XHR=1',
reader: {
type: 'json',
root: 'data',
totalProperty: 'totalCount'
}
},
autoLoad: false
}),
displayField: 'DEVICE',
valueField: 'DEVICE',
listeners: {
select: function(combo) {
var device = combo.getValue(),
readingscombo = combo.up().down('combobox[name=highchartyaxiscombo]'),
readingsstore = readingscombo.getStore(),
readingsproxy = readingsstore.getProxy();
readingsproxy.url = '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+' + device + '+getreadings&XHR=1';
readingsstore.load();
readingscombo.setDisabled(false);
}
}
},
{
xtype: 'combobox',
name: 'highchartyaxiscombo',
fieldLabel: 'Select Y-Axis',
disabled: true,
labelWidth: 90,
inputWidth: 110,
store: Ext.create('FHEM.store.ReadingsStore', {
proxy: {
type: 'ajax',
method: 'POST',
url: '../../../fhem?cmd=get+' + FHEM.dblogname + '+-+webchart+""+""+-+getreadings&XHR=1',
reader: {
type: 'json',
root: 'data',
totalProperty: 'totalCount'
}
},
autoLoad: false
}),
displayField: 'READING',
valueField: 'READING'
},
{
xtype: 'combobox',
name: 'highchartyaxiscolorcombo',
fieldLabel: 'Y-Color',
disabled: true,
labelWidth: 50,
inputWidth: 70,
store: Ext.create('Ext.data.Store', {
fields: ['name', 'value'],
data : [
{'name':'Blue','value':'#2F40FA'},
{'name':'Green', 'value':'#46E01B'},
{'name':'Orange','value':'#F0A800'},
{'name':'Red','value':'#E0321B'},
{'name':'Yellow','value':'#F5ED16'}
]
}),
displayField: 'name',
valueField: 'value',
value: '#2F40FA'
},
{
xtype: 'checkboxfield',
disabled: true,
name: 'highchartyaxisfillcheck',
boxLabel: 'Fill'
},
{
xtype: 'checkboxfield',
disabled: true,
name: 'highchartyaxisstepcheck',
boxLabel: 'Steps',
tooltip: 'Check, if the chart should be shown with steps instead of a linear Line'
},
{
xtype: 'radiogroup',
disabled: true,
name: 'highchartaxisside',
allowBlank: false,
border: true,
defaults: {
padding: "0 15px 0 0",
checked: false
},
items: [
{ labelWidth: 50, fieldLabel: 'Left Axis', name: 'highchartrbc' + me.getAxiscounter(), inputValue: 'left', checked: true },
{ labelWidth: 60, fieldLabel: 'Right Axis', name: 'highchartrbc' + me.getAxiscounter(), inputValue: 'right' }
]
},
{
xtype: 'combobox',
name: 'highchartyaxisstatisticscombo',
fieldLabel: 'Statistics',
disabled: true,
labelWidth: 70,
inputWidth: 120,
store: Ext.create('Ext.data.Store', {
fields: ['name', 'value'],
data : [
{'name':'None','value':'none'},
{'name':'Hour Sum', 'value':'hoursum'},
{'name':'Hour Average', 'value':'houraverage'},
{'name':'Hour Min','value':'hourmin'},
{'name':'Hour Max','value':'hourmax'},
{'name':'Hour Count','value':'hourcount'},
{'name':'Day Sum', 'value':'daysum'},
{'name':'Day Average', 'value':'dayaverage'},
{'name':'Day Min','value':'daymin'},
{'name':'Day Max','value':'daymax'},
{'name':'Day Count','value':'daycount'},
{'name':'Week Sum', 'value':'weeksum'},
{'name':'Week Average', 'value':'weekaverage'},
{'name':'Week Min','value':'weekmin'},
{'name':'Week Max','value':'weekmax'},
{'name':'Week Count','value':'weekcount'},
{'name':'Month Sum', 'value':'monthsum'},
{'name':'Month Average', 'value':'monthaverage'},
{'name':'Month Min','value':'monthmin'},
{'name':'Month Max','value':'monthmax'},
{'name':'Month Count','value':'monthcount'},
{'name':'Year Sum', 'value':'yearsum'},
{'name':'Year Average', 'value':'yearaverage'},
{'name':'Year Min','value':'yearmin'},
{'name':'Year Max','value':'yearmax'},
{'name':'Year Count','value':'yearcount'}
]
}),
displayField: 'name',
valueField: 'value',
value: 'none'
},
{
xtype: 'button',
disabled: true,
width: 110,
text: 'Add another Y-Axis',
name: 'highchartaddyaxisbtn',
handler: function(btn) {
me.createNewYAxis();
}
},
{
xtype: 'button',
disabled: true,
width: 90,
text: 'Add Baseline',
name: 'highchartaddbaselinebtn',
handler: function(btn) {
me.createNewBaseLineFields(btn);
}
}
]
};
Ext.ComponentQuery.query('fieldset[name=highchartaxesfieldset]')[0].add(components);
}
});

View File

@ -91,7 +91,7 @@ Ext.define('FHEM.view.LineChartPanel', {
var chartSettingPanel = Ext.create('Ext.form.Panel', { var chartSettingPanel = Ext.create('Ext.form.Panel', {
title: 'Chart Settings - Click me to edit', title: 'Chart Settings - Click me to edit',
name: 'chartformpanel', name: 'chartformpanel',
maxHeight: 230, maxHeight: 270,
autoScroll: true, autoScroll: true,
collapsible: true, collapsible: true,
titleCollapse: true, titleCollapse: true,
@ -171,6 +171,107 @@ Ext.define('FHEM.view.LineChartPanel', {
} }
] ]
}, },
{
xtype: 'fieldset',
layout: 'column',
title: 'Axis Configuration',
defaults: {
margin: '0 0 0 10'
},
items: [
{
xtype: 'radiogroup',
name: 'leftaxisconfiguration',
fieldLabel: 'Left Axis Scalerange',
labelWidth: 120,
allowBlank: true,
defaults: {
labelWidth: 55,
padding: "0 25px 0 0",
checked: false
},
items: [
{ fieldLabel: 'automatic', name: 'rb1', inputValue: 'automatic', checked: true },
{ fieldLabel: 'manual', name: 'rb1', inputValue: 'manual' }
],
listeners: {
change: function(rb1, newval, oldval) {
if (newval.rb1 === "automatic") {
rb1.up().down('numberfield[name=leftaxisminimum]').setDisabled(true);
rb1.up().down('numberfield[name=leftaxismaximum]').setDisabled(true);
} else {
rb1.up().down('numberfield[name=leftaxisminimum]').setDisabled(false);
rb1.up().down('numberfield[name=leftaxismaximum]').setDisabled(false);
}
}
}
},
{
xtype: 'numberfield',
fieldLabel: 'Minimum',
name: 'leftaxisminimum',
allowBlank: false,
disabled: true,
labelWidth: 60,
width: 120
},
{
xtype: 'numberfield',
fieldLabel: 'Maximum',
name: 'leftaxismaximum',
allowBlank: false,
disabled: true,
labelWidth: 60,
width: 120
},
{
xtype: 'radiogroup',
name: 'rightaxisconfiguration',
fieldLabel: 'Right Axis Scalerange',
labelWidth: 130,
allowBlank: true,
defaults: {
labelWidth: 55,
padding: "0 25px 0 0",
checked: false
},
items: [
{ fieldLabel: 'automatic', name: 'rb2', inputValue: 'automatic', checked: true },
{ fieldLabel: 'manual', name: 'rb2', inputValue: 'manual' }
],
listeners: {
change: function(rb2, newval, oldval) {
if (newval.rb2 === "automatic") {
rb2.up().down('numberfield[name=rightaxisminimum]').setDisabled(true);
rb2.up().down('numberfield[name=rightaxismaximum]').setDisabled(true);
} else {
rb2.up().down('numberfield[name=rightaxisminimum]').setDisabled(false);
rb2.up().down('numberfield[name=rightaxismaximum]').setDisabled(false);
}
}
}
},
{
xtype: 'numberfield',
fieldLabel: 'Minimum',
name: 'rightaxisminimum',
allowBlank: false,
disabled: true,
labelWidth: 60,
width: 120
},
{
xtype: 'numberfield',
fieldLabel: 'Maximum',
name: 'rightaxismaximum',
allowBlank: false,
disabled: true,
labelWidth: 60,
width: 120
}
]
},
{ {
xtype: 'fieldset', xtype: 'fieldset',
layout: 'column', layout: 'column',

View File

@ -28,20 +28,22 @@ Ext.define('FHEM.view.Viewport', {
items: [ items: [
{ {
region: 'north', region: 'north',
height: 85, height: 45,
layout: 'hbox', layout: 'hbox',
items: [ items: [
{ {
xtype: 'panel', xtype: 'container',
html: '<p><img src="../../fhem/images/default/fhemicon.png" height="70px"</></p><h1 class="x-panel-header">Frontend</h1>', //html: '<p><img src="../../fhem/images/default/fhemicon.png" height="40px"</></p><h1 class="x-panel-header">Frontend</h1>',
html: 'FHEM Webfrontend',
width: '25%', width: '25%',
padding: '15px 0 0 5px',
border: false border: false
}, },
{ {
xtype: 'textfield', xtype: 'textfield',
name: 'commandfield', name: 'commandfield',
width: '30%', width: '30%',
padding: '30px 0 0 0', padding: '10px 0 0 0',
fieldLabel: 'Send Commands', fieldLabel: 'Send Commands',
border: false border: false
}, },
@ -53,7 +55,7 @@ Ext.define('FHEM.view.Viewport', {
{ {
xtype: 'button', xtype: 'button',
width: 80, width: 80,
margin: '30px 0 0 5px', margin: '10px 0 0 5px',
text: 'Execute', text: 'Execute',
name: 'executecommand', name: 'executecommand',
icon: 'app/resources/icons/arrow_left.png' icon: 'app/resources/icons/arrow_left.png'
@ -61,7 +63,7 @@ Ext.define('FHEM.view.Viewport', {
{ {
xtype: 'button', xtype: 'button',
width: 110, width: 110,
margin: '30px 0 0 5px', margin: '10px 0 0 5px',
text: 'Save to Config', text: 'Save to Config',
name: 'saveconfig', name: 'saveconfig',
icon: 'app/resources/icons/database_save.png' icon: 'app/resources/icons/database_save.png'
@ -76,7 +78,7 @@ Ext.define('FHEM.view.Viewport', {
{ {
xtype: 'button', xtype: 'button',
width: 75, width: 75,
margin: '30px 5px 0 5px', margin: '10px 5px 0 5px',
text: 'Shutdown', text: 'Shutdown',
name: 'shutdownfhem', name: 'shutdownfhem',
tooltip: 'Shutdown FHEM', tooltip: 'Shutdown FHEM',
@ -85,7 +87,7 @@ Ext.define('FHEM.view.Viewport', {
{ {
xtype: 'button', xtype: 'button',
width: 70, width: 70,
margin: '30px 5px 0 5px', margin: '10px 5px 0 5px',
text: 'Restart', text: 'Restart',
name: 'restartfhem', name: 'restartfhem',
tooltip: 'Restart FHEM', tooltip: 'Restart FHEM',
@ -160,12 +162,6 @@ Ext.define('FHEM.view.Viewport', {
title: 'Database Tables', title: 'Database Tables',
name: 'tabledataaccordionpanel', name: 'tabledataaccordionpanel',
autoScroll: true autoScroll: true
},
{
xtype: 'panel',
title: 'Highcharts',
name: 'highchartsaccordionpanel',
autoScroll: true
} }
] ]
}, },

View File

@ -21,8 +21,6 @@
<link rel="stylesheet" type="text/css" href="lib/ext-4.2.0.663/ext-theme-gray-all.css" /> <link rel="stylesheet" type="text/css" href="lib/ext-4.2.0.663/ext-theme-gray-all.css" />
<script type="text/javascript" src="lib/ext-4.2.0.663/ext-all.js"></script> <script type="text/javascript" src="lib/ext-4.2.0.663/ext-all.js"></script>
<script type="text/javascript" src="lib/jquery/jquery.min.js"></script>
<script type="text/javascript" src="lib/highcharts/highcharts.src.js"></script>
<script type="text/javascript" src="app/userconfig.js"></script> <script type="text/javascript" src="app/userconfig.js"></script>
<script type="text/javascript" src="app/app.js"></script> <script type="text/javascript" src="app/app.js"></script>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +0,0 @@
/**
* Serie class for area range series type
*
* See {@link Chart.ux.Highcharts.RangeSerie} class for more info
*
*/
Ext.define('Chart.ux.Highcharts.AreaRangeSerie', {
extend : 'Chart.ux.Highcharts.RangeSerie',
alternateClassName: [ 'highcharts.arearange' ],
type : 'arearange'
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for area line series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.AreaSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.area' ],
type : 'area'
});

View File

@ -1,11 +0,0 @@
/**
* Serie class for area spline range series type
*
* See {@link Chart.ux.Highcharts.RangeSerie} class for more info
*
*/
Ext.define('Chart.ux.Highcharts.AreaSplineRangeSerie', {
extend : 'Chart.ux.Highcharts.RangeSerie',
alternateClassName: [ 'highcharts.areasplinerange' ],
type : 'areasplinerange'
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for area spline series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.AreaSplineSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.areaspline' ],
type : 'areaspline'
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for bar series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.BarSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.bar' ],
type : 'bar'
});

View File

@ -1,50 +0,0 @@
/**
* Serie class for BoxPlot series type
*
* See {@link Chart.ux.Highcharts.RangeSerie} class for more info
*
* Here is an example of BoxPlot series config:
* series: [{
* type: 'boxplot',
* minDataIndex: 'min',
* lowQtrDataIndex: 'q1',
* medianDataIndex: 'med',
* highQtrDataIndex: 'q2',
* maxDataIndex: 'max',
* xField: 'date'
* }]
*
*/
Ext.define('Chart.ux.Highcharts.BoxPlotSerie', {
extend : 'Chart.ux.Highcharts.RangeSerie',
alternateClassName: [ 'highcharts.boxplot' ],
type : 'boxplot',
/**
* @cfg {String} lowQtrDataIndex
* The low Quartile data field
*/
lowQtrDataIndex: null,
/**
* @cfg {String} highQtrDataIndex
* The high Quartile data field
*/
highQtrDataIndex: null,
/**
* @cfg {String} medianDataIndex
* The median data field
*/
medianDataIndex: null,
getData: function(record, index) {
return [
record.data[ this.minDataIndex ],
record.data[ this.lowQtrDataIndex ],
record.data[ this.medianDataIndex ],
record.data[ this.highQtrDataIndex ],
record.data[ this.maxDataIndex ]
];
}
});

View File

@ -1,65 +0,0 @@
/**
* Serie class for bubble type series
*
* The bubble series support two types of data input
*
* # Single Bubble Series
* For single bubble series, the series can be specified as
* series: [{
* xField: 'x',
* yField: 'y',
* radiusField: 'r'
* type: 'bubble'
* }]
*
* # Single / Multiple Bubble Series
* For single/multiple bubble series, the series should be specified as
* the Irregular data example, i.e.
* series: [{
* type: 'bubble',
* dataIndex: 'series1'
* }, {
* type: 'bubble',
* dataIndex: 'series2'
* }]
*
* The Json data returning from the server side should looking like the following:
* 'root': [{
* 'series1': [ [ 97,36,79],[94,74,60],[68,76,58], .... ] ],
* 'series2': [ [25,10,87],[2,75,59],[11,54,8],[86,55,93] .... ] ],
* }]
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.BubbleSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.bubble' ],
type : 'bubble',
/**
* @cfg {String} radiusField
* The field stores the radius value of a bubble data point
*/
radiusField : null,
/***
* @cfg {Array} dataIndex
* dataIndex should be used for specifying mutliple bubble series, i.e.
* the server side returns an array of truples which has values of [ x, y, r ]
*/
dataIndex: null,
/***
* @private
* each data point in the series is represented in it's own x and y values
*/
arr_getDataPair: function(record, index) {
return [
record.data[ this.xField ],
record.data[ this.yField ],
record.data[ this.radiusField ]
];
}
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for column range series type
*
* See {@link Chart.ux.Highcharts.RangeSerie} class for more info
*/
Ext.define('Chart.ux.Highcharts.ColumnRangeSerie', {
extend : 'Chart.ux.Highcharts.RangeSerie',
alternateClassName: [ 'highcharts.columnrange' ],
type : 'columnrange'
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for column series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.ColumnSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.column' ],
type : 'column'
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for error bar series type
*
* See {@link Chart.ux.Highcharts.RangeSerie} class for more info
*/
Ext.define('Chart.ux.Highcharts.ErrorBarSerie', {
extend : 'Chart.ux.Highcharts.RangeSerie',
alternateClassName: [ 'highcharts.errorbar' ],
type : 'errorbar'
});

View File

@ -1,41 +0,0 @@
/**
* Serie class for Funnel series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*
* Example of series config:
*
* series: [{
* type: 'funnel',
* // or xField
* categorieField: 'category',
* yField: 'value',
* }]
*
* **Note**: You must load Highcharts module http://code.highcharts.com/modules/funnel.js in
* your HTML file, otherwise you get unknown series type error
*/
Ext.define('Chart.ux.Highcharts.FunnelSerie', {
extend : 'Chart.ux.Highcharts.WaterfallSerie',
alternateClassName: [ 'highcharts.funnel' ],
type : 'funnel',
/**
* @cfg sumTypeField
* @hide
*/
getData: function(record, index) {
var dataObj = {
y: record.data[ this.valField ],
name: record.data[ this.nameField ]
};
// Only define color if there is value, otherwise it column
// won't take any global color definitiion
record.data [ this.colorField ] && (dataObj.color = record.data[this.colorField]);
return dataObj;
}
});

View File

@ -1,17 +0,0 @@
/**
* Serie class for gauge series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*
* Gauge series is a one dimensional series type, i.e only y-axis data
*/
Ext.define('Chart.ux.Highcharts.GaugeSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.gauge' ],
type : 'gauge'
/***
* @cfg xField
* @hide
*/
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for line series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.LineSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.line' ],
type : 'line'
});

View File

@ -1,301 +0,0 @@
/**
* # Plotting Pie Series
* There are two ways to plot pie chart from record data: a data point per record and
* total values of all the records
*
* ## Data point per record
* Pie series uses two options for mapping category name and data fields:
* *categoryField* and *dataField*, (This is historical reason instead of
* using *xField* and *dataIndex*). Suppose we have data model in the following format:
*
* <table>
* <tbody>
* <tr><td>productName</td><td>sold</td></tr>
* <tr><td>Product A</td><td>15,645,242</td></tr>
* <tr><td>Product B</td><td>22,642,358</td></tr>
* <tr><td>Product C</td><td>21,432,330</td></tr>
* </tbody>
* </table>
* Then we can define the series data as:
*
* series: [{
* type: 'pie',
* categoryField: 'productName',
* dataField: 'sold'
* }]
*
* # Data point as total value of all the records
* Instead of mapping *dataField* and *categorieField* fields to the store record for each
* pie data point, this approach uses the total value of a category as a data point.
* E.g. we have a class of pupils with a set of subject scores
* <table>
* <tbody>
* <tr><td>Name</td><td>English</td><td>Math</td><td>Science</td></tr>
* <tr><td>Joe</td><td>77</td><td>81</td><td>78</td></tr>
* <tr><td>David</td><td>67</td><td>56</td><td>69</td><tr>
* <tr><td>Nora</td><td>44</td><td>50</td><td>39</td><tr>
* </tbody>
* </table>
* All we want is to plot distribution of total scores for each subject. Hence, we define
* the pie series as follows:
* series: [{
* type: 'pie',
* useTotals: true,
* column: [ 'english', 'math', 'science' ]
* }]
* whereas the server-side should return JSON data as follows:
* { "root": [{ "english": 77, "math": 81, "science": 78 },
* { "english": 67, "math": 56, "science": 69 },
* { "english": 44, "math": 50, "science": 39 },
* ..... ]
* }
* and the data model for the store is defined as follows:
* Ext.define('ExamResults', {
* extend: 'Ext.data.Model',
* fields: [
* {name: 'english', type: 'int'},
* {name: 'math', type: 'int'},
* {name: 'science', type: 'int'}
* ]
* });
*
* # Multiple Pie Series (Donut chart)
* A donut chart is really two pie series which a second pie series lay outside of the
* first series. The second series is subcategory data of the first series.
* Suppose we want to plot a more detail chart with the breakdown of sold items into regions:
* <table>
* <tbody>
* <tr><td>productName</td><td>sold</td><td>Europe</td><td>Asia</td><td>Americas</td></tr>
* <tr><td>Product A</td><td>15,645,242</td><td>10,432,542</td><td>2,425,432</td><td>2,787,268</td></tr>
* <tr><td>Product B</td><td>22,642,358</td><td>4,325,421</td><td>4,325,321</td><td>13,991,616</td></tr>
* <tr><td>Product C</td><td>21,432,330</td><td>2,427,431</td><td>6,443,234</td><td>12,561,665</td></tr>
* </tbody>
* </table>
* The data model for the donut chart store should be refined with fields: productName,
* sold and region. The rows returning from the store should look like:
* <table>
* <tbody>
* <tr> <td>productName</td> <td>sold</td> <td>region</td> </tr>
* <tr> <td>Product A</td> <td>10,432,542</td> <td>Europe</td> <td></td> </tr>
* <tr> <td>Product A</td> <td>2,425,432</td> <td>Asia</td> <td></td> </tr>
* <tr> <td>Product A</td> <td>2,787,268</td> <td>Americas</td> <td></td> </tr>
* <tr> <td>Product B</td> <td>4,325,421</td> <td>Europe</td> <td></td> </tr>
* <tr> <td>Product B</td> <td>4,325,321</td> <td>Asia</td> <td></td> </tr>
* </tbody>
* </table>
The series definition for the donut chart should look like this:
* series: [{
* // Inner pie series
* type: 'pie',
* categoryField: 'productName',
* dataField: 'sold',
* size: '60%',
* totalDataField: true
* }, {
* // Outer pie series
* type: 'pie',
* categoryField: 'region',
* dataField: 'sold',
* innerSize: '60%',
* size: '75%'
* }]
* The *totalDataField* informs the first series to take the sum of *dataField* (sold)
* on entries with the same *categoryField* value, whereas the second series displays
* a section on each region (i.e. each record). The *innerSize* is just the Highcharts
* option to make the outer pie series appear as ring form.
*
* If you want to have a fix set of colours in the outer ring along each slice, then
* you can create an extra field in your store for the color code and use the
* *colorField* option to map the field.
*/
Ext.define('Chart.ux.Highcharts.PieSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.pie' ],
type : 'pie',
/***
* @cfg xField
* @hide
*/
/***
* @cfg yField
* @hide
*/
/***
* @cfg dataIndex
* @hide
*/
/**
* @cfg {String} categorieField
* the field name mapping to store records for pie category data
*/
categorieField : null,
/**
* @cfg {Boolean} totalDataField
* See above. This is used for producing donut chart. Bascially informs
* getData method to take the total sum of dataField as the data point value
* for those records with the same matching string in the categorieField.
*/
totalDataField : false,
/**
* @cfg {String} dataField
* the field name mapping to store records for value data
*/
dataField : null,
/***
* @cfg {Boolean} useTotals
* use the total value of a categorie of all the records as a data point
*/
useTotals : false,
/***
* @cfg {Array} columns
* a list of category names that match the record fields
*/
columns : [],
constructor : function(config) {
this.callParent(arguments);
if(this.useTotals) {
this.columnData = {};
var length = this.columns.length;
for(var i = 0; i < length; i++) {
this.columnData[this.columns[i]] = 100 / length;
}
}
},
//private
addData : function(record) {
for(var i = 0; i < this.columns.length; i++) {
var c = this.columns[i];
this.columnData[c] = this.columnData[c] + record.data[c];
}
},
//private
update : function(record) {
for(var i = 0; i < this.columns.length; i++) {
var c = this.columns[i];
if(record.modified[c])
this.columnData[c] = this.columnData[c] + record.data[c] - record.modified[c];
}
},
//private
removeData : function(record, index) {
for(var i = 0; i < this.columns.length; i++) {
var c = this.columns[i];
this.columnData[c] = this.columnData[c] - record.data[c];
}
},
//private
clear : function() {
for(var i = 0; i < this.columns.length; i++) {
var c = this.columns[i];
this.columnData[c] = 0;
}
},
/***
* As the implementation of pie series is quite different to other series types,
* it is not recommended to override this method
*/
getData : function(record, seriesData) {
var _this = (Chart.ux.Highcharts.sencha.product == 't') ? this.config : this;
// Summed up the category among the series data
if(this.totalDataField) {
var found = null;
for(var i = 0; i < seriesData.length; i++) {
if(seriesData[i].name == record.data[_this.categorieField]) {
found = i;
seriesData[i].y += record.data[_this.dataField];
break;
}
}
if(found === null) {
if (this.colorField && record.data[_this.colorField]) {
seriesData.push({
name: record.data[_this.categorieField],
y: record.data[_this.dataField],
color: record.data[_this.colorField],
record: this.bindRecord ? record : null,
events: this.dataEvents
});
} else {
seriesData.push({
name: record.data[_this.categorieField],
y: record.data[_this.dataField],
record: this.bindRecord ? record : null,
events: this.dataEvents
});
}
i = seriesData.length - 1;
}
return seriesData[i];
}
if(this.useTotals) {
this.addData(record);
return [];
}
if (this.colorField && record.data[this.colorField]) {
return {
name: record.data[_this.categorieField],
y: record.data[_this.dataField],
color: record.data[_this.colorField],
record: this.bindRecord ? record : null,
events: this.dataEvents
};
} else {
return {
name: record.data[_this.categorieField],
y: record.data[_this.dataField],
record: this.bindRecord ? record : null,
events: this.dataEvents
};
}
},
getTotals : function() {
var a = new Array();
for(var i = 0; i < this.columns.length; i++) {
var c = this.columns[i];
a.push([c, this.columnData[c]]);
}
return a;
},
/***
* @private
* Build the initial data set if there are data already
* inside the store.
*/
buildInitData:function(items, data) {
// Summed up the category among the series data
var record;
var data = this.config.data = [];
if (this.config.totalDataField) {
for (var x = 0; x < items.length; x++) {
record = items[x];
this.getData(record,data);
}
} else {
for (var x = 0; x < items.length; x++) {
record = items[x];
data.push(this.getData(record));
}
}
}
});

View File

@ -1,72 +0,0 @@
/**
* Serie class for general range series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*
* This is the base class for dealing range series type. RangeSerie offers
* sorted and unsorted ways of specifying range data. If it is desired to
* plot range data that are natively in sorted manner, the series can be specified as
* series:[{
* minDataIndex: 'low',
* maxDataIndex: 'high',
* type: 'columnrange'
* }]
* As for plotting range series data that are naturally without high and low ends, do
* series:[{
* dataIndex: [ 'marketOpen', 'marketClose' ],
* type: 'columnrange'
* }]
*/
Ext.define('Chart.ux.Highcharts.RangeSerie', {
extend : 'Chart.ux.Highcharts.Serie',
/***
* @cfg {String}
* data field mapping to store record which has minimum value
*/
minDataIndex: null,
/***
* @cfg {String}
* data field mapping to store record which has maximum value
*/
maxDataIndex: null,
/***
* @private
*/
needSorting: null,
/***
* @cfg {Array}
* dataIndex in the range serie class is treated as an array of
* [ field1, field2 ] if it is defined
*/
dataIndex: null,
/***
* @cfg yField
* @hide
*/
constructor: function(config) {
if (Ext.isArray(config.dataIndex)) {
this.field1 = config.dataIndex[0];
this.field2 = config.dataIndex[1];
this.needSorting = true;
} else if (config.minDataIndex && config.maxDataIndex) {
this.minDataIndex = config.minDataIndex;
this.maxDataIndex = config.maxDataIndex;
this.needSorting = false;
}
this.callParent(arguments);
},
getData: function(record, index) {
if (this.needSorting === true) {
return (record.data[this.field1] > record.data[this.field2]) ? [ record.data[this.field2], record.data[this.field1] ] : [ record.data[this.field1], record.data[this.field2] ];
}
if (record.data[this.minDataIndex] !== undefined && record.data[this.maxDataIndex] !== undefined) {
return ([record.data[this.minDataIndex], record.data[this.maxDataIndex]]);
}
}
});

View File

@ -1,10 +0,0 @@
/**
* Serie class for scatter type series
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.ScatterSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.scatter' ],
type : 'scatter'
});

View File

@ -1,341 +0,0 @@
/***
* Serie class is the base class for all the series types. Users shouldn't use any of the
* series classes directly, they are created internally from Chart.ux.Highcharts depending on the
* series configuration.
*
* Serie class is a general class for series data representation.
* # Mapping data fields
* In the Highcharts extension, the series option is declared outside of chartConfig, so as the *xField*.
* There is a subtle difference for declaring xField outside or inside a series. For example:
*
* series:[{
* name: 'Share A',
* type: 'line',
* yField: 'sharePriceA'
* }, {
* name: 'Share B',
* type: 'line',
* yField: 'sharePriceB'
* }],
* xField: 'datetime',
* ....
* This means both series share the same categories and each series has it own set of y-values.
* In this case, the datetime field can be either string or numerical representation of date time.
* series:[{
* name: 'Share A',
* type: 'line',
* yField: 'sharePriceA',
* xField: 'datetimeA'
* }, {
* name: 'Share B',
* type: 'line',
* yField: 'sharePriceB',
* xField: 'datetimeB'
* }],
* This means both series have their own (x,y) data. In this case, the xField must refer to numerical values.
*
* # Mapping multiple series with irregular datasets
* Suppose we have 3 series with different set of data points. To map the store with the series, first
* the store is required to return Json data in the following format:
* { root: [
* series1: [ [ 1, 3 ], [ 2, 5 ], [ 7, 1 ] ],
* series2: [ [ 2, 4 ], [ 5, 7 ] ],
* series3: [ [ 1, 8 ], [ 4, 6 ], [ 5, 1 ], [ 9, 4 ] ]
* ]
* }
*
* Then use {@link Chart.ux.Highcharts.Serie#cfg-dataIndex} to map the series data array
* series: [{
* name: 'Series A',
* dataIndex: 'series1'
* }, {
* name: 'Series B',
* dataIndex: 'series2'
* }, {
* name: 'Series C',
* dataIndex: 'series3'
* }]
*/
Ext.define('Chart.ux.Highcharts.Serie', {
requires: [ 'Chart.ux.Highcharts',
'Ext.util.Observable'
],
mixins: {
observable: 'Ext.util.Observable'
},
/***
* @cfg {String} type
* Highcharts series type name. This field must be specified.
*
* Line, area, scatter and column series are the simplest form of charts
* (includes Polar) which has the simple data mappings: *dataIndex* or *yField*
* for y-axis values and xField for either x-axis category field or data point's
* x-axis coordinate.
* series: [{
* type: 'scatter',
* xField: 'xValue',
* yField: 'yValue'
* }]
*/
type : null,
/**
* @readonly
* The {@link Chart.ux.Highcharts} chart object owns this serie.
* @type Object/Chart.ux.Highcharts
*
* This can be useful with pointclick event when you need to use an Ext.Component.
* pointclick:{
* fn:function(serie,point,record,event){
* //Get parent window to replace the chart inside (me)
* var window=this.chart.up('windows');
* }
* }
* Setting the scope on the listeners at runtime can cause trouble in Highcharts on
* parsing the listener
*/
chart: null,
/**
* @private
* The default action for series point data is to use array instead of point object
* unless desired to set point particular field. This changes the default behaviour
* of getData template method
* Default: false
*
* @type Boolean
*/
pointObject: false,
/**
* @cfg {String} xField
* The field used to access the x-axis value from the items from the data
* source. Store's record
*/
xField : null,
/**
* @cfg {String} yField
* The field used to access the y-axis value from the items from the data
* source. Store's record
*/
yField : null,
/**
* @cfg {String} dataIndex can be either an alias of *yField*
* (which has higher precedence if both are defined) or mapping to store's field
* with array of data points
*/
dataIndex : null,
/**
* @cfg {String} colorField
* This field is used for setting data point color
* number or color hex in '#([0-9])'. Otherwise, the option
* is treated as a field name and the store should return
* rows with the same color field name. For column type series, if you
* want Highcharts to automatically color each data point,
* then you should use [plotOptions.column.colorByPoint][link2] option in the series config
* [link2]: http://api.highcharts.com/highcharts#plotOptions.column.colorByPoint
*/
colorField: null,
/**
* @cfg {Boolean} visible
* The field used to hide the serie initial. Defaults to true.
*/
visible : true,
clear : Ext.emptyFn,
/***
* @cfg {Boolean} updateNoRecord
* Setting this option to true will enforce the chart to clear the series if
* there is no record returned for the series
*/
updateNoRecord: false,
/***
* @private
* Resolve color based on the value of colorField
*/
resolveColor: function(colorField, record, dataPtIdx) {
var color = null;
if (colorField) {
if (Ext.isNumeric(colorField)) {
color = colorField;
} else if (Ext.isString(colorField)) {
if (/^(#)?([0-9a-fA-F]{3})([0-9a-fA-F]{3})?$/.test(colorField)) {
color = colorField;
} else {
color = record.data[colorField];
}
}
}
return color;
},
/***
* @private
* object style of getData
*/
obj_getData : function(record, index) {
var yField = this.yField || this.dataIndex, point = {
data : record.data,
y : record.data[yField]
};
this.xField && (point.x = record.data[this.xField]);
this.colorField && (point.color = this.resolveColor(this.colorField, record, index));
this.bindRecord && (point.record = record);
return point;
},
/***
* @private
* single value data version of getData - Common category, individual y-data
*/
arr_getDataSingle: function(record, index) {
return record.data[this.yField];
},
/***
* @private
* each data point in the series is represented in it's own x and y values
*/
arr_getDataPair: function(record, index) {
return [ record.data[ this.xField ], record.data[ this.yField ] ];
},
/***
* @method getData
* getData is the core mechanism for transferring from Store's record data into the series data array.
* This routine acts as a Template Method for any series class, i.e. any new series type class must
* support this method.
*
* Generally, you don't need to override this method in the config because this method is internally
* created once the serie class is instantiated. Depending on whether *xField*, *yField* and
* *colorField* are defined, the class constructor creates a *getData* method which either returns a single value,
* tuple array or a data point object. This is done for performance reason. See Highcharts API document
* [Series.addPoint][link1] for more details.
*
* If your data model requires specific data processing in the record data, then you may need to
* override this method. The return for the method must confine to the [Series.addPoint][link1]
* prototype. Note that if this method is manually defined, there is no need to define field name options
* because this can be specified inside the implementation anyway
* series: [{
* type: 'spline',
* // Return avg y values
* getData: function(record) {
* return (record.data.y1 + record.data.y2) / 2;
* }
* }],
* xField: 'time',
* ....
*
* [link1]: http://api.highcharts.com/highcharts#Series.addPoint()
*
* @param {Object} record Store's record which contains the series data at particular instance
* @param {Number} index the index value of the record inside the Store
* @return {Object|Array|Number}
*/
getData: null,
serieCls : true,
constructor : function(config) {
config.type = this.type;
if(!config.data) {
config.data = [];
}
this.mixins.observable.constructor.call(this, config);
this.addEvents(
/**
* @event pointclick
* Fires when the point of the serie is clicked.
* @param {Chart.ux.Highcharts.Serie} serie the serie where is fired
* @param {Object} point the point clicked
* @param {Ext.data.Record} record the record associated to the point
* @param {Object} evt the event param
*/
'pointclick'
);
this.config = config;
this.yField = this.yField || this.config.dataIndex;
this.bindRecord = (this.config.listeners && this.config.listeners.pointclick !== undefined);
// If Highcharts series event is already defined, then don't support this
// pointclick event
Ext.applyIf(config,{
events:{
click: Ext.bind(this.onPointClick, this)
}
});
// If colorField is defined, then we have to use data point
// as object
(this.colorField || this.bindRecord) && (this.pointObject = true);
// If getData method is already defined, then overwrite it
if (!this.getData) {
if (this.pointObject) {
this.getData = this.obj_getData;
} else if (this.xField) {
this.getData = this.arr_getDataPair;
} else {
this.getData = this.arr_getDataSingle;
}
}
},
/***
* @private
* Build the initial data set if there are data already
* inside the store.
*/
buildInitData:function(items, data) {
var chartConfig = (Chart.ux.Highcharts.sencha.product == 't') ?
this.chart.config.chartConfig : this.chart.chartConfig;
var record;
var data = this.config.data = [];
record = items[0];
if (this.dataIndex && record && Ext.isArray(record.data[this.dataIndex])) {
this.config.data = record.data[this.dataIndex];
} else {
for (var x = 0; x < items.length; x++) {
record = items[x];
// Should use the pre-constructed getData template method to extract
// record data into the data point (Array of values or Point object)
data.push(this.getData(record, x));
}
}
var xAxis = (Ext.isArray(chartConfig.xAxis)) ? chartConfig.xAxis[0] : chartConfig.xAxis;
// Build the first x-axis categories
if (this.chart.xField && (!xAxis.categories || xAxis.categories.length < items.length)) {
xAxis.categories = xAxis.categories || [];
for (var x = 0; x < items.length; x++) {
xAxis.categories.push(items[x].data[this.chart.xField]);
}
}
},
onPointClick:function(evt){
this.fireEvent('pointclick',this,evt.point,evt.point.record,evt);
},
destroy: function() {
this.clearListeners();
this.mixins.observable.destroy();
}
});

View File

@ -1,10 +0,0 @@
/***
* Serie class for spline series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*/
Ext.define('Chart.ux.Highcharts.SplineSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.spline' ],
type : 'spline'
});

View File

@ -1,75 +0,0 @@
/**
* Serie class for water fall series type
*
* See {@link Chart.ux.Highcharts.Serie} class for more info
*
* The following is the config example converted from the original
* [Highcharts waterfall demo][1]
* [1]: http://www.highcharts.com/demo/waterfall
*
* series: [{
* type: 'waterfall',
* upColor: Highcharts.getOptions().colors[2],
* color: Highcharts.getOptions().colors[3],
* categorieField: 'category',
* yField: 'value',
* colorField: 'color',
* sumTypeField: 'sum',
* dataLabels: {
* ....
* }
* }]
*
* The Json data returning from the server side should look like as follows:
*
* {"root":[{ "category":"Start","value":120000 },
* { "category":"Product Revenue","value":569000 },
* { "category":"Service Revenue","value":231000 },
* { "category":"Positive Balance","color": "#0d233a", "sum": "intermediate" },
* { "category":"Fixed Costs","value":-342000 },
* { "category":"Variable Cost","value": -233000 },
* { "category":"Balance","color": "#0d233a", "sum": "final" }
* ]}
*
*/
Ext.define('Chart.ux.Highcharts.WaterfallSerie', {
extend : 'Chart.ux.Highcharts.Serie',
alternateClassName: [ 'highcharts.waterfall' ],
type : 'waterfall',
/**
* @cfg {String} sumTypeField
* Column value is whether derived from precious values.
* Possible values: 'intermediate', 'final' or null (expect dataIndex or yField contains value)
*/
sumTypeField: null,
constructor: function(config) {
this.callParent(arguments);
this.valField = this.yField || this.dataIndex;
this.nameField = this.categorieField || this.xField;
},
getData: function(record, index) {
var dataObj = {
y: record.data[ this.valField ],
name: record.data[ this.nameField ]
};
// Only define color if there is value, otherwise it column
// won't take any global color definitiion
record.data [ this.colorField ] && (dataObj.color = record.data[this.colorField]);
if (this.sumTypeField) {
if (record.data[this.sumTypeField] == "intermediate") {
dataObj.isIntermediateSum = true;
} else if (record.data[this.sumTypeField] == "final") {
dataObj.isSum = true;
}
}
return dataObj;
}
});

View File

@ -1,13 +0,0 @@
You are permitted to use this software for non-profit organisation under:
Creative Commons Attribution-NonCommercial 3.0 License.
(http://creativecommons.org/licenses/by-nc/3.0/)
If you are using the software for commercial and federal projects,
you can obtain the permission to use and distribute free of charge as long as
1. you inform me the author (kuan.joe@gmail.com) with a short description describing
your project using this software.
2. you give acknowledgement to the author in your project
"Highcharts extension for Sencha products is developed & maintained by Joe Kuan (kuan.joe@gmail.com)"

File diff suppressed because one or more lines are too long