/*****************
  Pruefung auf wesentliche Features; bei Misserfolg weiterleiten an "requirements"
*/
if( ! DOM) {
  top.document.location.href="00.requirements.html";
}


/*****************
  Meldungsfunktionen fuer Fehleranalyse
*/

var maKzch = "";
function ma( text) {
  if( maKzch) return;
  maKzch = prompt( text + " (keine Angabe => weiter melden)", maKzch);
}

function maKnoten( text, n) {
  var tName; try { tName = n.tagName;  } catch(e) { tName = "### err"; }
  var nType; try { nType = n.nodeType; } catch(e) { nType = "### err"; }
  var nVal;  try { nVal  = n.nodeValue;} catch(e) { nVal  = "### err"; }
  var is;    try { id    = n.id;       } catch(e) { id    = "### err"; }
  var data;  try { data  = n.data;     } catch(e) { data  = "### err"; }
  ma( text
    + ": tag=" + tName
    + ", type="+ nType
    + ", val=" + nVal
    + ", id="  + id
    + ", data="+ data);
}


/*****************
 Vorlageelement expandieren

Die folgenden Routinen implementieren einen Mechanismus, mit dem ein
bestimmtes Element mit saemtlichen untergeordneten Elementen vervielfaeltigt
werden kann. Dabei werden in jeder Kopie Platzhalter gegen die Werte einer
Tabelle ausgetauscht. Die Tabelle wird als Javascript-Text uebergeben; damit
kann die Definition der Tabelle in eine separate Datei ausgelagert werden.

Parameter fuer die im folgenden vorgestellten Funktionen:
  iVorlageId = Id des Elements (mitsamt Unterelementen), das vervielfaeltigt
    werden soll
  iTab = die Zeichenfolge, die die Tabelle enthaelt, die die Zeilen enthaelt,
    die die einzelnen Platzhalterwerte enthalten; zur Syntax siehe unten
  iMatrix = der Inhalt aus einem "iTab" konvertiert in eine Matrix
  iAttribute = die Liste der Attribute, in denen nach Platzhaltern gesucht
    werden soll

Funktionsuebersicht
  veText, veKnoten, veBaum ... sind lokaler Natur; sie werden nicht von
    ausserhalb aufgerufen.
  veTab2Matrix( iTab) ... konvertiert Zeichenfolge in Matrix
  veMatrixFilter( iTMatrix, iFilter, iTage) ... filtert eine Terminmatrix
  veTab2KMatrix( iKontakte, iFilter) ... konvertiert eine Zeichenfolge mit
    Kontakten in eine Matrix und bereitet diese auf
  veMatrix( iVorlageId, iMatrix, iAttribute) ... vervielfaeltigt "iVorlageld"
    je Datensatz in der Matrix
  veStart( iVorlageId, iTab, iAttribute) ... wie "veMatrix", aber an Stelle
    einer Matrix direkt eine Zeichenfolgetabelle "iTab"
  veTermine( iVorlageId, iTab, iFilter, iTage, iAttribute) ... wie "veStart",
    erwartet aber eine Terminmatrix und filtert diese zuvor


Tabellendefinition/-syntax:
    Die Zeilen in der Tabelle muessen durch das Dollarzeichen "$" voneinander
  getrennt sein, die Felder mit dem Kringelzeichen "°". Zeilen, die mit dem
  Zeichen "@" beginnen, werden ignoriert (Kommentarzeilen).
    Leerzeilen werden ignoriert. Schliessende Leerzeichen am Ende der Werte
  werden abgeschnitten ("rtrim"), so dass sich die Wertelisten wie Tabellen
  formatiert angeben lassen. Beispielsweise kann eine Liste wie folgt
  angegeben werden:
    tab="\
° Feld 0  Feld 1     Feld 2 \
$\
nullnull °null-1     °1null   $\
z1s0     °11         °1.2     $\
zweinull °z2s1       °feld2-2 $\
";
    Damit lassen sich Tabelleninhalte fuellen mit einfachen Javascript-
  Dateien, die nur eine Listendefinition allein enthalten. Lediglich
  die erste Zeile (tab="\), das Zeilenende ($\) und die letzte Zeile
  (") muessen stimmen.
    Die Platzhalter werden ersetzt in Texten (zwischen oeffnendem und
  schliessendem Tag) und in bestimmten Attributen. Welche Attribute das
  sind, kann man ueber den Parameter "iAttribute" angeben, die einzelnen
  Attributnamen voneeinander mit Komma getrennt.
  */


function veText( iText, iWerteArr) {
  /* ersetzt im angegebenen "iText" die Platzhalter $0, ..., $n gegen die im
  Feld "iWerteArr" angegebenen Werte; nicht verwendete Werte in "iWerteArr"
  werden ignoriert. Platzhalter, fuer die in "iWerteArr" kein Wert definiert
  ist, werden durch eine leere Zeichenfolge ersetzt.
    Wenn der Text einen Platzhalter mit 2 Dollarzeichen (bspw. $$3) enthaelt,
  dann wird der gesamte Text gegen den entsprechenden Wert ersetzt. Andere
  Platzhalter werden in diesem Fall ignoriert.
  */
  var lWert;
  var lIdx;
  var rText = iText;
  var lMuster = /\$(\d+)/; // regulaerer Ausdruck fuer "$n", n eine bel. Zahl
  var llMuster = /\$\$(\d+)/; // regulaerer Ausdruck fuer "$$n", ...

  // Sonderfall: $$-Muster: der ganze Text wrd ersetzt
  //
  if( llMuster.exec( rText)) {
    lIdx = RegExp.$1;
    if( typeof iWerteArr[ lIdx] != "undefined") {
      return iWerteArr[ lIdx];
    }
    return "";
  }

  // alle Platzhalter suchen und ersetzen
  //
  while( lMuster.exec( rText)) {
    lIdx = RegExp.$1;
    lWert = "";
    if( typeof iWerteArr[ lIdx] != "undefined") {
      lWert = iWerteArr[ lIdx];
    }
    rText = rText.replace( lMuster, lWert);
  }
  return  rText;
}


function veKnoten( iKnoten, iWerteArr, iAttrArr) {
  /* ersetzt die Platzhalter aus "iWerteArr" in Wert und Text eines Knotens
  und in allen seinen Attributen so weit sie im Feld "iAttrArr" angegeben
  sind.
  */
  var lText;
  var i;

  // Knoteninhalt aendern
  //
  lText = getContent( iKnoten);
  if( lText) {
    setContent( iKnoten
              , veText( lText, iWerteArr)
              );
  }

  // Knotenattribute aendern
  //
  if( iKnoten.nodeType == 1) {
    for( i=0; i<iAttrArr.length; i++) {
      try {
        lText = getAttribute( iKnoten, iAttrArr[i]);
        if( lText) {
          //if( iAttrArr[i] == "src") alert( "vorher "+ lText);
          lText = veText( lText, iWerteArr);
          //if( iAttrArr[i] == "src") alert( "nachher "+ lText);
          setAttribute( iKnoten, iAttrArr[i], lText);
        }
      } catch(e) {
      }
    }
  }
}


function veBaum( iUebergeordnet, iWerteArr, iAttrArr) {
  /* alle untergeordneten Knoten abarbeiten und dort in allen Texten
  und angegebenen Attributen alle Platzhalter ersetzen; fuer Knoten, die Tags
  sind, ruft sich die Funktion selbst wieder auf, mit dem jeweiligen Tag
  als uebergeordnetem Knoten (Rekursion).
  */
  var lUntergeordnet;               // Deklaration erforderlich, sonst funktioniert die Rekursion nicht

  // beginnen mit dem ersten Kind
  //
  lUntergeordnet = iUebergeordnet.firstChild;
  while( lUntergeordnet != null) {
    //msg( "1", lUntergeordnet);

    // Platzhalter ersetzen
    //
    veKnoten( lUntergeordnet, iWerteArr, iAttrArr);

    // Knoten ist Tag? ja: rekursiv eine Ebene tiefer
    //
    if( lUntergeordnet.tagName) {
      veBaum( lUntergeordnet, iWerteArr, iAttrArr);
    }

    // naechstes Kind
    //
    lUntergeordnet = lUntergeordnet.nextSibling;
  }
}


function veTab2Matrix( iTab) {
  /*
    ...konvertiert die als Zeichenfolge angegebene Tabelle "iTab" in eine Matrix
    aus Zeilen und Spalten, jeweils beginnend mit dem Index 0.
  */

  var lZeilenArr = new Array(); // die in Zeilen zerlegte Liste
  var lMatrix = new Array(); // die in Zeilen und Spalten zerlegte Liste
  var i, j, k;

  try {
    lZeilenArr = iTab.split( "$");

    j = 0;
    for( i=0; i<lZeilenArr.length; i++) {
      if( ! lZeilenArr[i] ) continue; // leer: direkt weiter
      if( lZeilenArr[i].match( /^@/)) continue; // Kommentar? ja: direkt weiter

      lMatrix[j] = new Array();
      lMatrix[j] = lZeilenArr[i].split( "°");

      for( k=0; k<lMatrix[j].length; k++) {
        lMatrix[j][k] = lMatrix[j][k].replace( / +$/, "");
        if( ! lMatrix[j][k] ) {
          lMatrix[j][k] = " ";
        }
      }

      j++;
    }

    return lMatrix;

  } catch( e) {
    alert( "Seitenfehler, Javascript 'veTab2Matrix': " + e);
  }
}


function veMatrixFilter( iTMatrix, iFilter, iTage, iStart, iHeuteCol) {
  /*
    ...geht von einer bestimmten Spaltenfolge aus. In der ersten Spalte wird
    ein filterbarer Ausdruck erwartet und in der zweiten ein nach YYYMMDD
    formatiertes Datum. Das Datum in der Tabelle wird mit dem Tagesdatum
    verglichen, alle Zeilen, die vor "heute" liegen, werden ignoriert. Auf den
    Filterausdruck wird der Parameter "iFilter" als regulaerer Ausdruck angewandt.
    Wenn der Ausdruck nicht passt, dann wird die Zeile ebenfalls abgewiesen.
      Auserdem kann mit "iTage" ein Intervall fuer den anzuzeigenden Zeitraum
    angegeben werden. Termine nach diesem Zeitraum werden ebenfalls ignoriert.
      Nachdem die Zeilen so gefiltert worden sind, wird die verbliebene Matrix
    an "veMatrix" uebergeben.
      Und noch ein Parameter: mit "iStart" kann ein anderes Datum als heute fuer
    das Filtern auf die zweite Spalte angegeben werden.
      Ausserdem wird in der Ergebnismatrix eine weitere Spalte "iHeuteCol"
    definiert, die fuer Eintraege zum Tagesdatum mit 'heute' belegt wird, sonst
    mit der leeren Zeichenfolge. Wenn "iHeuteCol" nicht angegeben wird, dann
    entfaellt die Angabe.
  */

  var lTMatrix = new Array();
  var i, j;

  // Startdatum
  var d = new Date();
  var seit8 = "29991231";
  if( typeof iStart == "undefined" ) {
    seit8 = "" + ( d.getFullYear() * 10000 + ( d.getMonth() + 1) * 100 + d.getDate() );
  } else {
    seit8 = iStart;
  }

  // heute
  d = new Date();
  heute8 = d.getFullYear() * 10000 + ( d.getMonth() + 1) * 100 + d.getDate();

  // Endedatum
  d.setDate( d.getDate() + iTage);
  var bald8 = d.getFullYear() * 10000 + ( d.getMonth() + 1) * 100 + d.getDate();

  // Matrix filtern
  j = 0;
  for( i=0; i<iTMatrix.length; i++) {
    if( iTMatrix[i][0].match( iFilter)
     && iTMatrix[i][1] >= seit8
     && ( typeof iTage == "undefined"
       || iTage == ""
       || iTage == 0
       || iTMatrix[i][1] <= bald8
        )
    ) {
      lTMatrix[j] = iTMatrix[i];

      if( typeof iHeuteCol != "undefined" ) {
        lTMatrix[j][iHeuteCol] = "";
        if( lTMatrix[j][1] == heute8 ) {
          lTMatrix[j][iHeuteCol] = "heute";
        }
      }

      j++;
    }
  }
  return lTMatrix;
}


  var cKTechL = 25;
  var cKTechU = 30;
  var cKFaktL = cKTechU;
  var cKFaktF = 5;
  var cKFaktU = 100;
  var cKQualL = cKFaktU;
  var cKQualF = 5;
  var cKQualU = 190;
  var cKLeadL = cKQualU;

function veTab2KMatrix( iKontakte, iFilter) {
  /*
    Eine Kontaktmatrix muss ein bestimmtes Format haben (siehe "kontakte.js").
    Ausgehend von diesem Format wird die Matrix aufbereitet mit den folgenden
    Spalten:
      0-24    Originaldaten
              17  Originalzeitstempel
              18..zeitweise verwendete Daten
      25-29   technische Daten
              25  Original-Aktualisierungszeitstempel
              26  ... konvertiert in lesbares Format
              27  Markierung (wird vom Aufrufer gesetzt oder geloescht und gelesen),
                  initial mit 0 belegt
              28  Benutzername in Originalschreibweise
              29  Zeilen-/Datensatznummer
      30-99   Fakten (Name, ...)
              +0  Original-Feldinhalt
              +1  Freigabekennzeichen (".", "x", "+", "-")
              +2  Suffix fuer Symbolnamen (p, x, j, n)
              +3  Beschreibung der Freigabe
              +4  Feldinhalt zur Veroeffentlichung
      100-189 Qualifikationen
              +0  Kennzeichen
              +1  Suffix fuer Symbolnamen
              +2  Beschreibung
              +3  (dz nicht verwendet)
              +4  (dz nicht verwendet)
      190-199 Fuehrungsqualifikation
              +0  Kennzeichen
              +1  Suffix fuer Symbolnamen
              +2  Beschreibung
              +3  (dz nicht verwendet)
              +4  (dz nicht verwendet)
    Ausserdem wird die Matrix mit "iFilter" gefiltert, sofern angegeben. Der
    Filter wird als regulaerer Ausdruck auf die erste Spalte angewandt.
  */
  var lKMatrix0 = new Array();
  var lKMatrix = new Array();
  var i, j, k, l;


  // Zeichenfolge konvertieren und bB filtern
  //
  lKMatrix0 = veTab2Matrix( iKontakte);
  if( iFilter && typeof iFilter != "undefined" ) {
    j = 0;
    for( i=0; i<lKMatrix0.length; i++) {
      if( lKMatrix0[i][0].match( iFilter) ) {
        lKMatrix[j++] = lKMatrix0[i];
      }
    }
  } else {
    lKMatrix = lKMatrix0;
  }

  // alle Kontaktdatensaetze...
  //
  cPp = '...darf veröffentlicht werden';
  cPx = '...nicht freigegeben';
  cPj = '...nicht freigegeben, auch nicht fuer intern';
  cPn = '...nicht bekannt';

  for( i=0; i<lKMatrix.length; i++) {

    // Bereich 25-29: technische Daten
    //
    lKMatrix[i][cKTechL +0] = lKMatrix[i][17];
    lKMatrix[i][cKTechL +1] = lKMatrix[i][17].substr( 6,2)
                       +"."+ lKMatrix[i][17].substr( 4,2)
                       +"."+ lKMatrix[i][17].substr( 0,4)
    ;
    lKMatrix[i][cKTechL +2] = 0;
    lKMatrix[i][cKTechL +3] = lKMatrix[i][1];
    lKMatrix[i][cKTechL +4] = i;

    // Bereich 30-99: Fakten jeweils mit Freigabe
    // zunaext initialisieren
    //
    for( j=cKFaktL; j<cKFaktU; j++) lKMatrix[i][j] = "";

    // dann Name und Vorname (dz immer veroeffentlicht)
    //
    lKMatrix[i][cKFaktL +0] = lKMatrix[i][03]; // Name
    lKMatrix[i][cKFaktL +1] = ".";
    lKMatrix[i][cKFaktL +2] = "p";
    lKMatrix[i][cKFaktL +3] = cPp;
    lKMatrix[i][cKFaktL +4] = lKMatrix[i][03];

    lKMatrix[i][cKFaktL +cKFaktF +0] = lKMatrix[i][04]; // Vorname
    lKMatrix[i][cKFaktL +cKFaktF +1] = ".";
    lKMatrix[i][cKFaktL +cKFaktF +2] = "p";
    lKMatrix[i][cKFaktL +cKFaktF +3] = cPp;
    lKMatrix[i][cKFaktL +cKFaktF +4] = lKMatrix[i][04];

    // Unterbereich 40-99: restliche Faktenfelder
    //
    for( j=0; j<6; j++) {
      // Quell- und Zielindexe
      k = 5 +2*j;
      l = (cKFaktL +10) + cKFaktF*j;

      // Fakten und Freigaben uebertragen
      lKMatrix[i][l]   = lKMatrix[i][k];
      lKMatrix[i][l+1] = lKMatrix[i][k+1];
      lKMatrix[i][l+4] = ""; // nicht veroeffentlichen
      if( lKMatrix[i][k+1] == '.' && lKMatrix[i][2] == '.' ) {
        lKMatrix[i][l+2] = 'p';
        lKMatrix[i][l+3] = cPp;
        lKMatrix[i][l+4] = lKMatrix[i][k]; // veroeffentlichen
      }
      if( lKMatrix[i][k+1] == 'x' ) { lKMatrix[i][l+2] = 'x'; lKMatrix[i][l+3] = cPx; }
      if( lKMatrix[i][k+1] == '+' ) { lKMatrix[i][l+2] = 'j'; lKMatrix[i][l+3] = cPj; }
      if( lKMatrix[i][k+1] == '-' ) { lKMatrix[i][l+2] = 'n'; lKMatrix[i][l+3] = cPn; }
    }

    // Bereich 100-199: zusaetzliche Spalten zur Anzeige der Qualifikationen
    // zunaext initialisieren
    //
    for( j=cKQualL; j<cKQualU; j++) lKMatrix[i][j] = "";

    // fuer jede Qualifikation einzeln die Anzeigewerte festlegen
    //
    lQ = "afjkAFGJM8543"; lc = "";
    for( j=0; j < lQ.length; j++) {
      lc = lQ.substr( j, 1);

      // Zielindex und Kennzeichen der Qualifikation
      k = cKQualL + cKQualF*j;
      if( lc.match( /[3-5]/) ) k = cKLeadL;
      lKMatrix[i][k] = lc;

      if( lKMatrix[i][0].match( lc ) ) {
          // Suffix fuer Symbolnamen
          if( lc.match( /[a-z]/) ) {
            lKMatrix[i][k+1] = "k" + lc;
          } else {
            if( lc.match( /[A-Z]/) ) {
              lKMatrix[i][k+1] = "g" + lc;
            } else {
              lKMatrix[i][k+1] = lc;
            }
          }

        // Beschreibung
        if( lc == "a" ) lKMatrix[i][k+2] = "Atemschutzgeräteräger";
        if( lc == "f" ) lKMatrix[i][k+2] = "MTF-Führerschein";
        if( lc == "j" ) lKMatrix[i][k+2] = "Jugendfeuerwehr";
        if( lc == "k" ) lKMatrix[i][k+2] = "aktive Kameraden";
        if( lc == "A" ) lKMatrix[i][k+2] = "Atemschutzgerätewart";
        if( lc == "F" ) lKMatrix[i][k+2] = "TSF-W-Führerschein";
        if( lc == "G" ) lKMatrix[i][k+2] = "Gerätewart";
        if( lc == "J" ) lKMatrix[i][k+2] = "Jugendwart";
        if( lc == "M" ) lKMatrix[i][k+2] = "Maschinist";
        if( lc == "8" ) lKMatrix[i][k+2] = "800er";
        if( lc == "5" ) lKMatrix[i][k+2] = "Gruppenführer";
        if( lc == "4" ) lKMatrix[i][k+2] = "stellv. Wehrführer";
        if( lc == "3" ) lKMatrix[i][k+2] = "Wehrführer";
      }
    }
  }

  //alert( lKMatrix);
  return lKMatrix;
}


function veMatrix2Verteiler( iMatrix, iSep, iFiltered, iOhne ) {
  /*
    ...stellt aus der Kontaktmatrix "iMatrix" nach Kontakten mit passendem Filter
    und setzt daraus einen Verteiler aus den beiden E-Mailadressen zusammen, so
    weit angegeben. Als Separator wird "iSep" verwendet.
      Wird "iFiltered" nicht oder mit false angegeben, dann werden alle Kontakte
    aus der Matrix verwendet. Wird "iFiltered" mit true belegt, dann werden nur
    die Kontakte verwendet, deren Markierungskennzeichen gesetzt ist (Spalte 22,
    nach Stand 20091017).
      Wird "iOhne" mit true belegt, dann liefert die Funktion die Namen der
    Kontakte, die keine E-Mailadresse haben.
  */
  var rVerteiler = "";
  var lAll = true;
  if( typeof iFiltered != "undefined") {
    if( iFiltered ) {
      lAll = false;
    }
  }
  var lOhne = false;
  if( typeof iOhne != "undefined") {
    if( iOhne ) {
      lOhne = true;
    }
  }

  for( i=0; i<iMatrix.length; i++) {
    if( lAll || iMatrix[i][22] ) {
      if( lOhne ) {
        if( ! iMatrix[i][55].replace(/ +$/, "")
         && ! iMatrix[i][60].replace(/ +$/, "")
          ) {
          if( rVerteiler ) rVerteiler = rVerteiler + iSep;
          rVerteiler = rVerteiler + iMatrix[i][35] + " " + iMatrix[i][30];
        }

      } else {
        if( iMatrix[i][55].replace(/ +$/, "") ) {
          if( rVerteiler ) rVerteiler = rVerteiler + iSep;
          rVerteiler = rVerteiler + iMatrix[i][55];
        }
        if( iMatrix[i][60].replace(/ +$/, "") ) {
          if( rVerteiler ) rVerteiler = rVerteiler + iSep;
          rVerteiler = rVerteiler + iMatrix[i][60];
        }
      }
    }
  }
  return rVerteiler;
}



function veMatrix( iVorlageId, iMatrix, iAttribute) {
  /*
    ...vervielfaeltigt das mit "iVorlageld" angegebene Element mit saemtlichen
    untergeordneten Elementen; und zwar sooft wie die angegebene Matrix "iMatrix"
    Zeilen enthaelt. Anschliessend wird das Originalelement entfernt. In jedem
    der vervielfaeltigen Elemente werden Platzhalter $0, .. $n ausgetauscht gegen
    das jeweils n-te Feld in der jeweiligen Zeile.
      Wenn ein Attributwert oder ein Text einen Platzhalter mit 2 Dollarzeichen
    enthaelt, dann wird der gesamte Attributwert bzw. Text gegen den entsprechenden
    Wert ersetzt. Andere Platzhalter werden in diesem Fall ignoriert. (Diese
    Sonderlocke war erforderlich, weil der Microsoft-IE 6.x Euer das href-Attribut
    nicht ganz verfaelschte Attributwerte liefert.)
  */

  var lAttrArr = new Array(); // die in Attributnamen zerlegte Attributliste
  var lVorlage;               // Objekt, das vervielfaeltigt wird
  var lUebergeordnet;         // uebergeordnetes Objekt fuer Vorlage und Kopien
  var lKopie;                 // Kopie der Vorlage var i;

  try {
    // Attributliste zerlegen bzw. Default setzen
    //
    if( typeof iAttribute == "undefined") {
      iAttribute = ""
    }
    lAttrArr = iAttribute.split( ",");

    // Vorlage und uebergeordnetes Element bestimmen
    //
    lVorlage = getElement( "id", iVorlageId);
    if( ! lVorlage) throw "Keine Vorlage gefunden";

    lUebergeordnet = lVorlage.parentNode;
    if( ! lUebergeordnet) throw "Kein uebergeordnetes Element zur Vorlage gefunden";


    // je (nicht leerer) Zeile eine Kopie anhaengen und die Platzhalter ersetzen
    //
    for( i=0; i<iMatrix.length; i++) {

      // Kopie der Vorlage herstellen, Platzhalter im obersten Knoten der Kopie
      // ersetzen und ebenso in allen untergeordneten Knoten
      //
      lKopie = lVorlage.cloneNode( true);

      veKnoten( lKopie, iMatrix[i], lAttrArr);
      veBaum( lKopie, iMatrix[i], lAttrArr);

      // Kopie einhaengen
      //
      lUebergeordnet.appendChild( lKopie);
    }


    // jetzt noch das Vorlageobjekt selbst entfernen, fertig
    //
    lUebergeordnet.removeChild( lVorlage);
  } catch( e) {
    alert( "Seitenfehler, Liste füllen:" + e);
  }
}


function veStart( iVorlageId, iTab, iAttribute) {
  /*
    ...arbeitet wie "veMatrix", nur dass an Stelle einer Matrix direkt eine
    Zeichenfolgetabelle "iTab" angegeben werden kann.
  */
  var lMatrix = new Array(); // die in Zeilen und Spalten zerlegte Liste

  // Liste aufteilen in Zeilen
  lMatrix = veTab2Matrix( iTab);
  veMatrix( iVorlageId, lMatrix, iAttribute);
}


function veTermine( iVorlageld, iTTab, iFilter, iTage, iAttribute, iStart, iHeuteCol) {
  /*
      wie "veStart"; erwartet aber eine Terminmatrix und filtert diese zuvor mit
      "veMAtrixFilter".
  */
  var lMatrix0 = veTab2Matrix( iTTab);
  var lMatrixl = veMatrixFilter( lMatrix0, iFilter, iTage, iStart, iHeuteCol );
  veMatrix( iVorlageld, lMatrixl, iAttribute);
}

function veTerminHinweis() {
  hinweis( 'veTerminHinweis'
         , 'Die Terminseiten verwenden das aktuelle Datum Ihres Rechners, um die Termine, die angezeigt werden sollen, zu filtern.'
         , '../../00.requirements.html#zeitlos'
         , 'Probleme?'
         )
  ;
}


/********************
 * Arbeiten mit Divs: ein/aus
 *
 */
function divShow( iId) {
  /*
      Flaeche mit der angegebenen Id einblenden
  */
  if (NS4) {
    getElement("id", iId).visibility = "show";
  } else {
    getElement("id", iId).style.visibility = "visible";
  }
}


function divHide( iId) {
  /*
      Flaeche mit der angegebenen Id ausblenden
  */
  if (NS4) {
    getElement("id", iId).visibility = "hide";
  } else {
    getElement("id", iId).style.visibility = "hidden";
  }
}


function divToggle( iId) {
  /*
      Flaeche mit der angegebenen Id ein-/ausblenden; liefert den Zustand danach
      als Ergebnis: 1 = eingeblendet, 0 = ausgeblendet
  */
  var v;
  if (NS4) {
    v = getElement("id", iId).visibility;
  } else {
    v = getElement("id", iId).style.visibility;
  }
  if( v == "hidden" || ! v ) {
    divShow( iId);
    return 1;
  } else {
    divHide( iId);
    return 0;
  }
}


/********************
 * Arbeiten mit Divs: Groesse anpassen
 *
 */

function repos( iId) {
  /*
    Groesse eines Bereichs in einer Seitenhälfte anpassen; die Funktion geht
    davon aus, dass der Bereich in der Zelle einer einzeiligen Tabelle
    untergebracht ist, die den gesamten Buldschirm abdeckt. Nur in dieser
    Konstellation ist diese Funktion bisher verwendet/getestet worden.
      Der Bereich wird mit seiner Id angegeben. Er ist initial mit "(style)
    position:static;" definiert, das ist der Default.
      Die repos-Funktion muss beim Laden des Dokuments registiert werden werden,
    dazu ist eine Fensterspezifische repos-Funktion zu definieren, die die
    vorliegend implementierte repos-Funktion mit der passenden Id aufruft und
    diese Funktion wird mit "window.onresize = reposXYZ" registriert.
      Ausserdem sollte der Bereich mit "reposInit" initial eingerichtet und
    positioniert werden, siehe unten.
  */

  // Oberkante des uebergeordneten Objekts bestimmen
  //
  var liste = getElement( "id", iId);
  var linxTop = liste.parentNode.offsetTop;
  var listeH;

  // verfuegbare Hoehe bestimmen: das ist die Fensterinnengroesse
  // (Browser-abhaengig) abzueglich der Oberkante des uebergeordneten
  // Objekts und abzueglich einem weiteren Browser-abhaengigen Abstand
    /* Browser:
      ...Firefox kennt die "window.innerheight"
      ...IE verwendet "document.body.offsetHeight"
      ...andere lassen die Anpassung bleiben
    */
  if( window.innerHeight ) { // Browser die "window.innerhei
    listeH = window.innerHeight - 25 - linxTop;
  } else if( document.body && document.body.offsetHeight ) {
    listeH = document.body.offsetHeight - 35 - linxTop;
  } else {
    return 0;
  }

  // neue Hoehe des Bereichs einstellen
  //
  liste.style.height = listeH;
}


function reposInit( iId) {
  /*
      den Bereich mit der Id "iId" initial repositionieren; zuvor wird der
      Bereich auf Autoscroll geschaltet: Initial ist der Bereich ohne scroll-
      Modus definiert, so dass der Textinhalt und das Layout des Browsers seine
      Breite festlegen; diese Breite wird dann explizit definiert zuzüglich dem
      geschaetzten Platz fuer eine Bildlaufleiste und dann wird "overflow" auf
      "auto" gestellt, um bei Bedarf die Bildlaufleiste einblenden zu lassen,
      aber ohne dadurch die Breite zu aendern.
        Diese Funktion wird am besten nach dem Laden, also direkt vor dem
      abschliessenden body-Tag aufgerufen.
  */

  // Groesse des Bereichs an Fenster anpassen
  //
  repos( iId);

  // Bereich auf "auto scroll" stellen
  //
  var liste = getElement( "id", iId);
  liste.style.width = liste.offsetWidth + 16;
  liste.style.overflow = "auto";
}




/********************
 * Meldungsfenster im Menuebereich
 *
 */

function hinweis( iHId, iText, iVerweis, iVerweisText) {
  parent['menu'].hinweis( iHId, iText, iVerweis, iVerweisText);
}


/********************
 * Quelltext ausgeben
 *
 */

function qtKnoten( iKnoten) {
  var rText = "";
  var i;

  if( iKnoten.nodeType == 1 && typeof iKnoten.attributes != "undefined") {
    for( i=0; i<iKnoten.attributes.length; i++) {
      if( rText) rText = rText + " ";
      rText = rText
           + iKnoten.attributes[i].nodeName
           + "=\""
           + iKnoten.attributes[i].nodeValue
           + "\"";
    }
  }

  maKnoten( rText, iKnoten);
}

function qtBaum( iKnoten) {
  var lUntergeordnet;               // Deklaration erforderlich, sonst funktioniert die Rekursion nicht

  qtKnoten( iKnoten)

  lUntergeordnet = iKnoten.firstChild;
  while( lUntergeordnet != null) {

    // Knoten ist Tag? ja: rekursiv eine Ebene tiefer
    //
    if( lUntergeordnet.tagName) {
      qtBaum( lUntergeordnet);
    }

    // naechstes Kind
    //
    lUntergeordnet = lUntergeordnet.nextSibling;
  }
}

function qtStart() {
  var q = "";
  q = "a";
  n = getElement( "tagname", "html", 0);
  qtBaum( n);
  //prompt( "Quelltext", n.childNodes[1]);
}
