"use strict"; // "$Id$"; 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, FW_longpollType, FW_csrfToken, FW_csrfOk=true; var FW_root = "/fhem"; // root var embedLoadRetry = 100; // 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; } window.onerror = function(errMsg, url, lineno) { url = url.replace(/.*\//,''); if($("body").attr("data-confirmJSError") != 0) FW_okDialog(url+" line "+lineno+":
"+errMsg); } 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(" "); var type=$(this).attr("type"); if( type == undefined ) type = "set"; FW_replaceWidget(this, dev, $(this).attr("arg").split(","), $(this).attr("current"), rd, params[0], params.slice(1), function(arg) { FW_cmd(FW_root+"?cmd="+type+" "+dev+ (params[0]=="state" ? "":" "+params[0])+" "+arg+"&XHR=1"); }); }); } function FW_jqueryReadyFn() { if(FW_docReady) // loading fhemweb.js twice is hard to debug return; FW_docReady = true; FW_serverGenerated = $("body").attr("generated"); FW_longpollType = $("body").attr("longpoll"); if(FW_longpollType != "0") setTimeout("FW_longpoll()", 100); FW_csrfToken = $("body").attr('fwcsrf'); $("a").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")); FW_confirmDelete(); // 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"); }); */ $("form input.get[type=submit]").click(function(e) { //"get" via XHR to dialog e.preventDefault(); var cmd = "", el=this; $(el).parent().find("input,[name]").each(function() { cmd += (cmd?"&":"")+encodeURIComponent($(this).attr("name"))+ "="+encodeURIComponent($(this).val()); }); FW_cmd(FW_root+"?"+cmd+"&XHR=1&addLinks=1", function(data) { if(!data.match(/^[\r\n]*$/)) // ignore empty answers FW_okDialog('
'+data+'
', el); }); }); $("#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(e) { var val = $(input).val(); if(val.match(/^\s*shutdown/)) { FW_cmd(FW_root+"?XHR=1&cmd="+val); $(input).val(""); return false; } else if(val.match(/^\s*get\s+/)) { // make get use xhr instead of reload //return true; FW_cmd(FW_root+"?cmd="+encodeURIComponent(val)+"&XHR=1", function(data){ if( !data.match( /^.*<\/html>/ ) ) { data = data.replace( '<', '<' ); data = '
'+data+'
'; } if( location.href.indexOf('?') === -1 ) $('#content').html(data); else FW_okDialog(data); }); e.preventDefault(); $(input).val(""); return false; } return true; }); }); $("div.devSpecHelp a").each(function(){ // Help on detail window var dev = FW_getLink(this).split("#").pop(); $(this).unbind("click"); $(this).attr("href", "#"); // Desktop: show underlined Text $(this).removeAttr("onclick"); $(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); }); }); }); $("table.attributes tr div.dname") // Click on attribute fills input value .each(function(){ $(this) .html(''+$(this).html()+'') .css({cursor:"pointer"}) .click(function(){ var aname = "#sel_attr"+$(this).attr("data-name").replace(/\./g,'_'); $(aname).val($(this).text()); FW_detailSelect(aname); }); }); $("[name=icon-filter]").on("change keyup paste", function() { clearTimeout($.data(this, 'delayTimer')); var wait = setTimeout(FW_filterIcons, 300); $(this).data('delayTimer', wait); }); FW_smallScreenCommands(); FW_inlineModify(); FW_rawDef(); } function FW_filterIcons() { var icons = $('.dist[title]'); icons.show(); var filterText = $('[name=icon-filter]').val(); if (filterText != '') { var re = RegExp(filterText,"i"); icons.filter(function() { return !re.test(this.title); }).hide(); } } function FW_confirmDelete() { var b = $("body"); var cd = $(b).attr("data-confirmDelete"); if(!cd || cd == 0) return; var wn = $(b).attr("data-webName"); $("div#content").find("a").each(function(){ var href = $(this).attr("href"); if(!href) return; var ma = $(this).attr("href").match(/.*cmd[^=]*=(delete[^&]*).*$/); if(!ma || ma.length != 2) return; $(this).attr("href", "#"); $(this).unbind("click"); $(this).click(function(e){ e.preventDefault(); var div = $("
"); $(div).html("Do you really want to "+ma[1]+"?

"+ " Skip this dialog in the future"); $("body").append(div); function doClose() { if($(div).find("input:checked").length) FW_cmd(FW_root+"?cmd=attr "+wn+" confirmDelete 0&XHR=1"); $(this).dialog("close"); $(div).remove(); } $(div).dialog({ dialogClass:"no-close", modal:true, width:"auto", closeOnEscape:true, maxWidth:$(window).width()*0.9, maxHeight:$(window).height()*0.9, buttons: [ {text:"Yes", click:function(){ location.href = ma[0]; doClose(); }}, {text:"No", click:function(){ doClose(); }}] }); }); }); } // Show the webCmd list in a dialog if: smallScreen & hiddenroom=detail & room function FW_smallScreenCommands() { if($("div#menu select").length == 0 || // not SmallScreen $("div#content").attr("room") == undefined || // not room Overview $("div#content div.col1 a").length > 0) // no hiddenroom=detail return; $("div#content div.col1").each(function(){ var tr = $(this).closest("tr"); if($(tr).find("> td").length <= 2) return; $(this).html(""+$(this).html()+""); $(this).find("a").click(function(){ var t = $("
"), row=0; $(tr).find("> td").each(function(){ $(t).append(""); if(row++ == 0) { $(t).find("tr:last").append($(this).find("a").html()); } else { $(this).attr("data-orig", 1); this.orig=$(this).parent(); $(t).find("tr:last").append($(this).detach()); } }); FW_okDialog(t, this, function(){ $("#FW_okDialog [data-orig]").each(function(){ $(this).detach().appendTo(this.orig); }); }); }); }); } 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) { if(typeof FW_csrfToken != "undefined") { arg = arg.replace(/&fwcsrf=[^&]*/,''); arg += '&fwcsrf='+encodeURIComponent(FW_csrfToken); } return arg; } function FW_csrfRefresh(callback) { log("FW_csrfRefresh, last was "+(FW_csrfOk ? "ok":"bad")); if(!FW_csrfOk) // avoid endless loop return; $.ajax({ url:location.pathname+"?XHR=1", success: function(data, textStatus, request){ FW_csrfToken = request.getResponseHeader('x-fhem-csrftoken'); FW_csrfOk = false; if(callback) callback(); } }); } function FW_cmd(arg, callback) { log("FW_cmd:"+arg); $.ajax({ url:addcsrf(arg)+'&fw_id='+$("body").attr('fw_id'), method:'POST', success: function(data, textStatus, req){ FW_csrfOk = true; if(callback) callback(req.responseText); else if(req.responseText) FW_errmsg(req.responseText, 5000); }, error:function(xhr, status, err) { if(xhr.status == 400 && typeof FW_csrfToken != "undefined") { FW_csrfToken = ""; FW_csrfRefresh(function(){FW_cmd(arg, callback)}); } } }); } 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, removeFn) { var div = $("
"); $(div).html(txt); $("body").append(div); var oldPos = $("body").scrollTop(); $(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"); if(removeFn) removeFn(); $(div).remove(); }}] }); FW_replaceWidgets(div); $(div).find("a").each(function(){FW_replaceLink(this);}); //Forum #33766 if(parent) $(div).dialog( "option", "position", { my: "left top", at: "right bottom", of: parent, collision: "flipfit" }); setTimeout(function(){$("body").scrollTop(oldPos);}, 1); // Not ideal. } 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_getLink(el) { var attr = $(el).attr("href"); if(!attr) { attr = $(el).attr("onclick"); // Tablet/smallScreen version if(!attr) return ""; attr = attr.replace(/^location.href='/,''); attr = attr.replace(/'$/,''); } return attr; } function FW_replaceLink(el) { var attr = FW_getLink(el); if(!attr) return; var ma = attr.match(/^(.*\?)(cmd[^=]*=.*)$/); if(ma == null || ma.length == 0 || !ma[2].match(/=(save|set)/)) { ma = attr.match(new RegExp("^"+FW_root)); // Avoid "Connection lost" @iOS if(ma) { $(el).click(function(e) { // Open link in window/tab, Forum #39154 if(e.shiftKey || e.ctrlKey || e.metaKey || e.button == 1) return; e.preventDefault(); FW_leaving = 1; if($(el).attr("target") == "_blank") { window.open(attr, '_blank').focus(); } else { location.href = attr; } }); } return; } $(el).removeAttr("href"); $(el).removeAttr("onclick"); $(el).click(function() { FW_cmd(attr+"&XHR=1", function(txt){ if(!txt) return; if(ma[2].match(/=set/)) // Forum #38875 FW_okDialog('
'+txt+'
', el);
      else
        FW_errmsg(txt, 5000);
    });
  });
  $(el).css("cursor", "pointer");
}

function
FW_inlineModify()       // Do not generate a new HTML page upon pressing modify
{
  var cm;
	
  if( typeof AddCodeMirror == 'function' ) {
    // init codemirror for FW_style edit textarea
    var s = $('textarea[name="data"]');
    if( s.length && !s[0].editor ) {
      s[0].editor = true; AddCodeMirror( s[0] );
    }
  }

  $('#DEFa').click(function(){
    var old = $('#edit').css('display');
    $('#edit').css('display', old=='none' ? 'block' : 'none');
    $('#disp').css('display', old=='none' ? 'none' : 'block');
    if( typeof AddCodeMirror == 'function' ) {
      var s=document.getElementById("edit").getElementsByTagName("textarea");
      if(!s[0].editor) { 
        s[0].editor=true; AddCodeMirror(s[0], function(pcm) {cm = pcm;});
      }
    }
    });
    
  $("div input.psc[type=submit]:not(.get)").click(function(e){
    e.preventDefault();
    var newDef = typeof cm !== 'undefined' ?
                 cm.getValue() : $(this).closest("form").find("textarea").val();
    var cmd = $(this).attr("name")+"="+$(this).attr("value")+" "+newDef;
    var isDef = true;

    if( newDef == undefined ) {
      isDef = false;
      var div = $(this).closest("div.makeSelect");
      var devName = $(div).attr("dev"),
          cmd = $(div).attr("cmd");
      var sel = $(this).closest("form").find("select");
      var arg = $(sel).val();
      var ifid = (devName+"-"+arg).replace(/\./g, '\\.');
      if($(".dval[informid="+ifid+"]").length == 0) {
        console.log(this);
        $(this).unbind('click').click();// No element found to replace, reload
        return;
      }
      newDef = $(this).closest("form").find("input:text").val();
      if(newDef == undefined)
        newDef = $(this).closest("form").find("select:last").val();
      cmd = $(this).attr("name")+"="+cmd+" "+devName+" "+arg+" "+newDef;
    }

    FW_cmd(FW_root+"?"+encodeURIComponent(cmd)+"&XHR=1", function(resp){
      if(resp) {
        if(resp.indexOf("\n") >= 0)
          resp = '
'+resp+'
'; return FW_okDialog(resp); } newDef = newDef.replace(/&/g, '&') // Same as in 01_FHEMWEB .replace(//g, '>'); if(isDef) { if(newDef.indexOf("\n") >= 0) newDef = '
'+newDef+'
'; $("div#disp").html(newDef).css("display", ""); $("div#edit").css("display", "none"); } }); }); } function FW_rawDef() { $("div.rawDef a").each(function(){ // Help on detail window var dev = FW_getLink(this).split(" ").pop().split("&")[0]; $(this).unbind("click"); $(this).attr("href", "#"); // Desktop: show underlined Text $(this).removeAttr("onclick"); $(this).click(function(evt){ if($("#rawDef").length) { $("#rawDef").remove(); return; } $("#content").append('
'+ '