mirror of
https://github.com/fhem/fhem-mirror.git
synced 2025-04-07 19:04:20 +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
|
||||
* Über dem CHart wird nun eine interaktive Tabelle mit den zugehörigen Daten angezeigt
|
||||
* 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/util
|
||||
DIR www/frontend/lib/ext-4.2.0.663/images/util/splitter
|
||||
DEL www/frontend/lib/ext-4.1.1a
|
||||
DEL www/frontend/lib/ext-4.1.1a/images
|
||||
DEL www/frontend/lib/ext-4.1.1a/images/gray
|
||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tools
|
||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/tree
|
||||
DEL www/frontend/lib/ext-4.1.1a/images/gray/window-header
|
||||
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
|
||||
DIR www/frontend/lib/highcharts
|
||||
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 1065 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
|
||||
@ -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 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-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-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-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-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:36 338 www/frontend/app/model/SavedChartsModel.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-top.gif
|
||||
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,
|
||||
disableCaching: false,
|
||||
paths: {
|
||||
'FHEM': 'app'
|
||||
'FHEM': 'app',
|
||||
'Chart' : 'lib/highcharts/'
|
||||
}
|
||||
});
|
||||
|
||||
@ -18,7 +19,8 @@ Ext.application({
|
||||
|
||||
controllers: [
|
||||
'FHEM.controller.MainController',
|
||||
'FHEM.controller.ChartController'
|
||||
'FHEM.controller.ChartController',
|
||||
'FHEM.controller.HighChartController'
|
||||
],
|
||||
|
||||
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', {
|
||||
extend: 'Ext.app.Controller',
|
||||
requires: [
|
||||
'FHEM.view.DevicePanel'
|
||||
'FHEM.view.DevicePanel',
|
||||
'FHEM.view.HighChartsPanel'
|
||||
],
|
||||
|
||||
refs: [
|
||||
@ -48,6 +49,9 @@ Ext.define('FHEM.controller.MainController', {
|
||||
'panel[name=tabledataaccordionpanel]': {
|
||||
expand: this.showDatabaseTablePanel
|
||||
},
|
||||
'panel[name=highchartsaccordionpanel]': {
|
||||
expand: this.showHighChartsPanel
|
||||
},
|
||||
'treepanel[name=maintreepanel]': {
|
||||
itemclick: this.showDevicePanel
|
||||
},
|
||||
@ -426,6 +430,29 @@ Ext.define('FHEM.controller.MainController', {
|
||||
duration: 500,
|
||||
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',
|
||||
name: 'tabledataaccordionpanel',
|
||||
autoScroll: true
|
||||
},
|
||||
{
|
||||
xtype: 'panel',
|
||||
title: 'Highcharts',
|
||||
name: 'highchartsaccordionpanel',
|
||||
autoScroll: true
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -20,9 +20,12 @@
|
||||
</p>
|
||||
|
||||
<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/app.js"></script>
|
||||
|
||||
</head>
|
||||
<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