"use strict"; FW_version["svg.js"] = "$Id$"; var svgCallback={}; if(!svgNS) { var svgNS = "http://www.w3.org/2000/svg"; var svg_initialized={}, lastHidden; var svg_db, svg_dbtbl = "SVG_KEYVALUE", svg_pastedata, svgCounter=0; } function svg_initDb(nextFn) { if(window.indexedDB == undefined) return; var dbreq = indexedDB.open("FHEM", 1); dbreq.onsuccess = function(op) { svg_db = op.target.result; nextFn() } dbreq.onerror = function(op) { var oldfn = window.onerror; window.onerror = undefined; // stupid FireFox private mode (Forum #64541) log("indexedDB.open Error: " + op.message); setTimeout(function(){window.onerror = oldfn;}, 100); } dbreq.onupgradeneeded = function(op) { svg_db = op.target.result; svg_db.createObjectStore(svg_dbtbl, { keyPath:"key" }); }; } function svg_save(key, value) { if(!svg_db) return; var os = svg_db.transaction([svg_dbtbl],"readwrite") .objectStore(svg_dbtbl); os.put({key:key, val:value}); } function svg_load(key, nextFn) { if(!svg_db) return; var req = svg_db.transaction([svg_dbtbl],"readonly") .objectStore(svg_dbtbl) .get(key); req.onsuccess = function(e) { if(req.result) nextFn(req.result.val); } } function svg_prepareHash(el) { var obj = { y_mul:0,y_h:0,y_min:0, decimals:0, t_mul:0,x_off:0,x_min:0, x_mul:0, scale:"" }; for(var name in obj) { var n = $(el).attr(name); if(name == "scale" && n) obj[name] = n; else if(n) obj[name] = parseFloat(n); } return obj; } // Called directly from the SVG.pm generated SVG. function svg_click(evt) { var t = evt.target; var o = svg_prepareHash(t); var svg=$(t).closest("svg"), x=evt.clientX, y=evt.clientY; if($(svg).parent().length) { // isEmbed=0 var off = $(svg).offset(); x -= off.left; y -= off.top; } var y_org = (((o.y_h-y)/o.y_mul)+o.y_min); if(o.scale == "log") y_org = Math.pow(10,y_org); else y_org = y_org.toFixed(o.decimals); var d = new Date((((x-o.x_min)/o.t_mul)+o.x_off) * 1000); var ts = (d.getHours() < 10 ? '0' : '') + d.getHours() + ":"+ (d.getMinutes() < 10 ? '0' : '') + d.getMinutes(); var tl = $(t).closest("svg").find("#svg_title"); $(tl).html($(t).attr("title")+": "+y_org+" ("+ts+")"); } function sv_menu(evt, embed) { var label = evt.target; var svg = $(label).closest("svg"); var svgNode = $(svg).get(0); var lid = $(label).attr("line_id"); var sel = $(svg).find("#"+lid); var selNode = $(sel).get(0); // horizintalLine* consists of a list of lines, too much work do handle // it. if(!selNode) return; var tl = $(svg).find("#svg_title"); var par = svgNode.par; var lines = $(svg).find("[line_id]"); var hidden = $(svg).find(".hidden"); function myPathSegList(node) // chrome 48+ removed the pathSegList interface { this.arr = $(node).attr("d").split(/ */); this.arr.splice(0,1); // remove M this.arr.splice(1,1); // remove L/Q/etc this.numberOfItems = this.arr.length; this.getItem = function(pos) { var xy = this.arr[pos].split(","); return { x:parseFloat(xy[0]), y:parseFloat(xy[1]) }; } } var embedOffsetY = 0; if(embed) { var name = $(evt.target).parent("svg").attr("id").substr(8); embedOffsetY = $("div.SVGplot.SVG_"+name).offset().top; } function showValOff() { $(svg).find("[id]").each(function(){delete($(this).get(0).showVal)}); $(svg).off("mousemove"); $(svg).off("mousedown"); if(par && par.circle) { $(par.circle).remove(); $(par.div).remove(); } } var sn = selNode.nodeName, pn = (sn=="path" ? "d" : "points"), arrName = (sn=="path" ? "pathSegList" : "points"); var textArr = ["Copy", "Paste", selNode.isHidden ? "Show line" : "Hide line", "Hide other lines", "Show all lines", selNode.showVal ? "Stop displaying values" : "Display plot values" ]; if(!selNode.showVal && $(selNode).attr("title").indexOf("/hr")> 0) // e.g. l/hr textArr.push("Integrate"); FW_menu(evt, label, textArr, [undefined, svg_pastedata == undefined, !selNode.isHidden && (lines.length - hidden.length) == 1, !selNode.isHidden && (lines.length - hidden.length) == 1, hidden.length==0, selNode.isHidden || (sn!="polyline" && sn!="path"), selNode.isHidden || (sn!="polyline" && sn!="path") ], function(arg) { //////////////////////////////////// copy if(arg == 0) { svg_pastedata = { key:"svg_pastedata", tag:sn, attr:pn, y_min:$(sel).attr("y_min"), y_mul:$(sel).attr("y_mul"), datapoints:$(sel).attr(pn) }; svg_save("svg_pastedata", svg_pastedata); } //////////////////////////////////// paste if(arg == 1) { var doc = $(svg).get(0).ownerDocument; var o=doc.createElementNS(svgNS, svg_pastedata.tag); o.setAttribute("class", "SVGplot pasted"); o.setAttribute(svg_pastedata.attr, svg_pastedata.datapoints); var h = parseFloat($(sel).attr("y_h")); var ny_mul = parseFloat(svg_pastedata.y_mul); var ny_min = parseInt(svg_pastedata.y_min); var y_mul = parseFloat($(sel).attr("y_mul")); var y_min = parseInt($(sel).attr("y_min")); var tr = "translate(0,"+ (h/y_mul+y_min-h/ny_mul-ny_min)*y_mul +") "+ "scale(1, "+ (y_mul/ny_mul) +") "; o.setAttribute("transform", tr); svgNode.appendChild(o); } //show/hide if(arg == 2) { setVisibility(lid, selNode.isHidden?1:0); $(label).attr("opacity", selNode.isHidden?0.4:1); } //hide other if(arg == 3) { $(svg).find("[id]").each(function(){ var id = $(this).attr("id"); if(id.indexOf("line_") != 0 ) return; var label = $(svg).find('[line_id="'+id+'"]'); if( !label.length ) // ignore lines with label none return; var sel = $(svg).find("#"+id); var selNode = $(sel).get(0); if( (selNode.isHidden?false:true) != (id == lid) ) setVisibility(id, id == lid); label.attr("opacity", id == lid?1:0.4); } ); } //show all if(arg == 4) { $(svg).find("[line_id]").attr("opacity",1); $(svg).find("[id]").each(function(){ var id = $(this).attr("id"); if(id.indexOf("line_") != 0 ) return; var sel = $(svg).find("#"+id); var selNode = $(sel).get(0); if( !selNode.isHidden ) return; setVisibility(id, 1); //$(svg).find('[line_id="'+id+'"]').attr("opacity",1); } ); } ////////////////////////////////// if(arg == 5 || // value display arg == 6) { // Integrate. Only for "per hour", title must contain /hr selNode.isInt = (arg == 6 ? true : false); var hadShowVal = selNode.showVal; showValOff(); if(!hadShowVal) { selNode.showVal = true; $(svg).mousemove(mousemove); if(arg == 6) { selNode.clicked = false; $(svg).mousedown(function(e) { selNode.clicked = !selNode.clicked; selNode.clickedOffset = 0; mousemove(e) }); } svgNode.par = par = svg_prepareHash(selNode); par.circle = $(svg).get(0).ownerDocument.createElementNS(svgNS, "circle"); $(par.circle).attr("id", "svgmarker").attr("r", "8"); $(svg).append(par.circle); par.div = $('
'); var parent = (embed ? $(embed).parent() : $(svg).parent()); $(parent).append(par.div); var pl = selNode[arrName]; if(!pl) selNode[arrName] = pl = new myPathSegList(selNode); if(pl.numberOfItems > 2) mousemove({pageX:pl.getItem(pl.numberOfItems-2).x}); } } if( arg >= 2 && arg <= 4 ) { var hidden = $(svg).find(".hidden"); if( lines.length - hidden.length == 1 ) { $(tl).attr("hiddentitle", $(tl).text()); if($(sel).attr(pn) != null) $(tl).text($(label).attr("title")); } else if( $(tl).attr("hiddentitle") ) { $(tl).text($(tl).attr("hiddentitle")); $(tl).removeAttr("hiddentitle") } } }, embed); function pad0(v) { return (v < 10 ? '0'+v :v); } function mousemove(e) { var xRaw = e.pageX, pl = selNode[arrName], l = pl.numberOfItems, i1; if(!embed) xRaw -= $(svg).offset().left; for(i1=0; i1 xRaw) break; if(i1==l || i1==0) return; var pp=pl.getItem(i1-1), pn=pl.getItem(i1); var xR = (xRaw-pp.x)/(pn.x-pp.x); // Compute interim values var yRaw = pp.y+xR*(pn.y-pp.y); var y = (((par.y_h-yRaw)/par.y_mul)+par.y_min); if(par.scale == "log") y = Math.pow(10,y); else y = y.toFixed(par.decimals); if(selNode.isInt) { if(selNode.clicked) { if(!selNode.clickedOffset) selNode.clickedOffset = i1-1; var sum = 0; var pn = pl.getItem(selNode.clickedOffset); var oy = ((par.y_h-pn.y)/par.y_mul)+par.y_min; var ox = (pn.x-par.x_min)/par.t_mul; for(var i2=selNode.clickedOffset+1; i2