//------------------------------------------------ // Prepare everything necessary to provide a chart //------------------------------------------------ //---------------- //...chart related //---------------- var initialize = true; // globally to have access from outer scope var globalChart; // dynamic subtitle management var chartInfoTmpl = ''; var yyyy_min = "{yyyy_min}"; var yyyy_max = "{yyyy_max}"; var mm_min = "{mm_min}"; var mm_max = "{mm_max}"; var dd_min = "{dd_min}"; var dd_max = "{dd_max}"; // dynamic credits var creditsText = ''; var creditsLink = ''; // y axis management var yAxisAttr = new Object(); var chartOptions = { chart: { resetZoomButton: { position: { x:-10, y:-35 } }, zoomType: 'x', events: { load: function() { this.credits.element.onclick = function() { window.open( creditsLink, '_blank' // <- This is what makes it open in a new window. ); } } } }, plotOptions: { series: { // this block is for y axis management events: { hide: function () { // check if all data is disabled if (this.yAxis.hasData() === false) { if (!yAxisAttr[this.yAxis.options.id]) { yAxisAttr[this.yAxis.options.id] = {}; } // check position of y axis if it was normal y axis if (this.yAxis.opposite === false) { var existsNormalYAxis = false; for (i=0;i < globalChart.series.length;i++) { if (globalChart.series[i].yAxis.hasData() && globalChart.series[i].yAxis.opposite === false) { existsNormalYAxis = true; } } // if no normal y axis exists swap next opposite axis if (existsNormalYAxis === false) { for (i=0;i < globalChart.series.length;i++) { if (globalChart.series[i].yAxis.hasData() && globalChart.series[i].yAxis.opposite === true) { // remeber original value if (!yAxisAttr[globalChart.series[i].yAxis.options.id]) { yAxisAttr[globalChart.series[i].yAxis.options.id] = {}; } yAxisAttr[globalChart.series[i].yAxis.options.id]['opposite'] = globalChart.series[i].yAxis.opposite; // update opposite value globalChart.series[i].yAxis.update({opposite: false}); break; } } } } // remeber original value yAxisAttr[this.yAxis.options.id]['title'] = this.yAxis.options.title.text; yAxisAttr[this.yAxis.options.id]['opposite'] = this.yAxis.opposite; // hide text from y axis if all series are disabled this.yAxis.setTitle({text: ''}); } }, // plotOptions::series::events::hide show: function () { // back to original state // update y axis if necessary if (this.yAxis.options.title.text === '') { // update text to original value this.yAxis.setTitle({text: yAxisAttr[this.yAxis.options.id]['title']}); // update opposite attribute to original value for (key in yAxisAttr) { if ('opposite' in yAxisAttr[key]) { for (i=0;i < globalChart.series.length;i++) { if (key == globalChart.series[i].yAxis.options.id) { globalChart.series[i].yAxis.update({opposite: yAxisAttr[key]['opposite']}); } } } } // when original state was achieved than remove entry delete yAxisAttr[this.yAxis.options.id]; } } // plotOptions::series::events::show } // plotOptions::series::events } // plotOptions::series }, // plotOptions title: { //text: 'Grafische Darstellung der Parameter', x: -20 //center }, subtitle: { text: 'Hier der Zeitraum', x: -20 }, legend :{ // provide space between legend and credits text y: -16 }, tooltip: { useHTML: true, crosshairs: true, shared: true, dateTimeLabelFormats: { hour:"%A, %e. %b, %H:%M Uhr", day:"%A, %e. %b, %Y" } } }; // chartOptions //-------------------------------------------- // .......................special theme //-------------------------------------------- // Load the fonts Highcharts.createElement('link', { href: '//fonts.googleapis.com/css?family=Signika:400,700', rel: 'stylesheet', type: 'text/css' }, null, document.getElementsByTagName('head')[0]); Highcharts.theme = { colors: ["#f45b5b", "#8085e9", "#8d4654", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee", "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"], chart: { backgroundColor: null, style: { fontFamily: "Signika, serif" } }, title: { style: { color: 'black', fontSize: '16px', fontWeight: 'bold' } }, subtitle: { style: { fontSize: '14px', color: 'black' } }, tooltip: { borderWidth: 0 }, legend: { itemStyle: { fontWeight: 'bold', fontSize: '13px' } }, xAxis: { labels: { style: { color: '#6e6e70' } } }, yAxis: { labels: { style: { color: '#6e6e70' } } }, plotOptions: { series: { shadow: true }, candlestick: { lineColor: '#404048' }, map: { shadow: false } }, // Highstock specific navigator: { xAxis: { gridLineColor: '#D0D0D8' } }, rangeSelector: { buttonTheme: { fill: 'white', stroke: '#C0C0C8', 'stroke-width': 1, states: { select: { fill: '#D0D0D8' } } } }, scrollbar: { trackBorderColor: '#C0C0C8' }, // General background2: '#E0E0E8' }; // -------------------------------- //...main chart function // -------------------------------- function prepareCharts(jsonData) { $('#container_0').html(''); $('#container_1').html(''); //...MODIFIED 2016-03-09 >>> //...It is recommended to destroy the obsolete highcharts objects //...Have a look at: http://api.highcharts.com/highcharts#Chart.destroy if ($('#container_0').highcharts()) $('#container_0').highcharts().destroy(); if ($('#container_1').highcharts())$('#container_1').highcharts().destroy(); if (Highcharts.charts.length > 0) Highcharts.charts.length = 0; //<<<< MODIFIED 2016-03-09 // iterate through charts $.each(jsonData.Charts, function (i, chartset) { var chartDiv = document.getElementById('container_' + i); // global data chartInfoTmpl = chartset["ChartInfoTmpl"]; creditsLink = chartset["CreditsLink"]; creditsText = chartset["CreditsText"]; var titleText = chartset["TitleText"]; //...prepare chartoptions chartOptions.chart.renderTo = chartDiv; chartOptions.subtitle.text = chartset["ChartInfo"]; chartOptions.xAxis = convertString2Bool(chartset["Xaxis"]); chartOptions.yAxis = convertString2Bool(chartset["Yaxis"]); chartOptions.series = chartset["Series"]; //...set global chart options if (initialize) { Highcharts.setOptions( { lang: { months: ['Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'], shortMonths: [ "Jan" , "Feb" , "Mrz" , "Apr" , "Mai" , "Jun" , "Jul" , "Aug" , "Sep" , "Okt" , "Nov" , "Dez"], weekdays: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'], printChart: 'Grafik drucken...', contextButtonTitle: 'Optionen...', downloadPNG: 'Speichern als PNG Grafik...', downloadJPEG: 'Speichern als JPG Grafik...', downloadPDF: 'Speichern als PDF Dokument...', downloadSVG: 'Speichern als SVG Grafik...', resetZoom: '<< Zurück zur Übersicht', resetZoomTitle: 'Ansicht wieder herstellen', thousandsSep: "" }, credits: { enabled: true, text: 'Quelle: ' + creditsText, position: { align: 'right', verticalAlign: 'bottom' } } } ); initialize = false; } // Apply the theme Highcharts.setOptions(Highcharts.theme); //...create chart object globalChart = new Highcharts.Chart(chartOptions); // set title extra because we have more than one chart globalChart.setTitle({ text: titleText }); // ...add dynamically event listener for period management when zooming in Highcharts.addEvent( globalChart.xAxis[0], 'setExtremes', syncExtremes ); Highcharts.addEvent( globalChart.xAxis[0], 'afterSetExtremes', function (event) { // update subtitle when zoom in var serie = this.chart.series[0], min = this.getExtremes().min, max = this.getExtremes().max; var pointArr = new Array(); $.each( serie.data, function(i, p) { if(p.x >= min && p.x <= max) { pointArr.push(p.x); } } ); // extract limits from point array var newSubtitle = chartInfoTmpl.replace(yyyy_min, Highcharts.dateFormat('%Y', pointArr[0])); newSubtitle = newSubtitle.replace(mm_min, Highcharts.dateFormat('%m', pointArr[0])); newSubtitle = newSubtitle.replace(dd_min, Highcharts.dateFormat('%d', pointArr[0])); newSubtitle = newSubtitle.replace(yyyy_max, Highcharts.dateFormat('%Y', pointArr[pointArr.length-1])); newSubtitle = newSubtitle.replace(mm_max, Highcharts.dateFormat('%m', pointArr[pointArr.length-1])); newSubtitle = newSubtitle.replace(dd_max, Highcharts.dateFormat('%d', pointArr[pointArr.length-1])); this.chart.options.subtitle.text = newSubtitle; this.chart.setTitle(null, newSubtitle); } ); }); } /** * In order to synchronize tooltips and crosshairs, override the * built-in events with handlers defined on the parent element. */ $(function () { $('#container_0').bind('mousemove touchmove', function (e) { syncChart($('#container_0').highcharts(), e); }); $('#container_1').bind('mousemove touchmove', function (e) { syncChart($('#container_1').highcharts(), e); }); }); // ----------------------------------------- // Synchronize zooming through the setExtremes event handler. // function syncExtremes(e) { var thisChart = this.chart; if (e.trigger !== 'syncExtremes') { // Prevent feedback loop Highcharts.each(Highcharts.charts, function (chart) { if (chart !== thisChart) { if (chart.xAxis[0].setExtremes) { // It is null while updating chart.xAxis[0].setExtremes(e.min, e.max, undefined, false, { trigger: 'syncExtremes' }); } } }); } } //---------------------------------------------------- // main function for chart synchronisation //---------------------------------------------------- function syncChart(currentChart, eventArgs) { // check if chart is defined, depends how it is opened // as a dialog or directly over a link if (currentChart === undefined) { return; } var chart, points, points4Tooltip, e4Search, point4Search, serie, i, j currentMax = currentChart.xAxis[0].max, currentMin = currentChart.xAxis[0].min; // get original point for synchronisation e4Search = currentChart.pointer.normalize(eventArgs); point4Search = currentChart.series[0].searchPoint(e4Search, true); if (!point4Search) { return; } // sync with charts for (i = 0; i < Highcharts.charts.length; i++) { chart = Highcharts.charts[i]; //...MODIFIED 20160309: prevent js error if (!chart) {continue}; eventArgs = chart.pointer.normalize(eventArgs); // Find coordinates within the chart points4Tooltip = []; for (j = 0; j < chart.series.length; j++) { if (!chart.series[j].visible) { continue; } points = chart.series[j].points; if (point4Search && points.length > 0 && points[0]) { $.each(points, function (i, p) { // sync chart and add points for tooltip if (p.x === point4Search.x && p.x >= currentMin && p.x <= currentMax) { points4Tooltip.push(p); // because "tooltip.refresh" expects an array!! p.onMouseOver(); // Show the hover marker chart.xAxis[0].drawCrosshair(eventArgs, p); // Show the crosshair return false; } }); } } // sync tooltip if (points4Tooltip && points4Tooltip.length > 0) { chart.tooltip.refresh(points4Tooltip); // Show the tooltip } } } //------------------------------------------------------------------------ //... ialog related stuff //------------------------------------------------------------------------ var myparent = window.opener; //---------------------------------------------------- //...Triggered e.g. by Form-Button Event //...or any arbitrary action to close the dialog //---------------------------------------------------- function dlgClose () { if (myparent && ! myparent.closed){ //...this also triggers body.onunload, which calls dlgCleanup window.close(); } } //---------------------------------------------------- //...triggered by 'onunload' //---------------------------------------------------- function dlgCleanup (){ if (myparent && ! myparent.closed) { //...notify parent, that dlg has closed myparent.chartDLG.cleanup(); } } //---------------------------------------------------- //...triggered by 'onfocus' //---------------------------------------------------- function dlgShowCharts(){ if (myparent) { if(myparent.chartDLG.jsonData) { prepareCharts(myparent.chartDLG.jsonData); } else { alert('Keine oder unzulässige Daten'); dlgClose(); } } } //---------------------------------------------------- //...triggered by 'onload' //---------------------------------------------------- function dlgPrepare () { //...'hide' chart container $( "#container_0" ).height(0); $( "#container_1" ).height(0); //...reset size of dlg redrawDlg(); //...adapt divheight var nofcharts = chartCount(); switch(nofcharts){ case 1: $( "#container_0" ).height($(window).innerHeight() - 16); break; case 2: $( "#container_0" ).height($(window).innerHeight()/2 - 16); $( "#container_1" ).height($(window).innerHeight()/2 - 16); } //...render chart dlgShowCharts(); } //---------------------------------------------------- //...triggered by 'onresize' //---------------------------------------------------- window.onresize = function() { //...adapt divheight var nofcharts = chartCount(); var minInnerHeight; var minHeigthPerChart = 248; var borderHeight = 16; if (nofcharts > 0) { minInnerHeight = nofcharts * minHeigthPerChart; } else {minInnerHeight = 1 * minHeigthPerChart;} if ($(window).innerHeight() > minInnerHeight){ //...adapt chartheight switch(nofcharts){ case 1: $( "#container_0" ).height( $(window).innerHeight() - borderHeight); break; case 2: $( "#container_0" ).height( $(window).innerHeight()/2 - borderHeight); $( "#container_1" ).height( $(window).innerHeight()/2 - borderHeight); } } } //---------------------------------------------------- // ............................some helper functions //---------------------------------------------------- //---------------------------------------------------- // resizes and moves the dialog to corresponding position //---------------------------------------------------- function redrawDlg() { if (myparent) { myparent.chartDLG.dlg.resizeTo(800, 680); myparent.chartDLG.dlg.moveTo((myparent.chartDLG.dlg.screen.availWidth-800)/2, (myparent.chartDLG.dlg.screen.availHeight-680)/2); } } //---------------------------------------------------- // return number of charts //---------------------------------------------------- function chartCount() { var chartCount = 0; if (myparent) { if (myparent.chartDLG.jsonData) { chartCount = myparent.chartDLG.jsonData.Charts.length; } } return chartCount; } //---------------------------------------------------- function convertString2Bool(jsonData) { for(var i in jsonData) { var axis = jsonData[i]; if(axis.opposite) if (axis.opposite.toUpperCase() === "TRUE") axis.opposite = true; else axis.opposite = false; if (axis.crosshair) if (axis.crosshair.toUpperCase() === "TRUE") axis.crosshair = true; else axis.crosshair = false; if(axis.title) if (axis.title.useHTML.toUpperCase() === "TRUE") axis.title.useHTML = true; else axis.title.useHTML = false; if (axis.labels) if (axis.labels.useHTML.toUpperCase() === "TRUE") axis.labels.useHTML = true; else axis.labels.useHTML = false; } return jsonData; } //---------------------------------------------------- function convertToDateUTC(jsonData) { for(var i in jsonData){ var series = jsonData[i]; if (series.pointStart != null && series.pointStart.length > 0) { series.pointStart = Date.UTC( parseInt(series.pointStart.substr(0, 4), 10), parseInt(series.pointStart.substr(4, 2), 10) - 1, parseInt(series.pointStart.substr(6, 2), 10)); } } return jsonData; }