diff --git a/fhem/contrib/commandref_static.pl b/fhem/contrib/commandref_static.pl new file mode 100755 index 000000000..e5fc60994 --- /dev/null +++ b/fhem/contrib/commandref_static.pl @@ -0,0 +1,199 @@ +#!/usr/bin/perl + +# $Id$ + +use strict; +use warnings; + +sub _cref_search { + my ($mod,$lang) = @_; + my $output = ""; + my $skip = 1; + my ($err,@text) = _cref_read($mod); + return $err if $err; + foreach my $l (@text) { + if($l =~ m/^=begin html$lang$/) { + $skip = 0; + } elsif($l =~ m/^=end html$lang$/) { + $skip = 1; + } elsif(!$skip) { + $output .= "$l\n"; + if($l =~ m,INSERT_DOC_FROM: ([^ ]+)/([^ /]+) ,) { + my ($dir, $re) = ($1, $2); + if(opendir(DH, $dir)) { + foreach my $file (grep { m/^$2$/ } readdir(DH)) { + $output .= cref_search("$dir/$file", $lang); + } + closedir(DH); + } + } + } + } + return $output; +} + +sub _cref_read { + my ($fileName) = @_; + my ($err, @ret); + if(open(FH, $fileName)) { + @ret = ; + close(FH); + chomp(@ret); + } else { + $err = "Can't open $fileName: $!"; + } + return ($err, @ret); +} + +sub _cref_write { + my ($fileName,$content) = @_; + + if(open(FH, ">$fileName")) { + binmode (FH); +# foreach my $l (@rows) { +# print FH $l,$nl; +# } + print FH, $content; + close(FH); + return undef; + } else { + return "Can't open $fileName: $!"; + } +} + +my $protVersion=1; +my @lang = ("EN", "DE"); +my $modDir = "FHEM"; +my $now = time(); + +for my $lang (@lang) { + my $sfx = ($lang eq "EN" ? "" : "_$lang"); + my %modData; + my $cmdref = "docs/commandref_frame$sfx.html"; + + open(FH, $cmdref) || die("Cant open $cmdref: $!\n"); + my $type = ""; + my $fileVersion = 0; + while(my $l = ) { + if($l =~ m//) { + $type = $1; next; + } + if($type && $l =~ m//) { + $modData{$1}{type} = $type; + $modData{$1}{"summary$sfx"} = $2; + $modData{$1}{ts} = $now; + } else { + $type = ""; + } + } + close(FH); + + $cmdref = "docs/commandref${sfx}.html"; + if(open(FH, $cmdref)) { + my $cmptime = (stat($cmdref))[9]; + my $type = "device"; + while(my $l = ) { + $type = $1 if($l =~ m,,); + $fileVersion = $1 if($type && $l =~ m//); + + if($l =~ m,(.*)(.*), && + !$modData{$1}{type}) { # commandref_frame has prio + $modData{$1}{type} = $type; + $modData{$1}{"summary$sfx"} = $2; + $modData{$1}{ts} = $cmptime; + } + + if($l =~ m,,) { + for my $ml (split(" ", $1)) { + my @kv=split(/[:,]/,$ml); + my $n = shift(@kv); + for my $v (@kv) { + $modData{$n}{modLinks}{$v} = 1; + } + } + } + } + close(FH); + } + + opendir(DH, $modDir) || die "Cant open $modDir: $!\n"; + while(my $fName = readdir DH) { + next if($fName !~ m/^\d\d_(.*)\.pm$/); + my $mName = $1; + my $ts = (stat("$modDir/$fName"))[9]; + if($protVersion != $fileVersion || + !$modData{$mName} || !$modData{$mName}{ts} || $modData{$mName}{ts}<$ts) { + #print "Checking $fName for $lang short description\n"; + + $modData{$mName}{type}="device" if(!$modData{$mName}{type}); + delete($modData{$mName}{modLinks}); + open(FH, "$modDir/$fName") || die("Cant open $modDir/$fName: $!\n"); + while(my $l = ) { + $modData{$mName}{type}=$1 if($l =~ m/^=item\s+(helper|command|device)/); + $modData{$mName}{$1} =$2 if($l =~ m/^=item\s+(summary[^ ]*)\s(.*)$/); + $modData{$mName}{modLinks}{$1} = 1 + if($l =~ m//); + } + close(FH); + print (_cref_search("$modDir/$fName",$lang)); + } + } + closedir(DH); + + $cmdref = "docs/commandref_frame${sfx}.html"; + open(IN, $cmdref) || die("Cant open $cmdref: $!\n"); + + $cmdref = ">docs/commandref${sfx}.html"; + open(OUT, $cmdref) || die("Cant open $cmdref: $!\n"); + + my $linkDumped = 0; + while(my $l = ) { + + print OUT $l; + if($l =~ m,\s*,) { + print OUT << 'EOF' + <script type="text/javascript" src="../pgm2/jquery.min.js"></script> + <script type="text/javascript" src="../pgm2/fhemdoc_static.js"></script> +EOF + } + + if($l =~ m,<!-- header:(.*) -->,) { + my @mList = sort {uc($a) cmp uc($b)} keys %modData; + if(!$linkDumped) { + my $ml = ""; + for my $m (@mList) { + next if(!$modData{$m}{modLinks}); + $ml .= " $m:".join(",", keys(%{$modData{$m}{modLinks}})); + } + print OUT "<div id='modLinks' style='display:none'>$ml</div>\n"; + $linkDumped = 1; + } + my $type = $1; + while(my $l = <IN>) { + last if($l !~ m/<a href="/); + } + print OUT "<table class='block summary class_$type' ". + "protVersion='$protVersion'>\n"; + my $rc = "odd"; + for my $m (@mList) { + next if(!$modData{$m}{type} || $modData{$m}{type} ne $type); + my $d = $modData{$m}{"summary$sfx"}; + if(!$d) { + my $osfx = ($lang eq "DE" ? "" : "_DE"); + $d = $modData{$m}{"summary$sfx"}; + if(!$d) { + $d = "keine Kurzbeschreibung vorhanden" if($lang eq "DE"); + $d = "no short description available" if($lang eq "EN"); + } + } + print OUT "<tr class='$rc'><td class='modname'><a href='#'>$m</a></td>". + "<td>$d</td><tr>\n"; + $rc = ($rc eq "odd" ? "even" : "odd"); + } + print OUT "</table>\n"; + } + + } + close(OUT); + close(IN); +} diff --git a/fhem/www/pgm2/fhemdoc_static.js b/fhem/www/pgm2/fhemdoc_static.js new file mode 100644 index 000000000..e59498a0b --- /dev/null +++ b/fhem/www/pgm2/fhemdoc_static.js @@ -0,0 +1,235 @@ +"use strict"; +// $Id$ + +var fd_loadedHash={}, fd_loadedList=[], fd_all={}, fd_allCnt, fd_progress=0, + fd_lang, fd_offsets=[], fd_scrolled=0, fd_modLinks={}, csrfToken="X"; + + +function +fd_status(txt) +{ + var errmsg = $("#errmsg"); + if(!$(errmsg).length) { + $('#menuScrollArea').append('<div id="errmsg">'); + errmsg = $("#errmsg"); + } + if(txt == "") + $(errmsg).remove(); + else + $(errmsg).html(txt); +} + +function +fd_fC(fn, callback) +{ + console.log("fd_fC:"+fn); + var p = location.pathname; + var cmd = p.substr(0,p.indexOf('/doc'))+'?cmd='+fn+csrfToken+'&XHR=1'; + $.ajax({ + url:cmd, method:'POST', cache:false, success:callback, + error:function(xhr, status, err) { + if(xhr.status == 400 && csrfToken) { + csrfToken = ""; + fd_csrfRefresh(function(){fd_fC(fn, callback)}); + } else { + console.log("FAIL ERR:"+xhr.status+" STAT:"+status); + } + } + }); +} + +function +loadOneDoc(mname, lang) +{ + var origLink = mname; + + function + done(err, calc) + { + if(fd_progress) { + fd_status(fd_progress+" / "+fd_allCnt); + if(++fd_progress > fd_allCnt) { + fd_progress = 0; + setTimeout(calcOffsets,100); // Firefox returns wrong offsets + fd_status(""); + } + } else { + if(calc) + setTimeout(calcOffsets,100); + if(!err) + setTimeout(function(){location.href = "#"+origLink;}, 100); + } + } + + if(fd_modLinks[mname]) + mname = fd_modLinks[mname]; + if(fd_loadedHash[mname] && fd_loadedHash[mname] == lang) + return done(false, false); + + fd_fC("help "+mname+" "+lang, function(ret){ + //console.log(mname+" "+lang+" => "+ret.length); + if(ret.indexOf("<html>") != 0 || ret.indexOf("<html>No help found") == 0) + return done(true, false); + ret = ret.replace(/<\/?html>/g,''); + ret = ret.replace(/Keine deutsche Hilfe gefunden!<br\/>/,''); + ret = '<div id="FD_'+mname+'">'+ret+'</div>'; + ret = ret.replace(/target="_blank"/g, ''); // revert help URL rewrite + ret = ret.replace(/href=".*?commandref.*?.html#/g, 'href="#'); + + if(fd_loadedHash[mname]) + $("div#FD_"+mname).remove(); + + if(!fd_loadedHash[mname]) + fd_loadedList.push(mname); + fd_loadedHash[mname] = lang; + fd_loadedList.sort(); + var idx=0; + while(fd_loadedList[idx] != mname) + idx++; + var toIns = "perl"; + if(idx < fd_loadedList.length-1) + toIns = fd_loadedList[idx+1]; + console.log("insert "+mname+" before "+toIns); + $(ret).insertBefore("a[name="+toIns+"]"); + addAHooks("div#FD_"+mname); + return done(false, true); + }); +} + +function +addAHooks(el) +{ + $(el).find("a[href]").each(function(){ + var href = $(this).attr("href"); + if(!href || href.indexOf("#") != 0) + return; + href = href.substr(1); + if(fd_modLinks[href] && !fd_loadedHash[href]) { + $(this).click(function(){ + $("a[href=#"+href+"]").unbind('click'); + loadOneDoc(href, fd_lang); + }); + } + }); +} + +function +calcOffsets() +{ + fd_offsets=[]; + for(var i1=0; i1<fd_loadedList.length; i1++) { + var cr = $("a[name="+fd_loadedList[i1]+"]").offset(); + fd_offsets.push(cr ? cr.top : -1); + } + checkScroll(); +} + +function +checkScroll() +{ + if(!fd_scrolled) { + setTimeout(checkScroll, 500); + return; + } + fd_scrolled = 0; + var viewTop=$(window).scrollTop(), viewBottom=viewTop+$(window).height(); + var idx=0; + while(idx<fd_offsets.length) { + if(fd_offsets[idx] >= viewTop && viewBottom > fd_offsets[idx]+30) + break; + idx++; + } + + if(idx >= fd_offsets.length) { + $("a#otherLang").hide(); + + } else { + var mname = fd_loadedList[idx]; + var l1 = fd_loadedHash[mname], l2 = (l1=="EN" ? "DE" : "EN"); + $("a#otherLang span.mod").html(mname); + $("a#otherLang span[lang="+l1+"]").hide(); + $("a#otherLang span[lang="+l2+"]").show(); + $("a#otherLang").show(); + } +} + +function +loadOtherLang() +{ + var mname = $("a#otherLang span.mod").html(); + loadOneDoc(mname, fd_loadedHash[mname]=="EN" ? "DE" : "EN"); +} + +function +fd_csrfRefresh(callback) +{ + console.log("fd_csrfRefresh"); + $.ajax({ + url:location.pathname.replace(/docs.*/,'')+"?XHR=1", + success: function(data, textStatus, request){ + csrfToken = request.getResponseHeader('x-fhem-csrftoken'); + csrfToken = csrfToken ? ("&fwcsrf="+csrfToken) : ""; + if(callback) + callback(); + } + }); +} + +$(document).ready(function(){ + var p = location.pathname; + fd_lang = p.substring(p.indexOf("commandref")+11,p.indexOf(".html")); + if(!fd_lang || fd_lang == '.') + fd_lang = "EN"; + + $("div#modLinks").each(function(){ + var a1 = $(this).html().split(" "); + for(var i1=0; i1<a1.length; i1++) { + var a2 = a1[i1].split(/[:,]/); + var mName = a2.shift(); + for(var i2=0; i2<a2.length; i2++) + if(!fd_modLinks[a2[i2]]) + fd_modLinks[a2[i2]] = mName; + } + }); + + $("a[name]").each(function(){ fd_loadedHash[$(this).attr("name")]=fd_lang; }); + $("table.summary td.modname a") + .each(function(){ + var mod = $(this).html(); + fd_all[mod]=1; + fd_modLinks[mod] = fd_modLinks[mod+"define"] = fd_modLinks[mod+"get"] = + fd_modLinks[mod+"set"] = fd_modLinks[mod+"attribute"]= mod; + }) + .click(function(e){ + e.preventDefault(); + loadOneDoc($(this).html(), fd_lang); + }); + + if(location.hash) + loadOneDoc(location.hash.substr(1), fd_lang); + + $(window).bind('hashchange', function() { + loadOneDoc(location.hash.substr(1), fd_lang); + }); + + $("a[name=loadAll]").show().click(function(e){ + e.preventDefault(); + $("a[name=loadAll]").hide(); + location.href = "#doctop"; + fd_allCnt = 0; + for(var m in fd_all) fd_allCnt++ + fd_progress = 1; + for(var mname in fd_all) + loadOneDoc(mname, fd_lang); + }); + + $("a#otherLang").click(loadOtherLang); + addAHooks("body"); + + window.onscroll = function(){ + if(!fd_scrolled++) + setTimeout(checkScroll, 500); + }; + + fd_csrfRefresh(); +});