mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-08 01:14:19 +00:00
- testing integration of highcharts
- added required librarys for highcharts and JQuery git-svn-id: https://svn.fhem.de/fhem/trunk@3120 2b470e98-0d58-463d-a4d8-8e2adae1ed80
This commit is contained in:
parent
61e01e1681
commit
49425c91f1
@ -1,3 +1,6 @@
|
|||||||
|
Update vom 26.4.2013
|
||||||
|
* Testintegration von Highcharts
|
||||||
|
* Neue abhängige Bibliotheken Highcharts und JQuery hinzugefügt
|
||||||
Update vom 20.4.2013
|
Update vom 20.4.2013
|
||||||
* Über dem CHart wird nun eine interaktive Tabelle mit den zugehörigen Daten angezeigt
|
* Über dem CHart wird nun eine interaktive Tabelle mit den zugehörigen Daten angezeigt
|
||||||
* Chartrendering effizienter
|
* Chartrendering effizienter
|
||||||
|
@ -29,38 +29,12 @@ 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
|
||||||
DEL www/frontend/lib/ext-4.1.1a
|
DIR www/frontend/lib/highcharts
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images
|
DIR www/frontend/lib/highcharts/ux
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray
|
DIR www/frontend/lib/highcharts/ux/Highcharts
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tools
|
DIR www/frontend/lib/jquery
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tree
|
UPD 2013-04-26_05:06:42 1065 www/frontend/index.html
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/window-header
|
UPD 2013-04-26_05:06:43 616 www/frontend/README.txt
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/form-invalid-tip
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/datepicker
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/sizer
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tab
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/toolbar
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/shared
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/btn
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/btn-group
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tab-bar
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/progress
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/boundlist
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/dd
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/box
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/form
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/menu
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/slider
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/button
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/layout
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/panel
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/window
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/grid
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/util
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/panel-header
|
|
||||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tip
|
|
||||||
UPD 2013-04-01_07:05:58 894 www/frontend/index.html
|
|
||||||
UPD 2013-02-27_07:20:39 236 www/frontend/README.txt
|
|
||||||
UPD 2013-04-01_07:05:33 613 www/frontend/app/userconfig.js
|
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-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 626 www/frontend/app/resources/icons/readme.txt
|
||||||
@ -74,14 +48,16 @@ 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 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 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-03_07:27:17 389 www/frontend/app/resources/icons/resultset_previous.png
|
||||||
UPD 2013-04-01_07:05:33 2154 www/frontend/app/app.js
|
UPD 2013-04-26_05:05:23 2238 www/frontend/app/app.js
|
||||||
UPD 2013-04-20_04:52:50 20318 www/frontend/app/view/LineChartPanel.js
|
UPD 2013-04-20_04:52:50 20318 www/frontend/app/view/LineChartPanel.js
|
||||||
|
UPD 2013-04-26_05:05:57 22790 www/frontend/app/view/HighChartsPanel.js
|
||||||
UPD 2013-04-20_04:52:50 1202 www/frontend/app/view/ChartGridPanel.js
|
UPD 2013-04-20_04:52:50 1202 www/frontend/app/view/ChartGridPanel.js
|
||||||
UPD 2013-04-03_07:26:40 15793 www/frontend/app/view/DevicePanel.js
|
UPD 2013-04-03_07:26:40 15793 www/frontend/app/view/DevicePanel.js
|
||||||
UPD 2013-04-20_04:52:50 8669 www/frontend/app/view/Viewport.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-01_07:05:14 2476 www/frontend/app/view/TableDataGridPanel.js
|
||||||
UPD 2013-04-20_04:52:30 62546 www/frontend/app/controller/ChartController.js
|
UPD 2013-04-20_04:52:30 62546 www/frontend/app/controller/ChartController.js
|
||||||
UPD 2013-04-20_04:52:31 13004 www/frontend/app/controller/MainController.js
|
UPD 2013-04-26_05:05:41 12806 www/frontend/app/controller/HighChartController.js
|
||||||
|
UPD 2013-04-26_05:05:41 13891 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: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: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:34 11535 www/frontend/app/model/ChartModel.js
|
||||||
@ -318,3 +294,26 @@ 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:25 264 www/frontend/lib/highcharts/ux/Highcharts/ColumnSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 2502 www/frontend/lib/highcharts/ux/Highcharts/WaterfallSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 10509 www/frontend/lib/highcharts/ux/Highcharts/PieSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 265 www/frontend/lib/highcharts/ux/Highcharts/SplineSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 1809 www/frontend/lib/highcharts/ux/Highcharts/BubbleSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 268 www/frontend/lib/highcharts/ux/Highcharts/ScatterSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 261 www/frontend/lib/highcharts/ux/Highcharts/AreaSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 1266 www/frontend/lib/highcharts/ux/Highcharts/BoxPlotSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 1088 www/frontend/lib/highcharts/ux/Highcharts/FunnelSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 283 www/frontend/lib/highcharts/ux/Highcharts/ErrorBarSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 281 www/frontend/lib/highcharts/ux/Highcharts/AreaSplineSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 11822 www/frontend/lib/highcharts/ux/Highcharts/Serie.js
|
||||||
|
UPD 2013-04-26_05:06:25 315 www/frontend/lib/highcharts/ux/Highcharts/AreaSplineRangeSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 2084 www/frontend/lib/highcharts/ux/Highcharts/RangeSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 295 www/frontend/lib/highcharts/ux/Highcharts/ColumnRangeSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 252 www/frontend/lib/highcharts/ux/Highcharts/BarSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 257 www/frontend/lib/highcharts/ux/Highcharts/LineSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 290 www/frontend/lib/highcharts/ux/Highcharts/AreaRangeSerie.js
|
||||||
|
UPD 2013-04-26_05:06:25 629 www/frontend/lib/highcharts/ux/License
|
||||||
|
UPD 2013-04-26_05:06:25 41709 www/frontend/lib/highcharts/ux/Highcharts.js
|
||||||
|
UPD 2013-04-26_05:06:25 426260 www/frontend/lib/highcharts/highcharts.src.js
|
||||||
|
UPD 2013-04-26_05:06:25 93867 www/frontend/lib/jquery/jquery.min.js
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
For installation details, have a look at
|
This is the readme of the new Webfrontend, based on ExtJS / Highcharts / JQuery.
|
||||||
|
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:
|
||||||
|
|
||||||
http://www.fhemwiki.de/wiki/Neues_Charting_Frontend
|
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. See the license.txt in the lib folder for details
|
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
|
||||||
|
@ -6,7 +6,8 @@ Ext.Loader.setConfig({
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
disableCaching: false,
|
disableCaching: false,
|
||||||
paths: {
|
paths: {
|
||||||
'FHEM': 'app'
|
'FHEM': 'app',
|
||||||
|
'Chart' : 'lib/highcharts/'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -18,7 +19,8 @@ Ext.application({
|
|||||||
|
|
||||||
controllers: [
|
controllers: [
|
||||||
'FHEM.controller.MainController',
|
'FHEM.controller.MainController',
|
||||||
'FHEM.controller.ChartController'
|
'FHEM.controller.ChartController',
|
||||||
|
'FHEM.controller.HighChartController'
|
||||||
],
|
],
|
||||||
|
|
||||||
launch: function() {
|
launch: function() {
|
||||||
|
@ -0,0 +1,317 @@
|
|||||||
|
/**
|
||||||
|
* 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,7 +4,8 @@
|
|||||||
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: [
|
||||||
@ -48,6 +49,9 @@ 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
|
||||||
},
|
},
|
||||||
@ -426,6 +430,29 @@ 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
|
||||||
|
// });
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
524
fhem/www/frontend/www/frontend/app/view/HighChartsPanel.js
Normal file
524
fhem/www/frontend/www/frontend/app/view/HighChartsPanel.js
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
@ -160,6 +160,12 @@ 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
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -20,9 +20,12 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<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-debug.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>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
</body>
|
</body>
|
||||||
|
16273
fhem/www/frontend/www/frontend/lib/highcharts/highcharts.src.js
Normal file
16273
fhem/www/frontend/www/frontend/lib/highcharts/highcharts.src.js
Normal file
File diff suppressed because it is too large
Load Diff
1104
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts.js
Executable file
1104
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts.js
Executable file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/AreaSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/AreaSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/BarSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/BarSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
50
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/BoxPlotSerie.js
Executable file
50
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/BoxPlotSerie.js
Executable file
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 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 ]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
});
|
65
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/BubbleSerie.js
Executable file
65
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/BubbleSerie.js
Executable file
@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* 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 ]
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/ColumnSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/ColumnSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/ErrorBarSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/ErrorBarSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
41
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/FunnelSerie.js
Executable file
41
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/FunnelSerie.js
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
});
|
17
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/GaugeSerie.js
Executable file
17
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/GaugeSerie.js
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/LineSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/LineSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
301
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/PieSerie.js
Executable file
301
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/PieSerie.js
Executable file
@ -0,0 +1,301 @@
|
|||||||
|
/**
|
||||||
|
* # 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
72
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/RangeSerie.js
Executable file
72
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/RangeSerie.js
Executable file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* 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]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/ScatterSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/ScatterSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/**
|
||||||
|
* 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'
|
||||||
|
});
|
341
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/Serie.js
Executable file
341
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/Serie.js
Executable file
@ -0,0 +1,341 @@
|
|||||||
|
/***
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/SplineSerie.js
Executable file
10
fhem/www/frontend/www/frontend/lib/highcharts/ux/Highcharts/SplineSerie.js
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
/***
|
||||||
|
* 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'
|
||||||
|
});
|
@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
});
|
13
fhem/www/frontend/www/frontend/lib/highcharts/ux/License
Executable file
13
fhem/www/frontend/www/frontend/lib/highcharts/ux/License
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
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)"
|
||||||
|
|
4
fhem/www/frontend/www/frontend/lib/jquery/jquery.min.js
vendored
Normal file
4
fhem/www/frontend/www/frontend/lib/jquery/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user