"use strict"; var FW_serverGenerated; var FW_serverFirstMsg = (new Date()).getTime()/1000; var FW_serverLastMsg = FW_serverFirstMsg; var FW_isIE = (navigator.appVersion.indexOf("MSIE") > 0); var FW_isiOS = navigator.userAgent.match(/(iPad|iPhone|iPod)/); var FW_scripts = {}, FW_links = {}; var FW_docReady = false; var FW_root = "/fhem"; // root // createFn returns an HTML Element, which may contain // - setValueFn, which is called when data via longpoll arrives // - activateFn, which is called after the HTML element is part of the DOM. var FW_widgets = { select: { createFn:FW_createSelect }, slider: { createFn:FW_createSlider }, time: { createFn:FW_createTime }, noArg: { createFn:FW_createNoArg }, multiple: { createFn:FW_createMultiple }, "multiple-strict": { createFn:FW_createMultiple }, textfield: { createFn:FW_createTextField }, "textfield-long": { createFn:FW_createTextField } }; window.onbeforeunload = function(e) { FW_leaving = 1; return undefined; } function FW_replaceWidgets(parent) { parent.find("div.fhemWidget").each(function() { var dev=$(this).attr("dev"); var cmd=$(this).attr("cmd"); var rd=$(this).attr("reading"); var params = cmd.split(" "); FW_replaceWidget(this, dev, $(this).attr("arg").split(","), $(this).attr("current"), rd, params[0], params.slice(1), function(arg) { FW_cmd(FW_root+"?cmd=set "+dev+(params[0]=="state" ? "":" "+params[0])+ " "+arg+"&XHR=1"); }); }); } function FW_jqueryReadyFn() { FW_docReady = true; FW_serverGenerated = document.body.getAttribute("generated"); if(document.body.getAttribute("longpoll")) setTimeout("FW_longpoll()", 100); $("a[href]").each(function() { FW_replaceLink(this); }) $("head script").each(function() { var sname = $(this).attr("src"), p = FW_scripts[sname]; if(!p) { FW_scripts[sname] = { loaded:true }; return; } FW_scripts[sname].loaded = true; if(p.callbacks && !p.called) { p.called = true; // Avoid endless loop for(var i1=0; i1< p.callbacks.length; i1++) if(p.callbacks[i1]) // pushing undefined callbacks on the stack is ok p.callbacks[i1](); delete(p.callbacks); } }); $("head link").each(function() { FW_links[$(this).attr("href")] = 1 }); $("div.makeSelect select").each(function() { FW_detailSelect(this); $(this).change(FW_detailSelect); }); // Activate the widgets var r = $("head").attr("root"); if(r) FW_root = r; FW_replaceWidgets($("html")); // Fix the td count by setting colspan on the last column $("table.block.wide").each(function(){ // table var id = $(this).attr("id"); if(!id || id.indexOf("TYPE") != 0) return; var maxTd=0, tdCount=[]; $(this).find("tr").each(function(){ // count the td's var cnt=0; $(this).find("td").each(function(){ cnt++; }); if(maxTd < cnt) maxTd = cnt; tdCount.push(cnt); }); $(this).find("tr").each(function(){ // set the colspan $(this).find("td").last().attr("colspan", maxTd-tdCount.shift()+1); }); }); // Replace the FORM-POST in detail-view by XHR /* Inactive, as Internals and Attributes arent auto updated. $("form input[type=submit]").click(function(e) { var cmd = ""; $(this).parent().find("[name]").each(function() { cmd += (cmd?"&":"")+$(this).attr("name")+"="+$(this).val(); }); if(cmd.indexOf("detail=") < 0) return; e.preventDefault(); FW_cmd(FW_root+"?"+cmd+"&XHR=1"); }); */ $("#saveCheck") .css("cursor", "pointer") .click(function(){ var parent = this; FW_cmd(FW_root+"?cmd=save ?&XHR=1", function(data) { FW_okDialog('
'+data+'
',parent); }); }); $("form").each(function(){ // shutdown handling var input = $(this).find("input.maininput"); if(!input.length) return; $(this).on("submit", function() { if($(input).val().match(/^\s*shutdown/)) { FW_cmd(FW_root+"?XHR=1&cmd="+$(input).val()); $(input).val(""); return false; } return true; }); }); $("div.devSpecHelp a").each(function(){ // Help on detail window var dev = $(this).attr("href").split("#").pop(); $(this).attr("href", "#"); $(this).click(function(evt){ if($("#devSpecHelp").length) { $("#devSpecHelp").remove(); return; } $("#content").append('
'); FW_cmd(FW_root+"?cmd=help "+dev+"&XHR=1", function(data) { $("#devSpecHelp").html(data); var off = $("#devSpecHelp").position().top-20; $('body, html').animate({scrollTop:off}, 500); }); }); }); } if(window.jQuery) { $(document).ready(FW_jqueryReadyFn); } else { // FLOORPLAN compatibility loadScript("pgm2/jquery.min.js", function() { loadScript("pgm2/jquery-ui.min.js", function() { FW_jqueryReadyFn(); }, true); }, true); } // FLOORPLAN compatibility function FW_delayedStart() { setTimeout("FW_longpoll()", 100); } function log(txt) { var d = new Date(); var ms = ("000"+(d.getMilliseconds()%1000)); ms = ms.substr(ms.length-3,3); txt = d.toTimeString().substring(0,8)+"."+ms+" "+txt; if(typeof window.console != "undefined") console.log(txt); } function addcsrf(arg) { var csrf = document.body.getAttribute('fwcsrf'); if(csrf && arg.indexOf('fwcsrf') < 0) arg += '&fwcsrf='+csrf; return arg; } function FW_cmd(arg, callback) { log("FW_cmd:"+arg); arg = addcsrf(arg); var req = new XMLHttpRequest(); req.open("POST", arg, true); req.send(null); req.onreadystatechange = function(){ if(req.readyState == 4 && req.responseText) { if(callback) callback(req.responseText); else FW_errmsg(req.responseText, 5000); } } } function FW_errmsg(txt, timeout) { log("ERRMSG:"+txt+"<"); var errmsg = document.getElementById("errmsg"); if(!errmsg) { if(txt == "") return; errmsg = document.createElement('div'); errmsg.setAttribute("id","errmsg"); document.body.appendChild(errmsg); } if(txt == "") { document.body.removeChild(errmsg); return; } errmsg.innerHTML = txt; if(timeout) setTimeout("FW_errmsg('')", timeout); } function FW_okDialog(txt, parent) { var div = $("
"); $(div).html(txt); $("body").append(div); $(div).dialog({ dialogClass:"no-close", modal:true, width:"auto", closeOnEscape:true, maxWidth:$(window).width()*0.9, maxHeight:$(window).height()*0.9, buttons: [{text:"OK", click:function(){ $(this).dialog("close"); $(div).remove(); }}] }); FW_replaceWidgets(div); $(div).find("a[href]").each(function(){FW_replaceLink(this);}); //Forum #33766 if(parent) $(div).dialog( "option", "position", { my: "left top", at: "right bottom", of: parent, collision: "flipfit" }); } function FW_menu(evt, el, arr, dis, fn, embedEl) { if(!embedEl) evt.stopPropagation(); if($("#fwmenu").length) { delfwmenu(); return; } var html = ''; $("body").append(html); function delfwmenu() { $("ul#fwmenu").remove(); $('html').unbind('click.fwmenu'); } var wt = $(window).scrollTop(); $("#fwmenu") .menu({ select: function(e,ui) { // changes the scrollTop(); e.stopPropagation(); fn($(e.currentTarget).find("[row]").attr("row")); delfwmenu(); setTimeout(function(){ $(window).scrollTop(wt) }, 1); // Bug in select? } }); var off = $(el).offset(); if(embedEl) { var embOff = $(embedEl).offset(); off.top += embOff.top; off.left += embOff.left; } var dH = $("#fwmenu").height(), dW = $("#fwmenu").width(), wH = $(window).height(), wW = $(window).width(); var ey = off.top+dH+20, ex = off.left+dW; if(ex>wW && ey>wH) { off.top -= dH; off.left -= (dW+16); } else if(ey > wH) { off.top -= dH; off.left += 20; } else if(ex > wW) { off.left -= (dW+16); } else { off.top += 20; } $("#fwmenu").css(off); $('html').bind('click.fwmenu', function() { delfwmenu(); }); } function FW_replaceLink(el) { var attr = $(el).attr("href"); var ma = attr.match(/^(.*\?)(cmd[^=]*=.*)$/); if(ma == null || ma.length == 0 || !ma[2].match(/=(save|set)/)) return; $(el).removeAttr("href"); $(el).click(function() { FW_cmd(attr+"&XHR=1"); }); $(el).css("cursor", "pointer"); } /*************** LONGPOLL START **************/ var FW_pollConn; var FW_longpollOffset = 0; var FW_leaving; function FW_doUpdate() { if(FW_pollConn.readyState == 4 && !FW_leaving) { FW_errmsg("Connection lost, trying a reconnect every 5 seconds.", 4900); setTimeout(FW_longpoll, 5000); return; // some problem connecting } if(FW_pollConn.readyState != 3) return; var input = FW_pollConn.responseText; var devs = new Array(); if(input.length <= FW_longpollOffset) return; FW_serverLastMsg = (new Date()).getTime()/1000; for(;;) { var nOff = input.indexOf("\n", FW_longpollOffset); if(nOff < 0) break; var l = input.substr(FW_longpollOffset, nOff-FW_longpollOffset); FW_longpollOffset = nOff+1; log("Rcvd: "+(l.length>132 ? l.substring(0,132)+"...("+l.length+")":l)); if(!l.length) continue; var d = JSON.parse(l); if(d.length != 3) continue; if( d[0].match(/^#FHEMWEB:/) ) { eval(d[1]); } else { $("[informId='"+d[0]+"']").each(function(){ if(this.setValueFn) { // change the select/etc value this.setValueFn(d[1]); } else { $(this).html(d[2]); // Readings-Value if(d[0].match(/-ts$/)) // timestamps $(this).addClass('changed'); $(this).find("a[href]").each(function() { FW_replaceLink(this) }); } }); } for(var w in FW_widgets) if(FW_widgets[w].updateLine) // updateLine is deprecated, use setValueFn FW_widgets[w].updateLine(d); devs.push(d); } for(var w in FW_widgets) if(FW_widgets[w].updateDevs) // used for SVG to avoid double-reloads FW_widgets[w].updateDevs(devs); // reset the connection to avoid memory problems if(FW_longpollOffset > 1024*1024 && FW_longpollOffset==input.length) FW_longpoll(); } function FW_longpoll() { FW_longpollOffset = 0; if(FW_pollConn) { FW_leaving = 1; FW_pollConn.abort(); } FW_pollConn = new XMLHttpRequest(); FW_leaving = 0; // Build the notify filter for the backend var filter = $("body").attr("longpollfilter"); if(filter == null) filter = ""; if(filter == "") { $("embed").each(function() { if($(this.getSVGDocument()).find("svg[flog]").attr("flog")) filter=".*"; }); } if(filter == "") { var sa = location.search.substring(1).split("&"); for(var i = 0; i < sa.length; i++) { if(sa[i].substring(0,5) == "room=") filter=sa[i]; if(sa[i].substring(0,7) == "detail=") filter=sa[i].substring(7); } } if($("#floorplan").length>0) //floorplan special filter += ";iconPath="+$("body").attr("name"); if(filter == "") { var content = document.getElementById("content"); if(content) { var room = content.getAttribute("room"); if(room) filter="room="+room; } } var iP = document.body.getAttribute("iconPath"); if(iP != null) filter = filter +";iconPath="+iP; var since = "null"; if(FW_serverGenerated) since = FW_serverLastMsg + (FW_serverGenerated-FW_serverFirstMsg); var query = location.pathname+"?XHR=1"+ "&inform=type=status;filter="+filter+";since="+since+";fmt=JSON"+ "×tamp="+new Date().getTime(); query = addcsrf(query); FW_pollConn.open("GET", query, true); FW_pollConn.onreadystatechange = FW_doUpdate; FW_pollConn.send(null); log("Longpoll with filter "+filter); } /*************** LONGPOLL END **************/ /*************** WIDGETS START **************/ /*************** "Double" select in detail window ****/ function FW_detailSelect(selEl) { if(selEl.target) selEl = selEl.target; var selVal = $(selEl).val(); var div = $(selEl).closest("div.makeSelect"); var arg, listArr = $(div).attr("list").split(" "), devName = $(div).attr("dev"), cmd = $(div).attr("cmd"); for(var i1=0; i1 selVal.length) vArr = arg.substr(selVal.length+1).split(","); var newEl = FW_replaceWidget($(selEl).next(), devName, vArr,undefined,selVal); if(cmd == "attr") FW_queryValue('{AttrVal("'+devName+'","'+selVal+'","")}', newEl); if(cmd == "set") FW_queryValue('{ReadingsVal("'+devName+'","'+selVal+'","")}', newEl); } function FW_replaceWidget(oldEl, devName, vArr, currVal, reading, set, params, cmd) { var newEl, wn; var elName = $(oldEl).attr("name"); if(!elName) elName = $(oldEl).find("[name]").attr("name"); if(vArr.length == 0) { // No parameters, input field newEl = FW_createTextField(elName, devName, ["textField"], currVal, set, params, cmd); wn = "textField"; } else { for(wn in FW_widgets) { if(FW_widgets[wn].createFn) { newEl = FW_widgets[wn].createFn(elName, devName, vArr, currVal, set, params, cmd); if(newEl) break; } } if(!newEl) { // Select as fallback vArr.unshift("select"); newEl = FW_createSelect(elName, devName, vArr, currVal, set, params, cmd); wn = "select"; } } if(!newEl) { // Simple link newEl = $(''); $(newEl).click(function(arg) { cmd(params[0]) }); $(oldEl).replaceWith(newEl); return newEl; } $(newEl).addClass(wn+"_widget"); if( $(newEl).find("[informId]").length == 0 && !$(newEl).attr("informId") ) { if(reading && reading == "state") $(newEl).attr("informId", devName); else if(reading) $(newEl).attr("informId", devName+"-"+reading); } $(oldEl).replaceWith(newEl); if(newEl.activateFn) // CSS is not applied if newEl is not in the document newEl.activateFn(); return newEl; } function FW_queryValue(cmd, el) { log("FW_queryValue:"+cmd); var query = location.pathname+"?cmd="+cmd+"&XHR=1"; query = addcsrf(query); var qConn = new XMLHttpRequest(); qConn.onreadystatechange = function() { if(qConn.readyState != 4) return; var qResp = qConn.responseText.replace(/[\r\n]/g, ""); if(el.setValueFn) el.setValueFn(qResp); qConn.abort(); } qConn.open("GET", query, true); qConn.send(null); } function FW_querySetSelected(el, val) // called by the attribute links { $("#"+el).val(val); FW_detailSelect("#"+el); } /*************** TEXTFIELD **************/ function FW_createTextField(elName, devName, vArr, currVal, set, params, cmd) { if(vArr.length != 1 || (vArr[0] != "textField" && vArr[0] != "textField-long") || (params && params.length)) return undefined; var is_long = (vArr[0] == "textField-long"); var newEl = $("
").get(0); if(set && set != "state") $(newEl).append(set+":"); $(newEl).append(''); var inp = $(newEl).find("input").get(0); if(elName) $(inp).attr('name', elName); if(currVal != undefined) $(inp).val(currVal); function addBlur() { if(cmd) $(inp).blur(function() { cmd($(inp).val()) }); }; newEl.setValueFn = function(arg){ $(inp).val(arg) }; addBlur(); var myFunc = function(){ $(inp).unbind("blur"); $('body').append( '