");return this.tooltipOptions&&this.tooltipOptions.cssClass&&(b=a("."+this.tooltipOptions.cssClass),0===b.length&&(b=a("
").addClass(this.tooltipOptions.cssClass),b.appendTo("body").hide().css({position:"absolute"}),this.tooltipOptions.defaultTheme&&b.css({background:"#fff","z-index":"1040",padding:"0.4em 0.6em","border-radius":"0.5em","font-size":"0.8em",border:"1px solid #111",display:"none","white-space":"nowrap"}))),b},c.prototype.stringFormat=function(a,b){var c,d,e,f,g,h=/%p\.{0,1}(\d{0,})/,i=/%s/,j=/%c/,k=/%lx/,l=/%ly/,m=/%x\.{0,1}(\d{0,})/,n=/%y\.{0,1}(\d{0,})/,o="%x",p="%y",q="%ct",r="%n";if("undefined"!=typeof b.series.threshold?(c=b.datapoint[0],d=b.datapoint[1],e=b.datapoint[2]):"undefined"!=typeof b.series.curvedLines?(c=b.datapoint[0],d=b.datapoint[1]):"undefined"!=typeof b.series.lines&&b.series.lines.steps?(c=b.series.datapoints.points[2*b.dataIndex],d=b.series.datapoints.points[2*b.dataIndex+1],e=""):(c=b.series.data[b.dataIndex][0],d=b.series.data[b.dataIndex][1],e=b.series.data[b.dataIndex][2]),null===b.series.label&&b.series.originSeries&&(b.series.label=b.series.originSeries.label),"function"==typeof a&&(a=a(b.series.label,c,d,b)),"boolean"==typeof a&&!a)return"";if(e&&(a=a.replace(q,e)),"undefined"!=typeof b.series.percent?f=b.series.percent:"undefined"!=typeof b.series.percents&&(f=b.series.percents[b.dataIndex]),"number"==typeof f&&(a=this.adjustValPrecision(h,a,f)),b.series.hasOwnProperty("pie")&&"undefined"!=typeof b.series.data[0][1]&&(g=b.series.data[0][1]),"number"==typeof g&&(a=a.replace(r,g)),a="undefined"!=typeof b.series.label?a.replace(i,b.series.label):a.replace(i,""),a="undefined"!=typeof b.series.color?a.replace(j,b.series.color):a.replace(j,""),a=this.hasAxisLabel("xaxis",b)?a.replace(k,b.series.xaxis.options.axisLabel):a.replace(k,""),a=this.hasAxisLabel("yaxis",b)?a.replace(l,b.series.yaxis.options.axisLabel):a.replace(l,""),this.isTimeMode("xaxis",b)&&this.isXDateFormat(b)&&(a=a.replace(m,this.timestampToDate(c,this.tooltipOptions.xDateFormat,b.series.xaxis.options))),this.isTimeMode("yaxis",b)&&this.isYDateFormat(b)&&(a=a.replace(n,this.timestampToDate(d,this.tooltipOptions.yDateFormat,b.series.yaxis.options))),"number"==typeof c&&(a=this.adjustValPrecision(m,a,c)),"number"==typeof d&&(a=this.adjustValPrecision(n,a,d)),"undefined"!=typeof b.series.xaxis.ticks){var s;s=this.hasRotatedXAxisTicks(b)?"rotatedTicks":"ticks";var t=b.dataIndex+b.seriesIndex;for(var u in b.series.xaxis[s])if(b.series.xaxis[s].hasOwnProperty(t)&&!this.isTimeMode("xaxis",b)){var v=this.isCategoriesMode("xaxis",b)?b.series.xaxis[s][t].label:b.series.xaxis[s][t].v;v===c&&(a=a.replace(m,b.series.xaxis[s][t].label.replace(/\$/g,"$$$$")))}}if("undefined"!=typeof b.series.yaxis.ticks)for(var w in b.series.yaxis.ticks)if(b.series.yaxis.ticks.hasOwnProperty(w)){var x=this.isCategoriesMode("yaxis",b)?b.series.yaxis.ticks[w].label:b.series.yaxis.ticks[w].v;x===d&&(a=a.replace(n,b.series.yaxis.ticks[w].label.replace(/\$/g,"$$$$")))}return"undefined"!=typeof b.series.xaxis.tickFormatter&&(a=a.replace(o,b.series.xaxis.tickFormatter(c,b.series.xaxis).replace(/\$/g,"$$"))),"undefined"!=typeof b.series.yaxis.tickFormatter&&(a=a.replace(p,b.series.yaxis.tickFormatter(d,b.series.yaxis).replace(/\$/g,"$$"))),a},c.prototype.isTimeMode=function(a,b){return"undefined"!=typeof b.series[a].options.mode&&"time"===b.series[a].options.mode},c.prototype.isXDateFormat=function(a){return"undefined"!=typeof this.tooltipOptions.xDateFormat&&null!==this.tooltipOptions.xDateFormat},c.prototype.isYDateFormat=function(a){return"undefined"!=typeof this.tooltipOptions.yDateFormat&&null!==this.tooltipOptions.yDateFormat},c.prototype.isCategoriesMode=function(a,b){return"undefined"!=typeof b.series[a].options.mode&&"categories"===b.series[a].options.mode},c.prototype.timestampToDate=function(b,c,d){var e=a.plot.dateGenerator(b,d);return a.plot.formatDate(e,c,this.tooltipOptions.monthNames,this.tooltipOptions.dayNames)},c.prototype.adjustValPrecision=function(a,b,c){var d,e=b.match(a);return null!==e&&""!==RegExp.$1&&(d=RegExp.$1,c=c.toFixed(d),b=b.replace(a,c)),b},c.prototype.hasAxisLabel=function(b,c){return-1!==a.inArray("axisLabels",this.plotPlugins)&&"undefined"!=typeof c.series[b].options.axisLabel&&c.series[b].options.axisLabel.length>0},c.prototype.hasRotatedXAxisTicks=function(b){return-1!==a.inArray("tickRotor",this.plotPlugins)&&"undefined"!=typeof b.series.xaxis.rotatedTicks};var d=function(a){new c(a)};a.plot.plugins.push({init:d,options:b,name:"tooltip",version:"0.8.5"})}(jQuery);
\ No newline at end of file
diff --git a/apps/OpenEnergyMonitor/myheatpump/lib/jquery.flot.tooltip.source.js b/apps/OpenEnergyMonitor/myheatpump/lib/jquery.flot.tooltip.source.js
new file mode 100644
index 0000000..0e49edc
--- /dev/null
+++ b/apps/OpenEnergyMonitor/myheatpump/lib/jquery.flot.tooltip.source.js
@@ -0,0 +1,595 @@
+(function ($) {
+ // plugin options, default values
+ var defaultOptions = {
+ tooltip: {
+ show: false,
+ cssClass: "flotTip",
+ content: "%s | X: %x | Y: %y",
+ // allowed templates are:
+ // %s -> series label,
+ // %c -> series color,
+ // %lx -> x axis label (requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels),
+ // %ly -> y axis label (requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels),
+ // %x -> X value,
+ // %y -> Y value,
+ // %x.2 -> precision of X value,
+ // %p -> percent
+ // %n -> value (not percent) of pie chart
+ xDateFormat: null,
+ yDateFormat: null,
+ monthNames: null,
+ dayNames: null,
+ shifts: {
+ x: 10,
+ y: 20
+ },
+ defaultTheme: true,
+ snap: true,
+ lines: false,
+ clickTips: false,
+
+ // callbacks
+ onHover: function (flotItem, $tooltipEl) {},
+
+ $compat: false
+ }
+ };
+
+ // dummy default options object for legacy code (<0.8.5) - is deleted later
+ defaultOptions.tooltipOpts = defaultOptions.tooltip;
+
+ // object
+ var FlotTooltip = function (plot) {
+ // variables
+ this.tipPosition = {x: 0, y: 0};
+
+ this.init(plot);
+ };
+
+ // main plugin function
+ FlotTooltip.prototype.init = function (plot) {
+ var that = this;
+
+ // detect other flot plugins
+ var plotPluginsLength = $.plot.plugins.length;
+ this.plotPlugins = [];
+
+ if (plotPluginsLength) {
+ for (var p = 0; p < plotPluginsLength; p++) {
+ this.plotPlugins.push($.plot.plugins[p].name);
+ }
+ }
+
+ plot.hooks.bindEvents.push(function (plot, eventHolder) {
+
+ // get plot options
+ that.plotOptions = plot.getOptions();
+
+ // for legacy (<0.8.5) implementations
+ if (typeof(that.plotOptions.tooltip) === 'boolean') {
+ that.plotOptions.tooltipOpts.show = that.plotOptions.tooltip;
+ that.plotOptions.tooltip = that.plotOptions.tooltipOpts;
+ delete that.plotOptions.tooltipOpts;
+ }
+
+ // if not enabled return
+ if (that.plotOptions.tooltip.show === false || typeof that.plotOptions.tooltip.show === 'undefined') return;
+
+ // shortcut to access tooltip options
+ that.tooltipOptions = that.plotOptions.tooltip;
+
+ if (that.tooltipOptions.$compat) {
+ that.wfunc = 'width';
+ that.hfunc = 'height';
+ } else {
+ that.wfunc = 'innerWidth';
+ that.hfunc = 'innerHeight';
+ }
+
+ // create tooltip DOM element
+ var $tip = that.getDomElement();
+
+ // bind event
+ $( plot.getPlaceholder() ).bind("plothover", plothover);
+ if (that.tooltipOptions.clickTips) {
+ $( plot.getPlaceholder() ).bind("plotclick", plotclick);
+ }
+ that.clickmode = false;
+
+ $(eventHolder).bind('mousemove', mouseMove);
+ });
+
+ plot.hooks.shutdown.push(function (plot, eventHolder){
+ $(plot.getPlaceholder()).unbind("plothover", plothover);
+ $(plot.getPlaceholder()).unbind("plotclick", plotclick);
+ plot.removeTooltip();
+ $(eventHolder).unbind("mousemove", mouseMove);
+ });
+
+ function mouseMove(e){
+ var pos = {};
+ pos.x = e.pageX;
+ pos.y = e.pageY;
+ plot.setTooltipPosition(pos);
+ }
+
+ /**
+ * open the tooltip (if not already open) and freeze it on the current position till the next click
+ */
+ function plotclick(event, pos, item) {
+ if (! that.clickmode) {
+ // it is the click activating the clicktip
+ plothover(event, pos, item);
+ if (that.getDomElement().is(":visible")) {
+ $(plot.getPlaceholder()).unbind("plothover", plothover);
+ that.clickmode = true;
+ }
+ } else {
+ // it is the click deactivating the clicktip
+ $( plot.getPlaceholder() ).bind("plothover", plothover);
+ plot.hideTooltip();
+ that.clickmode = false;
+ }
+ }
+
+ function plothover(event, pos, item) {
+ // Simple distance formula.
+ var lineDistance = function (p1x, p1y, p2x, p2y) {
+ return Math.sqrt((p2x - p1x) * (p2x - p1x) + (p2y - p1y) * (p2y - p1y));
+ };
+
+ // Here is some voodoo magic for determining the distance to a line form a given point {x, y}.
+ var dotLineLength = function (x, y, x0, y0, x1, y1, o) {
+ if (o && !(o =
+ function (x, y, x0, y0, x1, y1) {
+ if (typeof x0 !== 'undefined') return { x: x0, y: y };
+ else if (typeof y0 !== 'undefined') return { x: x, y: y0 };
+
+ var left,
+ tg = -1 / ((y1 - y0) / (x1 - x0));
+
+ return {
+ x: left = (x1 * (x * tg - y + y0) + x0 * (x * -tg + y - y1)) / (tg * (x1 - x0) + y0 - y1),
+ y: tg * left - tg * x + y
+ };
+ } (x, y, x0, y0, x1, y1),
+ o.x >= Math.min(x0, x1) && o.x <= Math.max(x0, x1) && o.y >= Math.min(y0, y1) && o.y <= Math.max(y0, y1))
+ ) {
+ var l1 = lineDistance(x, y, x0, y0), l2 = lineDistance(x, y, x1, y1);
+ return l1 > l2 ? l2 : l1;
+ } else {
+ var a = y0 - y1, b = x1 - x0, c = x0 * y1 - y0 * x1;
+ return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
+ }
+ };
+
+ if (item) {
+ plot.showTooltip(item, that.tooltipOptions.snap ? item : pos);
+ } else if (that.plotOptions.series.lines.show && that.tooltipOptions.lines === true) {
+ var maxDistance = that.plotOptions.grid.mouseActiveRadius;
+
+ var closestTrace = {
+ distance: maxDistance + 1
+ };
+
+ var ttPos = pos;
+
+ $.each(plot.getData(), function (i, series) {
+ var xBeforeIndex = 0,
+ xAfterIndex = -1;
+
+ // Our search here assumes our data is sorted via the x-axis.
+ // TODO: Improve efficiency somehow - search smaller sets of data.
+ for (var j = 1; j < series.data.length; j++) {
+ if (series.data[j - 1][0] <= pos.x && series.data[j][0] >= pos.x) {
+ xBeforeIndex = j - 1;
+ xAfterIndex = j;
+ }
+ }
+
+ if (xAfterIndex === -1) {
+ plot.hideTooltip();
+ return;
+ }
+
+ var pointPrev = { x: series.data[xBeforeIndex][0], y: series.data[xBeforeIndex][1] },
+ pointNext = { x: series.data[xAfterIndex][0], y: series.data[xAfterIndex][1] };
+
+ var distToLine = dotLineLength(series.xaxis.p2c(pos.x), series.yaxis.p2c(pos.y), series.xaxis.p2c(pointPrev.x),
+ series.yaxis.p2c(pointPrev.y), series.xaxis.p2c(pointNext.x), series.yaxis.p2c(pointNext.y), false);
+
+ if (distToLine < closestTrace.distance) {
+
+ var closestIndex = lineDistance(pointPrev.x, pointPrev.y, pos.x, pos.y) <
+ lineDistance(pos.x, pos.y, pointNext.x, pointNext.y) ? xBeforeIndex : xAfterIndex;
+
+ var pointSize = series.datapoints.pointsize;
+
+ // Calculate the point on the line vertically closest to our cursor.
+ var pointOnLine = [
+ pos.x,
+ pointPrev.y + ((pointNext.y - pointPrev.y) * ((pos.x - pointPrev.x) / (pointNext.x - pointPrev.x)))
+ ];
+
+ var item = {
+ datapoint: pointOnLine,
+ dataIndex: closestIndex,
+ series: series,
+ seriesIndex: i
+ };
+
+ closestTrace = {
+ distance: distToLine,
+ item: item
+ };
+
+ if (that.tooltipOptions.snap) {
+ ttPos = {
+ pageX: series.xaxis.p2c(pointOnLine[0]),
+ pageY: series.yaxis.p2c(pointOnLine[1])
+ };
+ }
+ }
+ });
+
+ if (closestTrace.distance < maxDistance + 1)
+ plot.showTooltip(closestTrace.item, ttPos);
+ else
+ plot.hideTooltip();
+ } else {
+ plot.hideTooltip();
+ }
+ }
+
+ // Quick little function for setting the tooltip position.
+ plot.setTooltipPosition = function (pos) {
+ var $tip = that.getDomElement();
+
+ var totalTipWidth = $tip.outerWidth() + that.tooltipOptions.shifts.x;
+ var totalTipHeight = $tip.outerHeight() + that.tooltipOptions.shifts.y;
+ if ((pos.x - $(window).scrollLeft()) > ($(window)[that.wfunc]() - totalTipWidth)) {
+ pos.x -= totalTipWidth;
+ pos.x = Math.max(pos.x, 0);
+ }
+ if ((pos.y - $(window).scrollTop()) > ($(window)[that.hfunc]() - totalTipHeight)) {
+ pos.y -= totalTipHeight;
+ }
+
+ /*
+ The section applies the new positioning ONLY if pos.x and pos.y
+ are numbers. If they are undefined or not a number, use the last
+ known numerical position. This hack fixes a bug that kept pie
+ charts from keeping their tooltip positioning.
+ */
+
+ if (isNaN(pos.x)) {
+ that.tipPosition.x = that.tipPosition.xPrev;
+ }
+ else {
+ that.tipPosition.x = pos.x;
+ that.tipPosition.xPrev = pos.x;
+ }
+ if (isNaN(pos.y)) {
+ that.tipPosition.y = that.tipPosition.yPrev;
+ }
+ else {
+ that.tipPosition.y = pos.y;
+ that.tipPosition.yPrev = pos.y;
+ }
+
+ };
+
+ // Quick little function for showing the tooltip.
+ plot.showTooltip = function (target, position, targetPosition) {
+ var $tip = that.getDomElement();
+
+ // convert tooltip content template to real tipText
+ var tipText = that.stringFormat(that.tooltipOptions.content, target);
+ if (tipText === '')
+ return;
+
+ $tip.html(tipText);
+ plot.setTooltipPosition({ x: that.tipPosition.x, y: that.tipPosition.y });
+ $tip.css({
+ left: that.tipPosition.x + that.tooltipOptions.shifts.x,
+ top: that.tipPosition.y + that.tooltipOptions.shifts.y
+ }).show();
+
+ // run callback
+ if (typeof that.tooltipOptions.onHover === 'function') {
+ that.tooltipOptions.onHover(target, $tip);
+ }
+ };
+
+ // Quick little function for hiding the tooltip.
+ plot.hideTooltip = function () {
+ that.getDomElement().hide().html('');
+ };
+
+ plot.removeTooltip = function() {
+ that.getDomElement().remove();
+ };
+ };
+
+ /**
+ * get or create tooltip DOM element
+ * @return jQuery object
+ */
+ FlotTooltip.prototype.getDomElement = function () {
+ var $tip = $('
');
+ if (this.tooltipOptions && this.tooltipOptions.cssClass) {
+ $tip = $('.' + this.tooltipOptions.cssClass);
+
+ if( $tip.length === 0 ){
+ $tip = $('
').addClass(this.tooltipOptions.cssClass);
+ $tip.appendTo('body').hide().css({position: 'absolute'});
+
+ if(this.tooltipOptions.defaultTheme) {
+ $tip.css({
+ 'background': '#fff',
+ 'z-index': '1040',
+ 'padding': '0.4em 0.6em',
+ 'border-radius': '0.5em',
+ 'font-size': '0.8em',
+ 'border': '1px solid #111',
+ 'display': 'none',
+ 'white-space': 'nowrap'
+ });
+ }
+ }
+ }
+
+ return $tip;
+ };
+
+ /**
+ * core function, create tooltip content
+ * @param {string} content - template with tooltip content
+ * @param {object} item - Flot item
+ * @return {string} real tooltip content for current item
+ */
+ FlotTooltip.prototype.stringFormat = function (content, item) {
+ var percentPattern = /%p\.{0,1}(\d{0,})/;
+ var seriesPattern = /%s/;
+ var colorPattern = /%c/;
+ var xLabelPattern = /%lx/; // requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels, will be ignored if plugin isn't loaded
+ var yLabelPattern = /%ly/; // requires flot-axislabels plugin https://github.com/markrcote/flot-axislabels, will be ignored if plugin isn't loaded
+ var xPattern = /%x\.{0,1}(\d{0,})/;
+ var yPattern = /%y\.{0,1}(\d{0,})/;
+ var xPatternWithoutPrecision = "%x";
+ var yPatternWithoutPrecision = "%y";
+ var customTextPattern = "%ct";
+ var nPiePattern = "%n";
+
+ var x, y, customText, p, n;
+
+ // for threshold plugin we need to read data from different place
+ if (typeof item.series.threshold !== "undefined") {
+ x = item.datapoint[0];
+ y = item.datapoint[1];
+ customText = item.datapoint[2];
+ }
+
+ // for CurvedLines plugin we need to read data from different place
+ else if (typeof item.series.curvedLines !== "undefined") {
+ x = item.datapoint[0];
+ y = item.datapoint[1];
+ }
+
+ else if (typeof item.series.lines !== "undefined" && item.series.lines.steps) {
+ x = item.series.datapoints.points[item.dataIndex * 2];
+ y = item.series.datapoints.points[item.dataIndex * 2 + 1];
+ // TODO: where to find custom text in this variant?
+ customText = "";
+ } else {
+ x = item.series.data[item.dataIndex][0];
+ y = item.series.data[item.dataIndex][1];
+ customText = item.series.data[item.dataIndex][2];
+ }
+
+ // I think this is only in case of threshold plugin
+ if (item.series.label === null && item.series.originSeries) {
+ item.series.label = item.series.originSeries.label;
+ }
+
+ // if it is a function callback get the content string
+ if (typeof(content) === 'function') {
+ content = content(item.series.label, x, y, item);
+ }
+
+ // the case where the passed content is equal to false
+ if (typeof(content) === 'boolean' && !content) {
+ return '';
+ }
+
+ /* replacement of %ct and other multi-character templates must
+ precede the replacement of single-character templates
+ to avoid conflict between '%c' and '%ct' and similar substrings
+ */
+ if (customText) {
+ content = content.replace(customTextPattern, customText);
+ }
+
+ // percent match for pie charts and stacked percent
+ if (typeof (item.series.percent) !== 'undefined') {
+ p = item.series.percent;
+ } else if (typeof (item.series.percents) !== 'undefined') {
+ p = item.series.percents[item.dataIndex];
+ }
+ if (typeof p === 'number') {
+ content = this.adjustValPrecision(percentPattern, content, p);
+ }
+
+ // replace %n with number of items represented by slice in pie charts
+ if (item.series.hasOwnProperty('pie')) {
+ if (typeof item.series.data[0][1] !== 'undefined') {
+ n = item.series.data[0][1];
+ }
+ }
+ if (typeof n === 'number') {
+ content = content.replace(nPiePattern, n);
+ }
+
+ // series match
+ if (typeof(item.series.label) !== 'undefined') {
+ content = content.replace(seriesPattern, item.series.label);
+ } else {
+ //remove %s if label is undefined
+ content = content.replace(seriesPattern, "");
+ }
+
+ // color match
+ if (typeof(item.series.color) !== 'undefined') {
+ content = content.replace(colorPattern, item.series.color);
+ } else {
+ //remove %s if color is undefined
+ content = content.replace(colorPattern, "");
+ }
+
+ // x axis label match
+ if (this.hasAxisLabel('xaxis', item)) {
+ content = content.replace(xLabelPattern, item.series.xaxis.options.axisLabel);
+ } else {
+ //remove %lx if axis label is undefined or axislabels plugin not present
+ content = content.replace(xLabelPattern, "");
+ }
+
+ // y axis label match
+ if (this.hasAxisLabel('yaxis', item)) {
+ content = content.replace(yLabelPattern, item.series.yaxis.options.axisLabel);
+ } else {
+ //remove %ly if axis label is undefined or axislabels plugin not present
+ content = content.replace(yLabelPattern, "");
+ }
+
+ // time mode axes with custom dateFormat
+ if (this.isTimeMode('xaxis', item) && this.isXDateFormat(item)) {
+ content = content.replace(xPattern, this.timestampToDate(x, this.tooltipOptions.xDateFormat, item.series.xaxis.options));
+ }
+ if (this.isTimeMode('yaxis', item) && this.isYDateFormat(item)) {
+ content = content.replace(yPattern, this.timestampToDate(y, this.tooltipOptions.yDateFormat, item.series.yaxis.options));
+ }
+
+ // set precision if defined
+ if (typeof x === 'number') {
+ content = this.adjustValPrecision(xPattern, content, x);
+ }
+ if (typeof y === 'number') {
+ content = this.adjustValPrecision(yPattern, content, y);
+ }
+
+ // change x from number to given label, if given
+ if (typeof item.series.xaxis.ticks !== 'undefined') {
+
+ var ticks;
+ if (this.hasRotatedXAxisTicks(item)) {
+ // xaxis.ticks will be an empty array if tickRotor is being used, but the values are available in rotatedTicks
+ ticks = 'rotatedTicks';
+ } else {
+ ticks = 'ticks';
+ }
+
+ // see https://github.com/krzysu/flot.tooltip/issues/65
+ var tickIndex = item.dataIndex + item.seriesIndex;
+
+ for (var xIndex in item.series.xaxis[ticks]) {
+ if (item.series.xaxis[ticks].hasOwnProperty(tickIndex) && !this.isTimeMode('xaxis', item)) {
+ var valueX = (this.isCategoriesMode('xaxis', item)) ? item.series.xaxis[ticks][tickIndex].label : item.series.xaxis[ticks][tickIndex].v;
+ if (valueX === x) {
+ content = content.replace(xPattern, item.series.xaxis[ticks][tickIndex].label.replace(/\$/g, '$$$$'));
+ }
+ }
+ }
+ }
+
+ // change y from number to given label, if given
+ if (typeof item.series.yaxis.ticks !== 'undefined') {
+ for (var yIndex in item.series.yaxis.ticks) {
+ if (item.series.yaxis.ticks.hasOwnProperty(yIndex)) {
+ var valueY = (this.isCategoriesMode('yaxis', item)) ? item.series.yaxis.ticks[yIndex].label : item.series.yaxis.ticks[yIndex].v;
+ if (valueY === y) {
+ content = content.replace(yPattern, item.series.yaxis.ticks[yIndex].label.replace(/\$/g, '$$$$'));
+ }
+ }
+ }
+ }
+
+ // if no value customization, use tickFormatter by default
+ if (typeof item.series.xaxis.tickFormatter !== 'undefined') {
+ //escape dollar
+ content = content.replace(xPatternWithoutPrecision, item.series.xaxis.tickFormatter(x, item.series.xaxis).replace(/\$/g, '$$'));
+ }
+ if (typeof item.series.yaxis.tickFormatter !== 'undefined') {
+ //escape dollar
+ content = content.replace(yPatternWithoutPrecision, item.series.yaxis.tickFormatter(y, item.series.yaxis).replace(/\$/g, '$$'));
+ }
+
+ return content;
+ };
+
+ // helpers just for readability
+ FlotTooltip.prototype.isTimeMode = function (axisName, item) {
+ return (typeof item.series[axisName].options.mode !== 'undefined' && item.series[axisName].options.mode === 'time');
+ };
+
+ FlotTooltip.prototype.isXDateFormat = function (item) {
+ return (typeof this.tooltipOptions.xDateFormat !== 'undefined' && this.tooltipOptions.xDateFormat !== null);
+ };
+
+ FlotTooltip.prototype.isYDateFormat = function (item) {
+ return (typeof this.tooltipOptions.yDateFormat !== 'undefined' && this.tooltipOptions.yDateFormat !== null);
+ };
+
+ FlotTooltip.prototype.isCategoriesMode = function (axisName, item) {
+ return (typeof item.series[axisName].options.mode !== 'undefined' && item.series[axisName].options.mode === 'categories');
+ };
+
+ //
+ FlotTooltip.prototype.timestampToDate = function (tmst, dateFormat, options) {
+ var theDate = $.plot.dateGenerator(tmst, options);
+ return $.plot.formatDate(theDate, dateFormat, this.tooltipOptions.monthNames, this.tooltipOptions.dayNames);
+ };
+
+ //
+ FlotTooltip.prototype.adjustValPrecision = function (pattern, content, value) {
+
+ var precision;
+ var matchResult = content.match(pattern);
+ if( matchResult !== null ) {
+ if(RegExp.$1 !== '') {
+ precision = RegExp.$1;
+ value = value.toFixed(precision);
+
+ // only replace content if precision exists, in other case use thickformater
+ content = content.replace(pattern, value);
+ }
+ }
+ return content;
+ };
+
+ // other plugins detection below
+
+ // check if flot-axislabels plugin (https://github.com/markrcote/flot-axislabels) is used and that an axis label is given
+ FlotTooltip.prototype.hasAxisLabel = function (axisName, item) {
+ return ($.inArray('axisLabels', this.plotPlugins) !== -1 && typeof item.series[axisName].options.axisLabel !== 'undefined' && item.series[axisName].options.axisLabel.length > 0);
+ };
+
+ // check whether flot-tickRotor, a plugin which allows rotation of X-axis ticks, is being used
+ FlotTooltip.prototype.hasRotatedXAxisTicks = function (item) {
+ return ($.inArray('tickRotor',this.plotPlugins) !== -1 && typeof item.series.xaxis.rotatedTicks !== 'undefined');
+ };
+
+ //
+ var init = function (plot) {
+ new FlotTooltip(plot);
+ };
+
+ // define Flot plugin
+ $.plot.plugins.push({
+ init: init,
+ options: defaultOptions,
+ name: 'tooltip',
+ version: '0.8.5'
+ });
+
+})(jQuery);
diff --git a/apps/OpenEnergyMonitor/myheatpump/myheatpump.js b/apps/OpenEnergyMonitor/myheatpump/myheatpump.js
index 4dceaf8..5f4ac19 100644
--- a/apps/OpenEnergyMonitor/myheatpump/myheatpump.js
+++ b/apps/OpenEnergyMonitor/myheatpump/myheatpump.js
@@ -47,6 +47,10 @@ config.app = {
"auto_detect_cooling":{"type":"checkbox", "default":false, "name": "Auto detect cooling", "description":"Auto detect summer cooling if cooling status feed is not present"},
"enable_process_daily":{"type":"checkbox", "default":false, "name": "Enable daily pre-processor", "description":"Enable split between water and space heating in daily view"},
"start_date": { "type": "value", "default": 0, "name": "Start date", "description": _("Start date for all time values (unix timestamp)") },
+
+ // solar
+ "solar_elec_kwh": { "type": "feed", "autoname": "solar_elec_kwh", "optional": true, "description": "Cumulative solar energy kWh" },
+
};
config.feeds = feed.list();
@@ -446,6 +450,11 @@ function resize() {
placeholder.height(height - top_offset);
+ // Add resize logic for the heat loss plot if it's visible
+ if ($("#heatloss-block").is(":visible")) {
+ plotHeatLossScatter(); // <<< CALL REMAINS
+ }
+
if (viewmode == "bargraph") {
bargraph_draw();
@@ -586,6 +595,155 @@ $("#clear-daily-data").click(function () {
});
});
+
+// --- Heat Loss Panel Toggle ---
+$("#heatloss-toggle").click(function () {
+ var $contentBlock = $("#heatloss-block");
+ var $toggleText = $("#heatloss-toggle-text");
+ var $arrow = $("#heatloss-arrow");
+
+ if ($contentBlock.is(":visible")) {
+ // Hiding Logic (Stays the same)
+ $contentBlock.slideUp(); // Start hiding animation
+ $(this).css("background-color", "");
+ $toggleText.text("SHOW HEAT DEMAND ANALYSIS");
+ $arrow.html("►");
+
+ } else {
+ // Showing Logic (Modified)
+ // These updates can happen immediately
+ $(this).css("background-color", "#4a6d8c");
+ $toggleText.text("HIDE HEAT DEMAND ANALYSIS");
+ $arrow.html("▼");
+
+ // Start the slideDown animation AND provide a callback function
+ $contentBlock.slideDown(function() {
+ // --- This code runs AFTER slideDown completes ---
+ console.log("Heat Loss Panel: slideDown complete.");
+
+ // Now it's safe to resize and plot
+ resize(); // Ensure container dimensions are recalculated based on final state
+ plotHeatLossScatter(); // Plot into the correctly sized, visible container
+
+ // Debugging log at the correct time
+ if (typeof daily_data !== 'undefined' && daily_data.combined_elec_kwh) {
+ console.log("Heat Loss Panel (Post-Slide): Accessing daily_data...");
+ } else {
+ console.log("Heat Loss Panel (Post-Slide): daily_data not yet available...");
+ }
+ // --- End of callback ---
+ });
+ }
+});
+// --- End Heat Loss Panel Toggle ---
+
+// 1. Minimum Delta T Input Change
+$("#heatloss_min_deltaT").on('input change', function() {
+ // Only replot if the panel is actually visible
+ if ($("#heatloss-block").is(":visible")) {
+ // Basic validation if needed (e.g., ensure it's a number)
+ // let value = parseFloat($(this).val());
+ // if (!isNaN(value)) { ... }
+ plotHeatLossScatter(); // Call the plotting function (defined in heatloss.js)
+ }
+});
+
+// 2. Fixed Room Temperature Checkbox Change
+$("#heatloss_fixed_roomT_check").on('change', function() {
+ var isChecked = $(this).is(":checked");
+ // Enable/disable the associated value input based on checkbox state
+ $("#heatloss_fixed_roomT_value").prop('disabled', !isChecked);
+
+ // Replot if the panel is visible
+ if ($("#heatloss-block").is(":visible")) {
+ plotHeatLossScatter();
+ }
+});
+
+// 3. Fixed Room Temperature Value Input Change
+$("#heatloss_fixed_roomT_value").on('input change', function() {
+ // Only replot if the panel is visible AND the checkbox is checked
+ if ($("#heatloss-block").is(":visible") && $("#heatloss_fixed_roomT_check").is(":checked")) {
+ plotHeatLossScatter();
+ }
+});
+
+// 4. Split Data Checkbox Change Event
+$("#heatloss_split_data_check").on('change', function() {
+ var isChecked = $(this).is(":checked");
+ var $radioButtons = $('input[name="heatloss_split_by"]');
+ var $regressionCheck = $("#heatloss_split_regression_check");
+
+ // Enable/disable radio buttons
+ $radioButtons.prop('disabled', !isChecked);
+ // Enable/disable the regression checkbox
+ $regressionCheck.prop('disabled', !isChecked);
+
+ // If main checkbox is unchecked, also uncheck radios and regression checkbox
+ if (!isChecked) {
+ $radioButtons.prop('checked', false);
+ $regressionCheck.prop('checked', false);
+ }
+ // Optional: If checking, and nothing is selected, select a default (e.g., year)
+ else if (isChecked && $radioButtons.filter(':checked').length === 0) {
+ $('#heatloss_split_by_year').prop('checked', true);
+ // Note: Manually setting 'checked' won't trigger its 'change' event here.
+ // If the plot needs to update immediately based on the default selection,
+ // you might need to explicitly call plotHeatLossScatter() here too,
+ // or trigger the change event: $('#heatloss_split_by_year').trigger('change');
+ }
+
+ // Replot if the panel is visible
+ if ($("#heatloss-block").is(":visible")) {
+ // Assuming plotHeatLossScatter is defined elsewhere
+ plotHeatLossScatter();
+ }
+});
+
+// 5. Split Data Radio Button Change Event
+$('input[name="heatloss_split_by"]').on('change', function() {
+ // Only replot if the panel is visible AND the main split checkbox is checked
+ if ($("#heatloss-block").is(":visible") && $("#heatloss_split_data_check").is(":checked")) {
+ // Assuming plotHeatLossScatter is defined elsewhere
+ plotHeatLossScatter();
+ }
+});
+
+// 6. Split Regression Checkbox Change Event
+$("#heatloss_split_regression_check").on('change', function() {
+ // Only replot if the panel is visible AND the main split checkbox is checked
+ if ($("#heatloss-block").is(":visible") && $("#heatloss_split_data_check").is(":checked")) {
+ // Assuming plotHeatLossScatter is defined elsewhere
+ plotHeatLossScatter();
+ }
+});
+
+
+// 7. Solar Gain Coloring Change event
+$("#heatloss_solar_gain_color").on('change', function() {
+ // Only replot if the panel is visible
+ if ($("#heatloss-block").is(":visible")) {
+ plotHeatLossScatter(); // Call the main plotting function
+ }
+});
+
+// 8. Minimum Quality Input Change
+$("#heatloss_min_quality").on('input change', function() {
+ // Only replot if the panel is actually visible
+ if ($("#heatloss-block").is(":visible")) {
+ plotHeatLossScatter(); // Call the plotting function (defined in heatloss.js)
+ }
+});
+
+// 8. Minimum Heat Input Change
+$("#heatloss_min_heat").on('input change', function() {
+ // Only replot if the panel is actually visible
+ if ($("#heatloss-block").is(":visible")) {
+ plotHeatLossScatter(); // Call the plotting function (defined in heatloss.js)
+ }
+});
+// --- End Heat Loss Control Event Listeners ---
+
$("#show_dhw_temp").click(function () {
if ($("#show_dhw_temp")[0].checked) {
show_dhw_temp = true;
@@ -595,4 +753,5 @@ $("#show_dhw_temp").click(function () {
if (viewmode == "powergraph") {
powergraph_draw();
}
-});
\ No newline at end of file
+});
+
diff --git a/apps/OpenEnergyMonitor/myheatpump/myheatpump.php b/apps/OpenEnergyMonitor/myheatpump/myheatpump.php
index 107c176..2f03eeb 100644
--- a/apps/OpenEnergyMonitor/myheatpump/myheatpump.php
+++ b/apps/OpenEnergyMonitor/myheatpump/myheatpump.php
@@ -13,6 +13,10 @@
+
+
+
+
@@ -426,7 +430,117 @@
+
+
+