/*
  Dieses Script wurde geschrieben von Mitja Stachowiak (www.mitjastachowiak.de)
  Es benoetigt ausserdem frameCommunicator.js.

  Dokumentation unter http://www.mitjastachowiak.de/code/js/dynamicIFrame
*/





function getTickCount () { // returns the milliseconds since beginning of the current year
  var T = new Date();
  return T.getMilliseconds() + T.getSeconds() * 1000 + T.getMinutes() * 60000 + T.getHours() * 3600000 + T.getDay() * 86400000;
}
function createOverscrollListener (doc, wait, callback) {
  var o = new Object();
  o.doc = doc;
  o.lastScroll = 0;
  o.lastTry = 0;
  o.lastInnerWidth = window.innerWidth;
  o.wait = wait / 2;
  o.callback = callback;
  o.scrollFkt = function () {
    o.lastScroll = getTickCount();
  }
  o.scrollEventFkt = function (ev) {
    if (getTickCount() < o.lastScroll + o.wait) { o.lastTry = 0; return; }
    if (o.lastTry == 0) o.lastTry = getTickCount();
    if (getTickCount() < o.lastTry + o.wait) { o.lastInnerWidth = window.innerWidth; return; }
    if (o.lastInnerWidth != window.innerWidth) { o.lastInnerWidth = window.innerWidth; return; }
    o.lastTry = 0;
    if (!ev) ev = window.event;
    var dir = ev.wheelDelta || -ev.detail;
    if (!callback) return;
    if (dir) { o.callback(dir < 0); return; }
    if (window.pageYOffset > 0) { o.callback(true); return; }
    if (ev.keyCode) { o.callback(ev.keyCode == 40); return; }
  }
  o.free = function () {
    o.doc.removeEventListener('scroll', o.scrollFkt, false);
    o.doc.removeEventListener('DOMMouseScroll', o.scrollEventFkt, false);
    o.doc.removeEventListener('mousewheel', o.scrollEventFkt, false);
    o.doc.removeEventListener('keypress', o.scrollEventFkt, false);
    o.callback = null;
    o.doc = null;
    o = null;
  }
  // EventListener setzen
  var d = o.doc;
  var _d = null;
  while (d && d != _d) {
    try {
      _d = d;
      d.addEventListener('scroll', o.scrollFkt, false);
      d = d.defaultView.parent.document;
    } catch (e) { break; }
  }
  o.doc.addEventListener('DOMMouseScroll', o.scrollEventFkt, false);
  o.doc.addEventListener('mousewheel', o.scrollEventFkt, false);
  o.doc.addEventListener('keypress', o.scrollEventFkt, false);
  return o;
}


/*
 Dies sind zwei standard-Effekte. Diese koennen Sie als Vorlage fuer eigene Effekte verwenden
 
 Parameter:
  el : iframe-Element
  s : Wert zwischen 0 und 1; Ausnahme -1 (0 = Effekt beginnt, 0.5 = Effekt ist bei der Haelfte, 1 = Effekt ist fertig, -1 = saemtliche von diesem Effekt benutzten css-Eigenschaften zuruecksetzen)
  blendIn : Wenn true, soll das iframe eingeblendet werden, ansonsten ausgeblendet
*/

function EffectBlend (el, s, blendIn) {
  if (s < 0) { el.style.opacity = 1; return; }
  if (!blendIn) s = 1-s;
  el.style.opacity = s;
}

function EffectBlendAndSlide (el, s, blendIn) {
  if (s < 0) { el.style.marginTop = '0px'; el.style.opacity = 1; return; }
  if (!blendIn) s = 1-s;
  el.style.opacity = s;
  el.style.marginTop = -100 * (1-s) * (1-s) + 'px';
}

function EffectAppendAndSlide (el1, el2, s, dir) {
  if (s < 0) {
    el1.style.marginTop = '0px';
    el2.style.marginTop = '0px';
    el1.parentElement.style.overflow = 'visible';
    return;
  }
  el1.parentElement.style.overflow = 'hidden';
  if (dir) {
    el1.style.marginTop = -el1.offsetHeight * (1-s);
    el2.style.marginTop = el1.offsetHeight * (s);
  } else {
    el2.style.marginTop = -el2.offsetHeight * (s);
    el1.style.marginTop = el2.offsetHeight * (1-s);
  }
}




/*
 Dieses Objekt darf nur einmal in einem HTML-Dokument erstellt werden und erlaubt, das Laden von Seiten in einem iframe mit Effekt zu versehen.
 
 Parameter:
  FrameBox : ein DIV-Element, das genau 2 iframes enthaellt. Jedes IFrame benoetigt ein eindeutriges name-Attribut. Nur so kann die Kompatibilitaet mit Links ohne JavaScript gewaehrleistet werden (<A href="frameSeite.html" target="frameName">).
 
 Eigenschaften und Funktionen:
  whitelist : Array aus RegExp-Objekten. Alle Seiten, die im Frame geladen werden sollen, muessen entweder von der gleichen Domain sein, oder auf die Whitelist passen
  addURLToWhitelist : Wandelt die uebergebene URL in ein RegExp-Objekt um und fuegt sie der whitelist hinzu. In der URL sind *-Zeichen erlaubt. Innerhalb des Hostname-Bereiches haben diese jedoch spezielle Aufgaben und es kann nicht jede beliebige Zeichenfolge fuer ein * stehen.
  differentQueryDifferentSites : Boolean - wenn true, dann werden Menuelinks nur hervorgehoen, wenn auch das QUerystring des Menuelinks und der Seite im Frame gleich ist.
  whitelist : Array aus Strigs (Darf Platzhalter (*) enthalten). Nur Seiten, die in dieses Profil passen, duerfen im Frame geladen werden. Seiten von der eigenen Domain duerfen immer geladen werden.
  maxWaitForSite : diese Zeit (ms) wird maximal gewartet, bis die neue Seite eingeblendet wird. Ist die Seite bis dahin nicht vollstaendig geladen, kann der Besucher beobachten, wie sie im iframe fertig laedt.
  blendOutTime : Dauer des Ausblendens (ms)
  blendInDelay : Verzoegerung - so viele Millisekunden nach Beginn des Ausblendens beginnt das Einblenden
  blendInTime : Dauer des Einblendens (ms)
  blendInEffect : Uebergeben Sie hier die Effekt-Funktion fuer das Einblenden
  blendOutEffect : Uebergeben Sie hier die Effekt-Funktion fuer das Ausblenden
  function loadSite : Rufen Sie diese Funktion mit einer URL auf, um diese URL mit Effekt in den iframe zu laden (Die Seite wird nur geladen, wenn sie von der gleichen Domain ist oder auf die Whitelist passt)
  function scrollTo : uebergeben werden kann eine y-Koordinate aus Sicht des aktuellen Iframes oder der Name eines Ankers innerhalb des aktuellen Iframes. ScrollTo scrollt dann zu diesem Ziel. Ein zweiter Parameter darf nur vom Objekt selbst uebergeben werden.
  autoFillMenu : Falls die Menu-Links ein Inhaltsverzeichnis darstellen, kann fuer autoFillMenu eine Zahl eingesetzt werden, bis zu der H*-Elemente der Frame-Seite gescant und automatisch im Inhaltsverzeichnis ergaenzt werden. Ein Wert von 3 z.B. bedeutet, dass fuer alle H1, H2 und H3-Elemente automatisch Links im Inhaltsverzeichnis beim Laden der Seite angelegt werden.
*/
function DynamicIFrame (FrameBox, debugLogging=false) { with (this) {
  // private
  var box = FrameBox;
  var frames = FrameBox.getElementsByTagName('iframe');
  var current = 0;
  var next = 1;
  var communicators = new Array();
  var aTarget, aTarget2;
  var isAutoFrameHeight = true;
  var menuLinks = new Array();
  var printLinks = new Array();
  var blendInfo = new Object();
  var scrollInfo = new Object();
  var autoHeightEl = new Array(); // Alle Elemente, deren NoJsHeight-Classname gesetzt/entfernt wird
  var loadWithoutEffect = false;
  var startPage = '';
  var incompatibleBrowser = false;
  var domWasReady = false;
  var selfHostname;
  var currentSite = '';
  var loadingSiteAnchor = ''; // Anker der zu ladenden Seite aus URL, wird gespeichert fuer scroll-Effekt
  var currentSiteAnchors = new Array(); // Array aus allen Ueberschrifen-Ankern der aktuellen Seite (kann nach absOffsetTop der Anker sortiert werden)
  var anchorPos = 0; // index in currentSiteAnchors des gerade aktiven Ankers
  var overscrollListener = null;
  var currentHirachy = null;
  var oldQuery = '';
  var scriptLoadingPage = '';

  // public
  this.differentQueryDifferentSites = false;
  this.whitelist = new Array();
  this.maxWaitForSite = 1000; // Nach dieser Zeit wird die Seite anzegeigt, auch wenn noch nicht vollst. geladen
  this.blendOutTime = 0;
  this.blendInDelay = 0;
  this.blendInTime = 0;
  this.scrollTime = 500;
  this.blendInEffect = null;
  this.blendOutEffect = null;
  this.browseEffect = EffectAppendAndSlide;
  this.browseDuration = 2000;
  this.scrollFunction = function (s) { return (-2*s+3)*s*s; }
  this.browseScrollFunction = function (s) { return s; }
  this.autoFillMenu = 0; // Bis zu dieser Zahl * werden H*-Elemente im Menue ergaenzt
  this.autoChangeAnchor = false;
  this.nummerateMenu = false;
  this.onSiteLoaded = null;
  this.onLinkActivate = null;
  this.onMenuChange = null;
  this.onTitleChange = null;
  this.onContentResize = null;
  
  var _this = this;
  var onEvent = /* private */ function (el, event, fkt, param, behavior) { // provides easy eventListeners. el: Element to set the listener; event: string-name of the event(without "on"); fkt: a Function to call ift the event occours, fkt is called in the scope of _this; param: any parameter, that is given to fkt; behavior: flags - 1=only the param is given to fkt (otherwise there is also given the element and the event), 2=event is not given to the element
   var f;
   if (behavior & 1) f = function (ev) {
     if (!ev) ev = window.event;
  	fkt.call(_this,param);
	 if (behavior & 2) { if (ev.preventDefault) ev.preventDefault(); return false; }
    }
   else f = function (ev) {
     if (!ev) ev = window.event;
  	fkt.call(_this,this,ev,param);
     if (behavior & 2) { if (ev.preventDefault) ev.preventDefault(); return false; }
    }
   if (window.addEventListener) el.addEventListener(event, f, false);
   else el.attachEvent('on'+event,f);
   return false;
  };
  
  var getAbsOffsetTop = function (el) { // returns the offset top of the given element seen from the document top (el CAN be in the current IFRAME)
   var pos = el.offsetTop;
   while (el.offsetParent) {
    if (el.offsetParent == el) break;
    el = el.offsetParent;
    pos += el.offsetTop;
   }
   try {
    if (el == frames[current].contentWindow.document.body || el == frames[current].contentWindow.document.documentElement) pos += getAbsOffsetTop(frames[current]);
   } catch (e) { }
   return pos;
  };
  
  var getAnchorFromURL = /* private */ function (url) {
   var i = url.lastIndexOf('#');
   if (i < 0) return '';
   var a = url.substring(i+1, url.length);
   if ((/[^(A-Za-z0-9_)]/).test(a)) return '';
   return a;
  };
  
  var getSiteFromURL = /* private */ function (url) {
   var i = url.lastIndexOf('#');
   var j = url.indexOf('?');
   if (!differentQueryDifferentSites && j >= 0 && j < j) i = j;
   if (i < 0) i = url.length;
   return url.substring(0,i);
  };

  var create = /* private */ function () {
   if ((!window.addEventListener && !window.attachEvent) || !document.body || !document.getElementsByTagName) { incompatibleBrowser = true; return; }
   if (!window.fapi) { incompatibleBrowser = true; throw 'DynamicIFrame benoetigt frameCommunicator.js!'; }
   if (window.jsw && jsw.units.switchableDesign) jsw.units.switchableDesign.unit.requireDesignFile('dynamicIFrame', box);
   selfHostname = getHostnameAndProtocol(self.location.href);
   // Re-Create Frame elements, because browser does stupid things on hard coded iframes when re-loading a site...
   startPage = frames[0].src;
   aTarget = frames[0].getAttribute('name', 0);
   aTarget2 = frames[1].getAttribute('name', 0);
   frames[1].parentElement.removeChild(frames[1]);
   frames[0].parentElement.removeChild(frames[0]);
   if (frames.length > 0) throw 'Not more than two frames allowed in HTML!';
   var f = document.createElement('iframe');
   f.setAttribute('name', aTarget);
   f.id = aTarget;
   FrameBox.appendChild(f);
   f = document.createElement('iframe');
   f.setAttribute('name', aTarget2);
   f.id = aTarget2;
   FrameBox.appendChild(f);
   // prepare communicators
   communicators[0] = new FrameCommunicator(frames[0], (debugLogging)?'0':'');
   communicators[1] = new FrameCommunicator(frames[1], (debugLogging)?'1':'');
   communicators[0].autoHeight = true;
   communicators[1].autoHeight = true;
   communicators[0].onContentResize = function (w, h) { siteResize(0, h); };
   communicators[1].onContentResize = function (w, h) { siteResize(1, h); };
   communicators[0].onURLChanged = setQueryString;
   communicators[1].onURLChanged = setQueryString;
   communicators[0].onload = manipulateCurrentSite;
   communicators[1].onload = manipulateCurrentSite;
   communicators[0].onTitleChanged = titleChange;
   communicators[1].onTitleChanged = titleChange;
   if (document.addEventListener) document.addEventListener("DOMContentLoaded", domReady, false);
   else {
    var oldReadyStateChange = document.onreadystatechange;
    document.onreadystatechange = function () {
      try { oldReadyStateChange(); } catch (e) { }
      if(document.readyState == "interactive" || document.readyState == "complete") domReady();
     }
   }
   // QueryString untersuchen
   if (window.addEventListener) window.addEventListener('popstate', evalQueryString, false);
  }

  var domReady = /* private */ function () {
   if (domWasReady) return;
   domWasReady = true;
   // Links auf JavaScript umstellen
   for (var i = 0; i < document.getElementsByTagName('a').length; i++)
    linkJSenhance(document.getElementsByTagName('a')[i]);
   if (nummerateMenu) buildMenuNumbers();
   activateCurrentLink();
   // AutoHeight-Elemente finden
   for (var i = 0; i < document.getElementsByTagName('div').length; i++) if (document.getElementsByTagName('div')[i].className.indexOf('NoJsHeight') >= 0) autoHeightEl.push(document.getElementsByTagName('div')[i])
   if (!isAutoFrameHeight) {
    isAutoFrameHeight = true;
    autoFrameHeight(false);
   }
   // Scroll-Event setzen
   window.addEventListener('scroll', sitescroll, false);
   // Seite aus Querystring laden
   window.setTimeout(function () { // for whatever reason, events do not pass before dom content is loaded.
     if (scriptLoadingPage != '' || !evalQueryString()) {
       if (scriptLoadingPage != '') {
         debugLog('Load site set by script: '+scriptLoadingPage);
         loadSite(scriptLoadingPage, 1);
       } else {
         debugLog('Load start site: '+startPage);
         var hashtag = window.location.href.split('#');
         if (hashtag.length > 1) hashtag = '#'+hashtag.pop();
         else hashtag = '';
         loadSite(startPage+hashtag, 1);
       }
     }
   }, 0);
  };

  var debugLog = /* private */ function (msg) {
   if (!debugLogging) return;
   var d = new Date();
   console.log('['+d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+' '+d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()+']  '+msg);
  }
  
  var evalQueryString = /* private */ function (effect) {
    // Wird in domready und beim klicken auf den Vorwaerts/Rueckwaerts-Button im Browser aufgerufen
    var newSite = self.location.href.split('?');
    newSite.shift();
    if (newSite.length == 0) return false;
    newSite[0] = newSite[0].split('&');
    var isFB = false;
    for (var i = 0; i < newSite[0].length; i++)
      if (newSite[0][i].indexOf('fbclid=') == 0 || newSite[0][i].indexOf('\/gclid=') == 0) {
        newSite[0].splice(i, 1);
        isFB = true;
        break;
      }
    if (newSite[0].length == 0) return false;
    newSite[0] = newSite[0].join('&');
    if (isFB) newSite[0] = newSite[0].split('%2F').join('\/');
    var newSite = absolutePath(newSite.join('?'));
    if (isNotInWhitelist(newSite)) return false;
    debugLog('Load site by query: '+newSite);
    loadSite(newSite, (effect)?0:1);
    return true;
  };

  this.addURLToWhitelist = /* public */ function (url) {
    var rep = new RegExp('([^\\w&^\\*])', 'g');
    var host = getHostnameAndProtocol(url);
    var i = host.length;
    host = host.replace(rep, '\\$1');
    var path = url.substring(i, url.length).replace(rep, '\\$1');
    if (path == '' && host.length > 0 && host.charAt(host.length-1) == '*') path = '*';
    while (host.length > 0 && host.charAt(host.length-1) == '*') host = host.substring(0, host.length-1);
    if (host == '') throw 'URL has wrong syntax';
    rep = new RegExp('\\*', 'g');
    path = path.replace(rep, '[a|^a]*'); // innerhalb des path-Bereiches koennen die * fuer beliebige Inhalte stehen
    // innerhalb des hostname-Bereiches haben die * spezielle Aufgaben
    var a = host.split('\\\:\\\/\\\/');
    if (a.length == 1) {
      a = a[0].split('*');
      if (a.length == 2)
        host = a[0] + '(\\w)*\\:\\/\\/((\\w)+\\.)*' + a[1];
      else throw 'URL has wrong syntax';
    } else if (a.length == 2) {
      rep = new RegExp('(\\*)', 'g');
      a[0] = a[0].replace(rep, '(\\w)*');
      a[1] = a[1].replace(rep, '((\\w)+\\.)*');
      host = a[0] + '\\:\\/\\/' + a[1];
    } else throw 'URL has wrong syntax';
    whitelist.push(new RegExp('\^'+host+path));
  };
  
  var isNotInWhitelist = /* private */ function (src) {
    if (getHostnameAndProtocol(src) == selfHostname) return false;
    for (var i = 0; i < whitelist.length; i++) if (whitelist[i].test(src)) return false;
    return true;
  };
  
  var buildMenuNumbers = /* private */ function () {
    if (!nummerateMenu) return;
    for (var i = 0; i < menuLinks.length; i++) {
      if (menuLinks[i]['el'].parentElement.tagName.toLowerCase() != 'li') continue;
      var ol = menuLinks[i]['el'].parentElement;
      var a = new Array();
      while (ol.parentElement && ol.parentElement.tagName.toLowerCase() == 'ol') {
        var nr = 1;
        var el = ol.parentElement.firstElementChild;
        while (el && el != ol) {
          el = el.nextElementSibling;
          if (el.tagName.toLowerCase() == 'li') nr++;
        }
        a.unshift(nr);
        ol = ol.parentElement;
      }
      if (!menuLinks[i]['numberEl']) {
        menuLinks[i]['numberEl'] = document.createElement('span');
        menuLinks[i]['el'].insertBefore(menuLinks[i]['numberEl'], menuLinks[i]['el'].firstChild);
      }
      menuLinks[i]['numberEl'].innerHTML = a.join('.')+'. ';
    }
  };
  
  var activateLink = /* private */ function (link) { // das in link uebergebene MenuLink-Objekt wird aktiviert, ggf. akuell aktive Links werden deaktiviert
    if (window.opera && link) { link['el'].style.visibility = 'hidden'; link['el'].style.visibility = 'visible'; } // Komisches Bug
    if (link && link['active']) return;
    for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i]['active']) {
      menuLinks[i]['active'] = false;
      var c = menuLinks[i]['el'].className.split(' ');
      for (var j = c.length-1; j >= 0; j--) if (c[j] == 'active') c.splice(j, 1);
      menuLinks[i]['el'].className = c.join(' ');
    }
    if (!link) { if (onLinkActivate) onLinkActivate(null); return; }
    link['active'] = true;
    link['el'].className += ' active';
    if (onLinkActivate) onLinkActivate(link['el']);
    activateCurrentHirachy();
  };

  var activateCurrentHirachy = /* private */ function () {
    var link = null;
    for (var i = 0; i < menuLinks.length; i++)
      if (menuLinks[i]['active']) {
        link = menuLinks[i];
        break;
      }
    if (!link || !currentHirachy) return;
    var found = false;
    for (var i = currentHirachy.length-1; i >= 0; i--)
      if (found) {
        if (currentHirachy[i].link && currentHirachy[i].link['el'].getAttribute('alwaysactive') == 'yes' && !currentHirachy[i].link['active']) {
          currentHirachy[i].link['active'] = true;
          currentHirachy[i].link['el'].className += ' active';
        }
      } else if (currentHirachy[i].link == link) found = true;
  };
  
  var activateCurrentLink = /* private */ function () {
    // Seite und Anker ermitteln
    var site = currentSite;
    var anchor = getAnchorFromURL(communicators[current].contentURL);
    if (site.indexOf('?') != -1) site = site.substring(0, site.indexOf('?'));
    // Links mit gleicher Seite suchen
    var a = new Array();
    for (var i = 0; i < menuLinks.length; i++)
      if (site == menuLinks[i]['site'])
        a.push(menuLinks[i]);
    // Keinen passenden Link gefunden
    if (a.length == 0) {
      activateLink(null);
      return;
    }
    // Falls aktueller Anker direkt existiert
    for (var i = 0; i < a.length; i++)
      if (a[i]['anchor'] == anchor) {
        activateLink(a[i]);
        return;
      }
    // Falls keine Ueberschriften-Anker bekannt
    if (currentSiteAnchors.length == 0) {
      activateLink(a[0]);
      return;
    }
    // Falls verknuepfter menuLink schon bekannt
    if (currentSiteAnchors.length != 0 && currentSiteAnchors[anchorPos]['link']) {
      activateLink(currentSiteAnchors[anchorPos]['link']);
      return;
    }
    // Anker-Rangfolge erstellen
    var b = new Array();
    var j = anchorPos-1;
    var h = currentSiteAnchors[anchorPos]['hNr'];
    b[currentSiteAnchors[anchorPos]['hNr']] = currentSiteAnchors[anchorPos];
    for (var i = anchorPos-1; i >= 0; i--)
      if (currentSiteAnchors[i]['hNr'] < h) {
        h = currentSiteAnchors[i]['hNr'];
        b[h] = currentSiteAnchors[i];
      }
    // naheliegensten Menulink aktivieren
    for (var i = b.length-1; i >= 0; i--) {
      if (!b[i]) continue;
      for (var j = 0; j < a.length; j++) if (a[j]['anchor'] == b[i]['name']) { activateLink(a[j]); return; }
    }
    activateLink(a[0]);
  };
  
  var setQueryString = /* private */ function () { // Diese Funktion haengt an die URL der aktuellen Seite das ?UrlVonFrame an.
    var P = communicators[current].contentURL;
    if (P == '') P = communicators[current].contentHostname;
    if (P == '' || P.indexOf('about:blank') == 0) return;
    currentSite = getSiteFromURL(P);
    if (isNotInWhitelist(P)) {
      frames[current].style.visibility = 'hidden';
      frames[current].setAttribute('notInWhitelist', 'yes');
    } else {
      frames[current].style.visibility = 'visible';
      frames[current].removeAttribute('notInWhitelist');
    }
    activateCurrentLink();
    var L = self.location.href;
    i = L.indexOf('?');
    if (i < 0) i = L.length;
    var Q = L.substring(i+1, L.length);
    L = L.substring(0, i);
    i = L.indexOf('#');
    if (i > 0) L = L.substring(0, i);
    if (P.split('#')[0].toLowerCase() == startPage.toLowerCase()) {
      if (P.split('#').length == 2) P = '#'+P.split('#').pop();
      else P = '';
    } else {
      var H = getHostnameAndProtocol(communicators[current].contentURL);
      if (H == '') return;
      if (selfHostname == H) P = P.substring(H.length, P.length);
      if (P == Q) return;
      P = '?' + P;
    }
    if (history.pushState && window.location.href != L+P) {
      var stateObj = { foo: "bar" };
      var title = '';
      try { title = frames[current].contentWindow.document.title; } catch (e) {}
      if (oldQuery.split('#')[0] == P.split('#')[0]) history.replaceState(stateObj, document.title+': '+title, L+P);
      else history.pushState(stateObj, document.title+': '+title, L+P);
      oldQuery = P;
    }
  };

  this.linkJSenhance = function (el, inFrameSite=false) { // replace simple links by javascript-enhanced links
    if (inFrameSite) {
      if ((el.target == '_self' || el.target == '') && el.href != '') {
        var a = el.href.split('#');
        if (a[0].indexOf('javascript:') != 0 && a[0].indexOf('callto:') != 0 && a[0].indexOf('mailto:') != 0 && a[0].indexOf('webcal:') != 0 && el.download == '') { // check, this is a common link
          if (isNotInWhitelist(el.href)) el.target = '_blank';
          else if (a[0] == frames[current].contentWindow.location.href.split('#')[0] && a.length > 1) onEvent(el, 'click', scrollTo, a[1], 3);
          else if ((' '+el.className+' ').indexOf(' WindowLink ') == -1) onEvent(el, 'click', loadSite, el.href, 3);
        }
      }
    } else {
      if (el.target == aTarget) {
        if ((' '+el.className+' ').indexOf(' MenuLink ') >= 0) { // Menue-Links, die hervorgehoben werden sollen, vorbereiten
          var href = absolutePath(el.href);
          menuLinks.push({ // menuLinks koennen auch in manipulateCurrentSite erzeugt werden!
            el : el,
            href : href,
            anchor : getAnchorFromURL(href),
            site : getSiteFromURL(href),
            subMenuId : el.getAttribute('submenu'),
            active : false
          });
        }
        onEvent(el, 'click', loadSite, el.href, 3);
      }
      if (el.href.toLowerCase() == 'javascript:'+aTarget.toLowerCase()+'.print()') printLinks.push(el);
    }
  };

  var scanElements = function (el, comm) {
    for (var i = 0; i < el.childNodes.length; i++)
      if (el.childNodes[i].nodeType == 1) {
        var eli = el.childNodes[i];
        var tagName = eli.tagName.toLowerCase();
        if (tagName == 'a')
          linkJSenhance(eli, true);
        else if ((/h\d/i).test(tagName)) { // add headlines to currentSiteAnchors
          // find or create headline's id
          var id = eli.id;
          if (!id && eli.getElementsByTagName('a').length > 0) id = eli.getElementsByTagName('a')[0].id;
          if (!id) { // auto-generate an id
            var txt = eli.textContent.toLowerCase();
            id = '';
            for (var j = 0; j < txt.length; j++)
              if (txt.charAt(j) == ' ' && id != '') id += '_';
              else if (txt.charCodeAt(j) >= 97 && txt.charCodeAt(j) <= 122) id += txt.charAt(j);
              else if (txt.charCodeAt(j) >= 48 && txt.charCodeAt(j) <= 57 && id != '') id += txt.charAt(j);
              else if (txt.charCodeAt(j) == 228) id += 'ae';
              else if (txt.charCodeAt(j) == 246) id += 'oe';
              else if (txt.charCodeAt(j) == 252) id += 'ue';
              else if (txt.charCodeAt(j) == 223) id += 'ss';
            var j = 2;
            while (document.getElementById(id)) {
              id += '_'+j;
              j++;
            }
            eli.id = id;
          }
          // add headline to current site anchors
          currentSiteAnchors.push({
            el: eli,
            hNr: parseInt(tagName.substring(1)),
            id: id,
            content: eli.textContent,
            site: comm.contentURL.split('#')[0],
            pos: getAbsOffsetTop(eli),
            link: null
          });
        }
        scanElements(eli, comm);
      }
  }

  var manipulateCurrentSite = /* private */ function (comm) { // wird von iframe.communicator.onload aufgerufen, sobald der Inhalt des frames geladen hat und dessen Hoehe bekannt ist.
    var _this = this;
    var frameNr = communicators.indexOf(comm);
    if (frameNr != current)
      return;
    debugLog('manipulate frame '+frameNr+'; url='+comm.contentURL);
    currentSiteAnchors = new Array();
    anchorPos = 0;
    try { // Innerhalb von try-catch: Aenderungen an der Frame-Seite
      frames[current].contentWindow.document.body.style.overflow = (isAutoFrameHeight)?'auto':'hidden';
      if (window.jsw) window.jsw.processSite(frames[current].contentWindow.document.body, jswMainWindow);
      scanElements(frames[current].contentWindow.document.body, comm);
    } catch (e) {}
    // find this site's root menu link (first menu link, that points to this site)
    var hirachy = new Array();
    hirachy.push(new Object());
    hirachy[0]['link'] = null;
    hirachy[0]['subMenu'] = null;
    for (var i = 0; i < menuLinks.length; i++)
      if (menuLinks[i]['site'] == comm.contentURL.split('#')[0]) {
        hirachy[0]['link'] = menuLinks[i];
        hirachy[0]['subMenu'] = null;
        if (menuLinks[i]['el'].getAttribute('submenu'))
          hirachy[0]['subMenu'] = document.getElementById(menuLinks[i]['el'].getAttribute('submenu'));
        if (!hirachy[0]['subMenu'] && menuLinks[i]['el'].parentElement.tagName.toLowerCase() == 'li') {
          var nextEl = menuLinks[i]['el'].parentElement.nextElementSibling;
          while (nextEl)
            if (nextEl.tagName.toLowerCase() == 'li' || nextEl.tagName.toLowerCase() == 'ol') break;
            else nextEl = nextEl.nextElementSibling;
          if (nextEl && nextEl.tagName.toLowerCase() == 'ol') hirachy[0]['subMenu'] = nextEl;
          else if (autoFillMenu > 0) {
            hirachy[0]['subMenu'] = document.createElement('ol');
            if (nextEl) menuLinks[i]['el'].parentElement.parentElement.insertBefore(hirachy[0]['subMenu'], nextEl);
            else menuLinks[i]['el'].parentElement.parentElement.appendChild(hirachy[0]['subMenu']);
          }
        }
        break;
      }
    // here, hirachy[0] contains the root link to this site and the destination ol-list for headline anchors. Now expand hirachy towards higher levels
    linkBaseSearch:
    while (hirachy[0]['link'] && hirachy[0]['link']['el'].parentElement.tagName.toLowerCase() == 'li') {
      var ol = hirachy[0]['link']['el'].parentElement.parentElement;
      while (ol.tagName.toLowerCase() == 'ol') {
        hirachy.unshift({
          link: null,
          subMenu: ol
        });
        if (ol.id != '')
          for (var i = 0; i < menuLinks.length; i++)
            if (menuLinks[i].subMenuId == ol.id) {
              hirachy[0].link = menuLinks[i];
              continue linkBaseSearch;
            }
        ol = ol.parentElement;
      }
      break;
    }
    // actualize menu with currentSiteAnchors
    var h = hirachy.length-1;
    if (h >= 0 && hirachy[h]['subMenu'] && currentSiteAnchors.length > 0) {
      for (var i = 0; i < currentSiteAnchors.length; i++) { // search or create related links for all anchors (don't do this in try..catch)
        // find related menu link to this anchor
        var isSingleTopHeadline = (currentSiteAnchors[i]['el'].tagName.toLowerCase() == 'h1') && (currentSiteAnchors[i]['el'].ownerDocument.getElementsByTagName('h1').length == 1);
        for (var j = 0; j < menuLinks.length; j++)
          if (menuLinks[j]['site'] == comm.contentURL.split('#')[0] && (menuLinks[j]['anchor'] == currentSiteAnchors[i]['id'] || isSingleTopHeadline && menuLinks[j]['anchor'] == '')) {
            var pel = menuLinks[j]['el'].parentElement;
            if (pel.tagName.toLowerCase() != 'li')
              if (isSingleTopHeadline) {
                currentSiteAnchors[i]['link'] = menuLinks[j];
                currentSiteAnchors[i]['isSingleTopHeadline'] = true;
                break;
              } else continue; // this link is not a list-element. Ignore!
            while (pel)
              if (pel == hirachy[0]['subMenu']) break
              else pel = pel.parentElement;
            if (!pel) continue; // this link is outside of the hirachy-ol and has to remain untouched!
            currentSiteAnchors[i]['link'] = menuLinks[j];
            break;
          }
        var link = currentSiteAnchors[i]['link'];
        if (link) {
          // remove li-element from DOM (maybe add it to an other location, later)
          if (link == hirachy[h]['link']) continue; // don't touch root link
          link['el'].parentElement.parentElement.removeChild(currentSiteAnchors[i]['link']['el'].parentElement);
        } else if (currentSiteAnchors[i]['hNr'] <= autoFillMenu && (!currentSiteAnchors[i]['el'] || currentSiteAnchors[i]['el'].getAttribute('autocompletemenu') !== 'no')) {
          // create a new menu link, if missing
          link = {
         	  href: comm.contentURL.split('#')[0] + '#' + currentSiteAnchors[i]['id'],
            anchor: currentSiteAnchors[i]['id'],
            site: comm.contentURL.split('#')[0],
	           active: false,
            el: document.createElement('a')
          };
          link['el'].textContent = currentSiteAnchors[i]['content'];
          link['el'].className = 'MenuLink';
          link['el'].target = aTarget;
          link['el'].href = link['href'];
          onEvent(link['el'], 'click', loadSite, link['href'], 3);
          currentSiteAnchors[i]['link'] = link;
          document.createElement('li').appendChild(link['el']);
          menuLinks.push(link);
        }
        if (!link) continue;
        // insert menu link at correct position, create missing ol-hirachies
        if (i == 0 || currentSiteAnchors[i-1]['isSingleTopHeadline'])
          hirachy[h]['subMenu'].insertBefore(link['el'].parentElement, hirachy[h]['subMenu'].firstChild);
        else {
          var hnr = currentSiteAnchors[i-1]['hNr'];
          var nextSiblingForLi = currentSiteAnchors[i-1]['el'].parentElement.nextSibling;
          // enter necessary sub-menus
          while (hnr < currentSiteAnchors[i]['hNr']) {
            var newOl = null;
            if (hnr == currentSiteAnchors[i-1]['hNr']) {
              if (currentSiteAnchors[i-1]['link']['el'].parentElement.nextElementSibling && currentSiteAnchors[i-1]['link']['el'].parentElement.nextElementSibling.tagName.toLowerCase() == 'ol') newOl = currentSiteAnchors[i-1]['link']['el'].parentElement.nextElementSibling;
              else {
                newOl = document.createElement('ol');
                hirachy[h]['subMenu'].insertBefore(newOl, currentSiteAnchors[i-1]['el'].parentElement.nextSibling);
             }
            } else {
              if (hirachy[h]['subMenu'].firstElementChild && hirachy[h]['subMenu'].firstElementChild.tagName.toLowerCase() == 'ol')
                newOl = hirachy[h]['subMenu'].firstElementChild;
              else {
                newOl = document.createElement('ol');
                hirachy[h]['subMenu'].insertBefore(newOl, hirachy[h]['subMenu'].firstChild);
              }
            }
            hnr++;
            h++;
            hirachy.push({
              link: null,
              subMenu: newOl
            });
            nextSiblingForLi = newOl.firstChild;
          }
          // exit unneccessary sub-menus
          while (hnr > currentSiteAnchors[i]['hNr'] && h > 0) {
            nextSiblingForLi = hirachy[h]['subMenu'].nextSibling;
            h--;
            hnr--;
            hirachy.pop();
          }
          hirachy[h]['subMenu'].insertBefore(link['el'].parentElement, nextSiblingForLi);
        }
      }
    }
    currentHirachy = hirachy;
    activateCurrentHirachy();
    // Nummerierung
    if (nummerateMenu) {
      buildMenuNumbers();
      for (var i = 0; i < currentSiteAnchors.length; i++)
        if (currentSiteAnchors[i]['link'] && currentSiteAnchors[i]['link']['numberEl']) {
          var nrEl = currentSiteAnchors[i]['el'].getElementsByTagName('span')[0];
          if (!nrEl || (' '+nrEl.className+' ').indexOf(' number ') < 0) {
            nrEl = frames[current].contentWindow.document.createElement('span');
            currentSiteAnchors[i]['el'].insertBefore(nrEl, currentSiteAnchors[i]['el'].firstChild);
          }
          nrEl.innerHTML = currentSiteAnchors[i]['link']['numberEl'].innerHTML;
        }
    }
    if (onMenuChange) onMenuChange();
    // sonstiges
    setQueryString();
    blendInfo['currentSiteReady'] = true;
    if (onSiteLoaded) {
      debugLog('Call onSiteLoaded'+((loadWithoutEffect)?' without effect':''));
      onSiteLoaded(frames[current].contentWindow, loadWithoutEffect);
    }
    loadingSiteAnchor = getAnchorFromURL(communicators[current].contentURL);
    if (loadWithoutEffect && loadingSiteAnchor != '') {
      debugLog('Try to scroll to #'+loadingSiteAnchor);
      scrollTo(loadingSiteAnchor, 2);
    } else if (loadingSiteAnchor == '') scrollTo(0, 2);
  };

  var sitescroll = /* private */ function () { // funktion fuer Scrollevent
    if (!autoChangeAnchor || currentSiteAnchors.length == 0 || scrollInfo['scrolling']) return;
    // richtigen Anker ermitteln
    var pos = window.pageYOffset;
    var nr = anchorPos;
    while (nr > 0 && currentSiteAnchors[nr]['pos'] > pos + 100) nr--;
    while (nr < currentSiteAnchors.length-1 && currentSiteAnchors[nr+1]['pos'] <= pos + 100) nr++;
    // Ankerposition testen
    if (!currentSiteAnchors[nr]) return; // ToDo: unknown error!
    var top = getAbsOffsetTop(currentSiteAnchors[nr]['el']);
    if (Math.abs(top - currentSiteAnchors[nr]['pos']) > 5) {
      currentSiteAnchors[nr]['pos'] = top;
      sitescroll();
      return;
    }
    if (pos < getAbsOffsetTop(frames[current])) nr = -1;
    // Anker wechseln
    if (nr != anchorPos) {
      anchorPos = nr;
      var i = frames[current].contentWindow.location.href.lastIndexOf('#');
      if (i < 0) i = frames[current].contentWindow.location.href.length;
      var stateObj = { foo: "bar" };
      var anchorHashtag = '';
      if (nr >= 0) anchorHashtag = '#'+currentSiteAnchors[nr]['id'];
      frames[current].contentWindow.history.replaceState(stateObj, '', frames[current].contentWindow.location.href.substring(0,i)+anchorHashtag);
      communicators[current].newURL(frames[current].contentWindow.location.href.substring(0,i)+anchorHashtag); // Bugfix: Beim reload der Seite werden im Frame die Funktionen replaceState und pushState nicht ueberwacht
    }
  };

  var autoFrameHeight = /* private */ function (auto) {
    // wenn true uebergeben wird, wird die Hoehe des iframe-Bereiches auf ~100% der Seitenhoehe (das ClassName 'NoJsHeight' wird wieder an alle Elemente, die dies zu Anfang trugen, angehaengt) angepasst, ansonsten richtet sich die Hoehe nach dem Inhalt des aktuellen frames.
    if (auto == isAutoFrameHeight) {
      debugLog('Auto frame height already '+((auto)?'activated':'deactivated')+'.');
      return;
    }
    debugLog(((auto)?'Activate':'Deactivate')+' auto frame height...');
    isAutoFrameHeight = auto;
    for (var i = 0; i < autoHeightEl.length; i++) {
      var a = autoHeightEl[i].className.split(' ');
      var n = '';
      for (var j = 0; j < a.length; j++) {
        if (a[j] == 'NoJsHeight') continue;
       	if (n != '') n += ' ';
	       n += a[j];
      }
      autoHeightEl[i].className = n;
    }
    if (auto) {
      for (var i = 0; i < autoHeightEl.length; i++) {
        if (autoHeightEl[i].className != '') autoHeightEl[i].className = autoHeightEl[i].className + ' ';
        autoHeightEl[i].className = autoHeightEl[i].className + 'NoJsHeight';
      }
      frames[current].style.height = '100%';
      try { frames[current].contentWindow.document.body.style.overflow = 'auto'; } catch (e) { }
    } else {
      try { frames[current].contentWindow.document.body.style.overflow = 'hidden'; } catch (e) { }
    }
  };

  var siteResize = /* private */ function (nr, h) {
    if (nr != current) return;
    debugLog('Resize info from frame '+nr+' received. Height = '+h);
    if (loadingSiteAnchor != '') scrollTo(loadingSiteAnchor, 2);
    if (onContentResize) onContentResize(communicators[current].contentWidth, communicators[current].contentHeight, communicators[current]);
    if (blendInfo['blending'] && !(blendInfo['state'] & 2)) return; // kein Resize fuer noch nicht sichtbare Seiten
    if (blendInfo['blending'] && isAutoFrameHeight && h >= 0) frames[next].style.height = frames[next].offsetHeight + 'px'; // automatisch gegebene Hoehe einfrieren, bevor autoFrameHeight false wird
    autoFrameHeight(h < 0);
  };

  var titleChange = /* private */ function (title, communicator) {
    if (communicator != communicators[current]) return;
    if (onTitleChange) onTitleChange(title);
  };
  
  var blend = /* private */ function () {
    // Loop fuer Einblendeeffekt
    if (!blendInfo['timer']) blendInfo['timer'] = window.setInterval(blend, 40);
    var t = getTickCount();
    // blendState - Flags:   1 = alter Frame wurde ausgeblendet    2 = neue Seite hat geladen und Verzoegerung ist um     4 = neue Seite wurde eingeblendet
    if ((blendInfo['state'] & 1) != 1 && !blendInfo['browse']) { // Wenn Flag 1 nicht gesetzt (alte Seite ausblenden)
      if (t - blendInfo['start'] < blendOutTime) {
        if (blendOutEffect) blendOutEffect(frames[next], (t - blendInfo['start'])/blendOutTime, false);
      } else {
        blendInfo['state'] += 1;
        frames[next].style.display = 'none';
        communicators[next].setSrc('about:blank');
        if (blendOutEffect) blendOutEffect(frames[next], -1, false);
      }
    }
    if (((blendInfo['state'] & 2) != 2) && (blendInfo['currentSiteReady'] || t - blendInfo['start'] > maxWaitForSite) && (t - blendInfo['start'] > blendInDelay)) { // Wenn Flag 2 nicht gesetzt und Bedingungen fuer Einblenden von neuer Seite erfuellt (neue Seite anzeigen)
      if (blendInEffect && !blendInfo['browse']) blendInEffect(frames[current], 0, true);
      else if (browseEffect && blendInfo['browse']) browseEffect(frames[current], frames[next], 0, blendInfo['dir']);
      frames[current].style.maxHeight = 'none';
      blendInfo['startDelay'] = t;
      blendInfo['state'] += 2;
      siteResize(current, communicators[current].contentHeight);
      if (blendInfo['browse']) {
        if (blendInfo['dir']) loadingSiteAnchor = communicators[current].contentHeight + frames[current].parentElement.offsetTop - window.innerHeight;
        else loadingSiteAnchor = 0;
      }
      if (blendInfo['browse']) scrollTo(loadingSiteAnchor, 5);
      else if (loadingSiteAnchor != '') scrollTo(loadingSiteAnchor, 1);
    }
    if (((blendInfo['state'] & 4) != 4) && (blendInfo['state'] & 2)) { // wenn Flag 2 gesetzt aber Flag 4 noch nicht (neue Seite einblenden)
      if (blendInfo['browse']) {
        if (t - blendInfo['startDelay'] < browseDuration) {
          if (browseEffect) browseEffect(frames[current], frames[next], (t-blendInfo['startDelay'])/browseDuration, blendInfo['dir']);
        } else {
          blendInfo['state'] |= 5;
          frames[next].style.display = 'none';
          communicators[next].setSrc('about:blank');
          if (browseEffect) browseEffect(frames[current], frames[next], -1, blendInfo['dir']);
        }
      } else {
        if (t - blendInfo['startDelay'] < blendInTime) {
          if (blendInEffect) blendInEffect(frames[current], (t - blendInfo['startDelay'])/blendInTime, true);
        } else {
          blendInfo['state'] |= 4;
          if (blendInEffect) blendInEffect(frames[current], -1, true);
        }
      }
    }
    if ((blendInfo['state'] & 5) == 5) { // wenn Flag 1 und 4 gesetzt (Blendvorgang beenden)
      if (blendInfo['timer']) window.clearInterval(blendInfo['timer']);
      blendInfo['timer'] = null;
      blendInfo['blending'] = false;
    }
  };
  
  this.loadSite = /* public */ function (src, behavior) { // behavior : 0=normal, 1=kein Effekt, 2=blaettern, 4=nach oben blaettern
    if (!behavior) behavior = 0;
    if (incompatibleBrowser || src == 'about:blank') {
      debugLog('Incompatible site load blocked: '+src);
      return;
    }
    src = absolutePath(src);
    if (isNotInWhitelist(src)) {
      debugLog('Not whitelisted site blocked: '+src);
      return;
    }
    if (!domWasReady && behavior == 1) {
      this.scriptLoadingPage = src;
      return;
    }
    // Wenn neue Seite gleich aktueller Seite, nur scrollTo ausfuehren
    if (getSiteFromURL(src) == currentSite) {
      if (~behavior & 2) scrollTo(getAnchorFromURL(src));
      debugLog('Loading of site blocked, because it already is loaded! ('+src+')');
      return;
    }
    // Einstellungen
    loadWithoutEffect = (behavior & 1) == 1;
    if (loadWithoutEffect) debugLog('Load without effect');
    else debugLog('Load with effect');
    currentSiteAnchors = new Array();
    anchorPos = 0;
    // Wenn Effekt aktiv, nur neue Seite in aktuellen Frame laden
    if (blendInfo['blending']) {
      debugLog('Replacing url of currently loading site in frame '+current+' with '+src);
      communicators[current].setSrc(src);
      return;
    }
    // current und next wechseln
    current = next;
    next = (current == 0)?1:0;
    // neue Seite laden
    if (!loadWithoutEffect) {
      blendInfo['currentSiteReady'] = false;
      blendInfo['blending'] = true;
    }
    debugLog('Frame '+current+' starts loading of '+src+' | behavior = '+behavior);
    communicators[current].setSrc(src);
    // PrintLinks umstellen
    if (current == 0)
      for (var i = 0; i < printLinks.length; i++)
        printLinks[i].href = 'javascript:'+aTarget+'.print()';
    else
      for (var i = 0; i < printLinks.length; i++)
        printLinks[i].href = 'javascript:'+aTarget2+'.print()';
    // Blenduebergang starten
    if (loadWithoutEffect) {
      frames[next].style.display = 'none';
      frames[current].style.display = 'block';
    } else {
      frames[current].style.maxHeight = '0px';
      frames[current].style.display = 'block';  // display = block ist notwendig, damit Seite im Frame beginnt zu laden!
      blendInfo['state'] = 0;
      blendInfo['browse'] = (behavior & 2) == 2;
      blendInfo['dir'] = (behavior & 4) == 4;
      blendInfo['start'] = getTickCount();
      blend();
    }
  };
  
  var scroll = /* private */ function () {
    // Loop fuer Scroll-Effekt
    if (!scrollInfo['timer']) scrollInfo['timer'] = window.setInterval(scroll, 40);
    var t = getTickCount();
    var d = scrollInfo['blendEffectTime'];
    if (scrollInfo['start'] + d > t) {
      var s = (t - scrollInfo['start']) / d;
      s = scrollInfo['fkt'](s);
      s = scrollInfo['startPos'] * (1-s) + scrollInfo['endPos'] * s;
      window.scrollTo(window.pageXOffset, s);
    } else {
      scrollInfo['scrolling'] = false;
      if (scrollInfo['timer']) window.clearInterval(scrollInfo['timer'], 40);
      scrollInfo['timer'] = null;
      window.scrollTo(window.pageXOffset, scrollInfo['endPos']);
    }
  };
  
  this.scrollTo = /* public */ function (target, scrollbehavior) { // scrollbehavior: 0=normal, 1=scrollt so lange, wie Einblendeeffekt, 2=springt sofort zu neuer Position (kein Effekt), 4=scrollFkt = browseScrollFkt
    if (!scrollbehavior) scrollbehavior = 0;
    if (incompatibleBrowser) return;
    if (scrollbehavior & 4) scrollInfo['fkt'] = browseScrollFunction;
    else scrollInfo['fkt'] = scrollFunction;
    // Scrollziel suchen
    var pos = 0;
    if (!isNaN(target)) pos = target;
    else {
      try {
        var el = frames[current].contentWindow.document.getElementById(target);
	       if (!el) el = frames[current].contentWindow.document.getElementsByName(target)[0];
	       if (el) pos = getAbsOffsetTop(el);
      } catch (e) { return; }
      try {
        var L = frames[current].contentWindow.location.href;
        var i = L.lastIndexOf('#');
        if (i > 0 && L.lastIndexOf('?') < i) L = L.substring(0, i);
        var stateObj = { foo: "bar" };
        if (frames[current].contentWindow.location.href != L+'#'+target)
          frames[current].contentWindow.history.replaceState(stateObj, document.title+': '+frames[current].contentWindow.document.title, L+'#'+target);
      } catch (e) {  }
    }
    // Scrollziel setzen
    if (loadingSiteAnchor != '' && getAbsOffsetTop(frames[current]) + frames[current].offsetHeight >= pos) {
      debugLog('Anchor of loading site is reachable.');
      loadingSiteAnchor = '';
    }
    scrollInfo['endPos'] = pos;
    if (scrollInfo['scrolling']) return;
    // Scrolleffekt starten
    scrollInfo['scrolling'] = true;
    scrollInfo['start'] = getTickCount();
    if (scrollbehavior & 2) scrollInfo['start'] = 0;
    scrollInfo['startPos'] = window.pageYOffset;
    scrollInfo['blendEffectTime'] = scrollTime;
    if (scrollbehavior & 1) scrollInfo['blendEffectTime'] = blendInTime;
    if (scrollbehavior & 5 == 5) scrollInfo['blendEffectTime'] = browseDuration;
    scroll();
    return;
  };
  
  this.enableOverscroll = function () {
    if (overscrollListener) return;
    overscrollListener = createOverscrollListener(document, 1000, function (dir) {
      for (var i = 0; i < menuLinks.length; i++) if (menuLinks[i]['active']) {
        if (dir && i < menuLinks.length - 1) { loadSite(menuLinks[i+1].href, 2); activateLink(menuLinks[i+1]); }
        else if (!dir && i > 0) { loadSite(menuLinks[i-1].href, 6); activateLink(menuLinks[i-1]); }
        return;
      }
    });
  };

  this.getActiveContentWindow = function () {
    return frames[current].contentWindow;
  };

  this.getActiveCommunicator = function () {
    return communicators[current];
  };
  
  create(); // Objekt sollte vollstaendig geladen sein, bevor Create aufgerufen wird
} }
