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:
parent
f5011972e5
commit
d80a0dbfd9
@ -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
|
||||||
|
@ -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
|
@ -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
|
||||||
|
@ -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() {
|
||||||
|
@ -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 {
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
@ -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
|
|
||||||
// });
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
});
|
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
@ -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',
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -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
@ -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'
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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 ]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
});
|
|
@ -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 ]
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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;
|
|
||||||
}
|
|
||||||
});
|
|
@ -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
|
|
||||||
*/
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
@ -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]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
@ -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'
|
|
||||||
});
|
|
@ -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;
|
|
||||||
}
|
|
||||||
});
|
|
@ -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
Loading…
x
Reference in New Issue
Block a user