/*
 Dieses Script wurde entwickelt von Mitja Stachowiak.
 Fuer Details lesen Sie unter "http://www.mitjastachowiak.de/components/ecmascript/framecommunicator/index.html" nach.
*/

var selfScriptSource_huwheigif = document.getElementsByTagName('script')[document.getElementsByTagName('script').length-1].src;
var fapiStandard = 41;

// window.fapi und window.fapiFull werden am Ende des Scripts gesetzt


function getHostnameAndProtocol (loc) {
  if (!loc) loc = '';
  let i = loc.indexOf('?');
  if (i < 0) i = loc.length;
  let j = loc.indexOf('#');
  if (j > 0 && j < i) i = j;
  j = -1;
  do {
    j = loc.indexOf('\/', j+1);
  } while (j >= 0 && !((j == 0 || loc.charAt(j-1) != '\/') && (j == loc.length-1 || loc.charAt(j+1) != '\/')));
  if (j >= 0 && j < i) i = j;
  let h = loc.substring(0,i);
  return h;
}

function absolutePath (rel, absFrom) { // Diese Funktion wird nicht in diesem Script benoetigt, jedoch von vielen Scripts, die dieses verwenden.
  if (!absFrom) absFrom = self.location.href;
  let i = rel.indexOf('?');
  if (i < 0) i = rel.length;
  let j = rel.indexOf('#');
  if (j >= 0 && j < i) i = j;
  let Q = '';
  if (i < rel.length) Q = rel.substring(i,rel.length);
  rel = rel.substring(0,i); // ab hier: Querystring/Anker wurde von rel abgetrennt und in Q gespeichert
  if (rel.indexOf(':') >= 0 && rel.charAt(0) != '\/') return rel+Q; // rel ist schon absolut;
  i = absFrom.indexOf('?');
  if (i < 0) i = absFrom.length;
  j = absFrom.indexOf('#');
  if (j >= 0 && j < i) i = j;
  j = absFrom.lastIndexOf('/',i);
  if (j >= 0 && j < i-1) i = j+1;
  j = absFrom.lastIndexOf(':',i);
  if (j > i) i = j+1;
  absFrom = absFrom.substring(0,i); // ab hier: abs ist ohne Anker, Querystring und Dateiname
  let host = getHostnameAndProtocol(absFrom);
  if (rel.indexOf('/') == 0) return host + rel + Q; // rel ist relativ zu Root
  while (rel.indexOf('./') == 0) rel = rel.substring(2,rel.length);
  while (rel.indexOf('../') == 0) {
    rel = rel.substring(3,rel.length);
    j = absFrom.lastIndexOf('/',absFrom.length-2);
    if (j >= host.length) absFrom = absFrom.substring(0,j+1);
  }
  if (absFrom.lastIndexOf('/') != absFrom.length-1) absFrom += '/'; // ab hier: fuer jedes ../ wurde ein Ordner von absFrom abgehaengt
  return absFrom + rel + Q; // rel ist relativ zur Seite
}

function relativePath (abs, absFrom) {
  if (!absFrom) absFrom = self.location.href;
  let h = getHostnameAndProtocol(abs);
  if (h == '' && abs.indexOf('\/') == 0) { // falls abs relativ zu Root ist, absolut machen
    abs = absolutePath(abs, absFrom);
    h = getHostnameAndProtocol(abs);
  }
  if (h == '') return abs; // abs ist schon relativ
  if (h != getHostnameAndProtocol(absFrom)) return abs; // abs kann nicht relativ werden
  let i = absFrom.length;
  let j = absFrom.indexOf('#');
  if (j != -1 && j < i) i = j;
  j = absFrom.indexOf('?');
  if (j != -1 && j < i) i = j;
  j = (absFrom.indexOf('\/', h.length) == h.length)?h.length+1:h.length;
  i = absFrom.lastIndexOf('\/', i);
  let fromPath = new Array();
  if (i > j) fromPath = absFrom.substring(j, i).split('\/'); // ab hier: fromPath besteht nur noch aus den Ordnern von absFrom
  i = abs.length;
  j = abs.indexOf('#');
  if (j != -1 && j < i) i = j;
  j = abs.indexOf('?');
  if (j != -1 && j < i) i = j;
  j = (abs.indexOf('\/', h.length) == h.length)?h.length+1:h.length;
  i = abs.lastIndexOf('\/', i);
  let targetPath = new Array();
  if (i > j) targetPath = abs.substring(j, i).split('\/'); // ab hier: targetPath besteht nur noch aus den Ordnern von abs
  if (abs.indexOf('\/', i) == i) i++;
  let datQ = abs.substring(i, abs.length); // alles nach den Ordnern
  i = 0;
  while (targetPath.length > i && fromPath.length > i && targetPath[i] == fromPath[i]) i++;
  let rel = '';
  for (j = i; j < fromPath.length; j++) rel += '../';
  for (j = i; j < targetPath.length; j++) rel += targetPath[j] + '/';
  rel += datQ;
  return rel;
}

function createScrollRectEventListener (el, callback, win) {
  //console.log('ScrollRectListener startet for '+win.location.href+'!');
  var o = new Object();
  o.el = el;
  o.el2 = (el.tagName.toLowerCase() == 'html')?((el.getElementsByTagName('body').length == 0)?null:el.getElementsByTagName('body')[0]):el; // wenn el == documentElement, versuche, body zum einhaengen von heihtGetter zu finden
  o.callback = callback;
  o.win = win;
  o.lastH = -1;
  o.lastW = -1;
  o.pause = 0;
  o.pauseTimer = null;
  o.search = true;
  o.padding = 0;
  o.freed = false;
  o.resize = function () {
    if (o.freed) return;
    if (o.pause != 0) { o.pause = 2; return; }
    o.pause = 1;
    o.pauseTimer = win.setTimeout(function () {
      o.pauseTimer = null;
      if (o.pause == 2) {
        o.pause = 0;
        o.resize();
      } else o.pause = 0;
    }, 50);
    try { o.win.document.body.width = '500px'; } catch (e) {}
    let w = o.el.scrollWidth;
    let h = o.el.scrollHeight;
    let wh = 0;
    if (o.win) try { wh = o.win.innerHeight; } catch (e) {}
    else wh = o.el.offsetHeight;
    if (h > wh + 4) o.search = true; // nach Overflow max-Suche wiederholen
    else if (o.el2) { // wenn scrollHeigt nicht groesser, als offsetHeight
      if (!o.heightGetter) { // beim Erstaufruf heightGetter einfuegen
        o.heightGetter = o.win.document.createElement('hr');
        o.heightGetter.style.height = '1px'; // komisches null-height-Bug erkennen
        o.heightGetter.style.margin = '0px';
        o.heightGetter.style.visibility = 'hidden';
        o.heightGetter.style.clear = 'both';
        o.el2.appendChild(o.heightGetter);
        o.max = o.heightGetter;
      }
      if (o.el2.lastChild != o.heightGetter) {
        o.el2.removeChild(o.heightGetter);
        o.el2.appendChild(o.heightGetter);
      }
      try {
        h = o.max.offsetTop + o.max.offsetHeight;
        if (isNaN(h)) {
          h = -1;
          o.search = true;
        }
      } catch (e) {
        h = -1;
        o.search = true;
      }
      if (h == 0) return; // null-height-Bug: Eine nicht mehr geladene Seite hat das Event ausgeloest
      if (o.search) { // Tiefstes Element suchen und in o.max speichern
        for (let i = 0; i < o.el2.childNodes.length; i++) { 
          let _el = o.el2.childNodes[i];
          if (_el.nodeType != 1) continue;
          let ch = _el.offsetTop + _el.offsetHeight;
          try { ch += parseInt(o.win.getComputedStyle(_el).marginBottom); } catch (e) {}
          if (!isNaN(ch) && ch > h) {
            h = ch;
            o.max = _el;
          }
        }
        try {
          o.padding = parseInt(o.win.getComputedStyle(o.max).marginBottom) + parseInt(o.win.getComputedStyle(o.el2).paddingBottom);
        } catch (e) {
          o.padding = 0;
        }
        if (isNaN(o.padding)) o.padding = 0;
        o.search = false;
      }
      h += o.padding;
    } else if (o.el.getElementsByTagName('body').length != 0) { // versuche body zu finden
      o.el2 = o.el.getElementsByTagName('body')[0];
      o.resize();
      return;
    }
    if (Math.abs(h + 2 - o.lastH) <= 2 && Math.abs(w + 2 - o.lastW) <= 2) return; // keine Aenderung seit letztem Event
    o.lastH = h;
    o.lastW = w;
    //console.log('New height for '+win.location.href+' = '+h);
    o.callback(o.el, w, h);
  }
  // destructor
  o.free = function () {
    if (o.freed) return;
    try { o.observer.disconnect(); } catch (e) {
      o.el.removeEventListener('DOMNodeInserted', o.resize);
      o.el.removeEventListener('DOMNodeRemoved', o.resize);
      o.el.removeEventListener('DOMCharacterDataModified', o.resize);
      o.el.removeEventListener('DOMAttrModified', o.resize);
      o.el.removeEventListener('DOMContentLoaded', o.resize);
    }
    o.win.removeEventListener('resize', o.resize);
    o.el.removeEventListener('overflow', o.resize);
    o.el.removeEventListener('overflowchanged', o.resize);
    if (o.pauseTimer) o.win.clearTimeout(o.pauseTimer);
    o.win.clearInterval(o.timer);
    if (o.heightGetter) o.el2.removeChild(o.heightGetter);
    o.freed = true;
    for (let key in o) if (typeof o[key] == 'object') o[key] = null;
  }
  // constructor
  try {
    o.observer = new MutationObserver(o.resize);
    let config = { attributes: true, childList: true, characterData: true, subtree: true };
    o.observer.observe(el, config);
  } catch (e) {
    el.addEventListener('DOMNodeInserted', o.resize, false);
    el.addEventListener('DOMNodeRemoved', o.resize, false);
    el.addEventListener('DOMCharacterDataModified', o.resize, false);
    el.addEventListener('DOMAttrModified', o.resize, false);
    el.addEventListener('DOMContentLoaded', o.resize, false);
  }
  o.win.addEventListener('resize', o.resize, false);
  o.timer = o.win.setInterval(function () { o.search = true; o.resize() }, 10000);
  el.addEventListener('overflow', o.resize, false);
  el.addEventListener('overflowchanged', o.resize, false);
  o.resize();
  return o;
}

function hostsAreFromSameDomain (host1, host2) {
  var hosts = ['', ''];
  for (var i = 0; i < 2; i++) {
    hosts[i] = getHostnameAndProtocol(arguments[i]).split('\/\/');
    if (hosts[i].length == 2) hosts[i] = hosts[i][1]; // remove protocol if exists
    else hosts[i] = hosts[i][0];
    hosts[i] = hosts[i].split(':')[0]; // remove port if exists
    hosts[i] = hosts[i].split('.');
  }
  return hosts[0].length > 0 && hosts[1].length > 0 && hosts[0][hosts[0].length-1] == hosts[1][hosts[1].length-1] && (hosts[0].length == 1 || hosts[1].length == 1 || hosts[0][hosts[0].length-2] == hosts[1][hosts[1].length-2]);
}






function FrameCommunicator (iframe, debugLogName='') {
  this.debugLogName = debugLogName;
  // protected
  this.el = iframe;
  this.el.communicator = this;
  // restliche proterties stehen nach Konstruktor
  // init
  var _this = this;
  window.addEventListener('message', function (msg) {
    _this.interpretMessage.call(_this, msg);
  }, false);
  this.el.addEventListener('load', function () {
    _this.debugLog('onload fired');
    _this.checkForNewSite(2);
    if (_this.onload) _this.onload(_this);
  }, false);
}

FrameCommunicator.prototype = {
  // private
  currentLoc : null,
  currentPostMsg : null,
  resizeRepeatH : 0,
  resizeRepeatW : 0,
  currentSiteHasFapi : false,
  debugLogName : '',
  // public readonly
  siteAllows : 0,
  contentHeight : -1,
  contentWidth : -1,
  contentHostname : '',
  contentURL : '',
  contentTitle : '',
  // public
  onContentResize : null,  // wenn frame-seite ihre Groesse aendert
  onSiteReady : null,      // wenn frame-seite geparst/geladen wurde - wie onload, aber eventuell frueher, noch vor DOMReady
  onURLChanged : null,     // wenn frame-seite ihre URL aendert
  onTitleChanged : null,   // wenn frame-seite ihren Titel aendert
  onload : null,           // wenn frame-seite geladen - wie iframe.onload, wird jedoch erst aufgerufen, wenn Seitenhoehe bekannt ist
  _autoHeight : false, get autoHeight () { return this._autoHeight; }, set autoHeight (v) { this._autoHeight = v; if (v) this.el.style.height = (this.contentHeight >= 0)?(this.contentHeight+'px'):this.autoHeightValue; },
  _autoWidth : false, get autoWidth () { return this._autoWidth; }, set autoWidth (v) { this._autoWidth = v; if (v) this.el.style.width = (this.contentWidth >= 0)?(this.contentWidth+'px'):this.autoWidthValue; },
  autoHeightValue : '100%',
  autoWidthValue : '100px'
}

FrameCommunicator.prototype.checkForNewSite = /* private */ function (load, knownURL) {
  if (load & 1 != 0) this.currentSiteHasFapi = true;
  try { if (this.currentDoc != this.el.contentWindow.document.documentElement) { this.newSite(knownURL); return; } } catch (e) {
    try {
      if (this.el.contentWindow && this.currentLoc != this.el.contentWindow.location) {
        this.newSite(knownURL);
        return;
      }
    } catch (e) {}
    if (this.el.contentWindow.postMessage == this.el.contentWindow.postMessage && this.currentPostMsg != this.el.contentWindow.postMessage) this.newSite(knownURL);
    else if (load & 2 != 0 && this.el.contentWindow.postMessage != this.el.contentWindow.postMessage) this.newSite(knownURL);
  }
}

FrameCommunicator.prototype.setSrc = /* public */ function (url) {
  try {
    el.contentWindow.location.replace(url); // tries to change URL without browser history change
  } catch (e) {
    this.el.src = url;
  }
  this.checkForNewSite(0, url);
}

FrameCommunicator.prototype.newSite = /* private */ function (knownURL) {
  this.debugLog('New site detected...');
  this.currentLoc = this.el.contentWindow.location;
  this.currentPostMsg = this.el.contentWindow.postMessage;
  try { this.currentDoc = this.el.contentWindow.document.documentElement; } catch (e) { this.currentDoc = null; }
  // Informationen ueber Seite loeschen
  this.contentHeight = -1;
  this.contentWidth = -1;
  this.contentHostname = (knownURL)?getHostnameAndProtocol(knownURL):'';
  this.contentURL = (knownURL)?knownURL:'';
  this.siteAllows = 0;
  this.contentHeight = -1;
  this.contentWidth = -1;
  this.contentTitle = '';
  // Events ausfuehren
  if (this.onSiteReady) this.onSiteReady();
  if (this.onContentResize) this.onContentResize(this.contentWidth, this.contentHeight);
  if (this.onURLChanged) this.onURLChanged(this.contentHostname, this.contentURL);
  if (this.onTitleChanged) this.onTitleChanged(this.contentTitle);
  // Versuchen, neue Informationen zu bekommen / FrameAPI in der Seite zu erzeugen
  try {
    if (this.el.contentWindow.fapi) {
      this.debugLog('Site is on same location ('+this.el.contentWindow.location.href+') and has fapi. Allow all messages...');
      this.el.contentWindow.fapi.setTarget(this);
      this.el.contentWindow.fapi.setAllowed(65535);
    } else if (this.el.contentWindow.document.documentElement) {
      this.debugLog('Site is on same location ('+this.el.contentWindow.location.href+') and has no fapi. Create fapi with all messages...');
      let scr = this.el.contentWindow.document.createElement('script');
      scr.src = selfScriptSource_huwheigif;
      this.el.contentWindow.document.body.appendChild(scr);
    }
  } catch (e) {}
}

FrameCommunicator.prototype.checkForNewHostname = /* private */ function (host) {
  try { host = getHostnameAndProtocol(this.el.contentWindow.location.href); } catch (e) {}
  if (this.contentHostname == host) return;
  this.contentHostname = host;
  this.debugLog('New Hostname detected: '+this.contentHostname);
  if (this.contentURL.indexOf(host) != 0) this.contentURL = '';
  if (this.onURLChanged) this.onURLChanged(this.contentHostname, this.contentURL);
}

FrameCommunicator.prototype.interpretMessage = /* private */ function (msg) {
  if (msg.source != this.el.contentWindow) {
    this.debugLog('Message "'+msg.data+'" dropped because from wrong source!');
    return;
  } else this.debugLog('Message "'+msg.data+'" accepted!');
  var data = msg.data.split('&');
  this.checkForNewSite(1);
  this.checkForNewHostname(msg.origin);
  switch (data[0]) {
    case 'FrameCommunicator' :
      this.setState((data.length > 0)?parseInt(data[1]):0);
      break;
    case 'mousedown' :
      var x = undefined;
      var y = undefined;
      if (data.length >= 3) {
       x = parseInt(data[1]);
       y = parseInt(data[2]);
      }
      this.raiseMouseEvent('mousedown', x, y);
      break;
    case 'mouseup' :
      var x = undefined;
      var y = undefined;
      if (data.length >= 3) {
       x = parseInt(data[1]);
       y = parseInt(data[2]);
      }
      this.raiseMouseEvent('mouseup', x, y);
      break;
    case 'click' :
      this.raiseMouseEvent('click');
      break;
    case 'resize' :
      this.resize(parseInt(data[1]), parseInt(data[2]));
      break;
    case 'URLchange' :
      if (data.length < 2) break;
      var url = decodeURIComponent(data[1]);
      var fromSameOrigin = false;
      try { if (this.el.contentWindow.document) fromSameOrigin = true; } catch (e) {}
      if (fromSameOrigin || msg.origin == getHostnameAndProtocol(url)) this.newURL(url);
      break;
    case 'TitleChange' :
      if (data.length < 2) break;
      this.setTitle(decodeURIComponent(data[1]));
      break;
    case 'mousewheel' :
      this.raiseMouseEvent('mousewheel', parseInt(data[1]));
      break;
  }
}

FrameCommunicator.prototype.raiseMouseEvent = /* protected */ function  (type, x, y) {
  this.checkForNewSite(1);
  if (type == 'click') this.siteAllows |= 4;
  if (type == 'mousedown') this.siteAllows |= 8;
  if (type == 'mouseup') this.siteAllows |= 16;
  if (type == 'mousewheel') this.siteAllows |= 512;
  if (document.dispatchEvent) {
    var ev;
    // Bei MouseEvent sind offsetX/Y und pageX/y nicht ueberschreibbar! daher document.createEvent().
    ev = document.createEvent("Events"); 
    ev.initEvent(type, true, true);
    if (type == 'mousewheel') {
      ev.wheelDelta = x;
    } else if (!isNaN(x) && !isNaN(x)) { // falls gegeben, Cursorposition an Event haengen
      this.siteAllows |= 2;
      ev.offsetX = x;
      ev.offsetY = y;
      p = this.el;
      do {
        x += p.offsetLeft;
        y += p.offsetTop;
        p = p.offsetParent;
      } while (p);
      ev.pageX = x;
      ev.pageY = y;
    }
    this.el.dispatchEvent(ev);
  } else {
    var ev = document.createEventObject(); 
    this.el.fireEvent('on' + type, ev); 
  }
}

FrameCommunicator.prototype.debugLog = /* private */ function (msg) {
  if (this.debugLogName == '') return;
  var d = new Date();
  console.log('['+d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+' '+d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()+']   Frame "'+this.debugLogName+'":  '+msg);
}

FrameCommunicator.prototype.setState = /* protected */ function (_allowed) {
  this.checkForNewSite(1);
  this.siteAllows = _allowed;
}

FrameCommunicator.prototype.resize = /* protected */ function (w, h) {
  if (isNaN(w)) w = -1;
  if (isNaN(h)) h = -1;
  this.debugLog('Resize w='+w+' h='+h);
  this.checkForNewSite(1);
  if (this.contentWidth == w && this.contentHeight == h) return;
  if (w != -1) this.siteAllows |= 64;
  if (h != -1) this.siteAllows |= 32;
  this.contentWidth = w;
  this.contentHeight = h;

  if (h >= 0 && this.autoHeight) {
    var ch = this.el.offsetHeight;
    if (h + 15 <= ch) this.resizeRepeatH++;
    if (this.resizeRepeatH > 5) return; // Rekursion verhindern
    if (h + 10 > ch || h < ch + 20) this.el.style.height = h + 15 + 'px';
    if (this.resizeRepeatHTimer) window.clearTimeout(this.resizeRepeatHTimer);
    var _this = this;
    this.resizeRepeatHTimer = window.setTimeout(function () { _this.resizeRepeatH = 0; }, 100); 
  } else if (this.autoHeight) this.el.style.height = this.autoHeightValue;

  if (w >= 0 && this.autoWidth) {
    var cw = this.el.offsetWidth;
    if (w <= cw) this.resizeRepeatW++;
    if (this.resizeRepeatW > 5) return; // Rekursion verhindern
    if (w > cw || w < cw - 5) this.el.style.width = w + 'px';
    if (this.resizeRepeatWTimer) window.clearTimeout(this.resizeRepeatWTimer);
    var _this = this;
    this.resizeRepeatWTimer = window.setTimeout(function () { _this.resizeRepeatW = 0; }, 100); 
  } else if (this.autoWidth) this.el.style.width = this.autoWidthValue;

  if (this.onContentResize) this.onContentResize(w, h);
}

FrameCommunicator.prototype.newURL = /* protected */ function (url) {
  this.checkForNewSite(1);
  if (!url) url = '';
  if (url != '') this.siteAllows |= 128;
  this.contentHostname = getHostnameAndProtocol(url);
  this.contentURL = url;
  if (this.onURLChanged) this.onURLChanged(this.contentHostname, this.contentURL);
}

FrameCommunicator.prototype.setTitle = /* protected */ function (title) {
  this.checkForNewSite(1);
  this.siteAllows |= 256;
  this.contentTitle = title;
  if (this.onTitleChanged) this.onTitleChanged(title, this);
}





function FrameAPI (__allowed, _target) {
  // private
  var win = (_target)?_target.el.contentWindow:window;
  if (win.fapi) return win.fapi;
  win.fapi = this;
  var target = _target;
  var allowed = 0;
  var evListeners = new Object();
  var _this = this;
  var lastURL = '';
  var autoFrameSize = false;

  var debugLog = /* private */ function (msg) {
    if (!target || target.debugLogName == '') return;
    var d = new Date();
    console.log('['+d.getFullYear()+'-'+(d.getMonth()+1)+'-'+d.getDate()+' '+d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()+']   Frame "'+target.debugLogName+'":  '+msg);
  };

  var mouseevent = /* private */ function (ev, type) {
    if (!ev) ev = win.event;
    if (type == 'DOMMouseScroll') type = 'mousewheel';
    var msg = [type];
    if (type == 'mousewheel') msg.push(ev.wheelDelta || -ev.detail);
    else if ((allowed & 2) && type != 'click') {
      msg.push(ev.clientX);
      msg.push(ev.clientY);
    }
    if (target) target.raiseMouseEvent.apply(target, msg);
    else win.parent.postMessage(msg.join('&'), '*');
  };

  var postState = /* private */ function () {
    /*if (target) target.setState(allowed);
    else */win.parent.postMessage('FrameCommunicator&'+allowed, '*');
  };

  var resize = /* private */ function (el, w, h) {
    try { if (autoFrameSize) return; } catch (e) { return; }
    if (!(allowed & 32) || isNaN(h)) h = -1;
    if (!(allowed & 64) || isNaN(w)) w = -1;
    /*if (target) target.resize(w, h);
    else */win.parent.postMessage('resize&'+w+'&'+h, '*');
  };

  var sendURL = /* private */ function () {
    if (lastURL == win.location.href) return;
    lastURL = win.location.href;
    /*if (target) target.newURL(lastURL);
    else */win.parent.postMessage('URLchange&'+encodeURIComponent(lastURL), '*');
  };

  var sendTitle = /* private */ function () {
    /*if (target) target.setTitle(win.document.title);
    else */win.parent.postMessage('TitleChange&'+encodeURIComponent(win.document.title), '*');
  };

  var redirectLink = /* private */ function (a) {
    if (a.href.indexOf('http') != 0) return;
    var hp = getHostnameAndProtocol(a.href);
  };

  this.setTarget = /* public */ function (_target) {
    target = _target;
  };

  this.overrideAutoFrameSize = /* public */ function () {
    autoFrameSize = true;
    /*if (target) target.resize(-1, -1);
    else */win.parent.postMessage('resize&-1&-1', '*');
  };

  this.redirectLinks = /* public */ function () {
    var observer = new MutationObserver(function(mutationsList, observer) {
      for (let mutation of mutationsList)
        if (mutation.type === 'childList')
          for (let i = 0; i < mutation.addedNodes.length; i++)
            if (mutation.addedNodes[i].nodeType == 1 && mutation.addedNodes[i].tagName == 'a')
              redirectLink(mutation-addedNodes[i]);
    });
    observer.observe(document.documentElement, { childList: true, subtree: true });
    let a = document.documentElement.getElementsByTagName('a');
    for (var i = 0; i < a.length; i++)
      redirectLink(a[i]);
  };

  this.setAllowed = /* public */ function (_allowed) {
    if (allowed == _allowed) {
      //if (evListeners['resize']) evListeners['resize'].resize();
      debugLog('New allowed flags ignored, because already set ('+win.location.href+').');
      return;
    }
    if (win.parent == win) {
      debugLog('New allowed flags ignored, because not in frame ('+win.location.href+').');
      return;
    }
    allowed = _allowed;
    debugLog('New allowed flags accepted ('+win.location.href+').');
    
    function addListener (name, type, f, el, b) {
      if (evListeners[name]) return;
      if (!el) el = win.document;
      var ev = new Object();
      ev['event'] = type;
      ev['el'] = el;
      ev['fkt'] = function (ev) { f.call(_this, ev, type); }
      el.addEventListener(type, ev['fkt'], b);
      evListeners[name] = ev;
    }
    function rmListener (name) {
      if (!evListeners[name]) return;
      evListeners[name]['el'].removeEventListener(evListeners[name]['event'], evListeners[name]['fkt']);
      delete evListeners[name];
    }
    
    if (allowed & 1) { postState(); addListener('postState', 'load', postState, win); } else rmListener('postState');
    if (allowed & 4) addListener('mouseeventC', 'click', mouseevent); else rmListener('mouseeventC');
    if (allowed & 8) addListener('mouseeventD', 'mousedown', mouseevent); else rmListener('mouseeventD');
    if (allowed & 16) addListener('mouseeventU', 'mouseup', mouseevent); else rmListener('mouseeventU');
    if (allowed & 96) { if (!evListeners['resize']) evListeners['resize'] = createScrollRectEventListener(win.document.documentElement, resize, win); }
    else { if (evListeners['resize']) { evListeners['resize'].free(); delete evListeners['resize']; } }
    if (allowed & 128) {
      lastURL = '';
      sendURL();
      addListener('sendURLL', 'load', sendURL, win);
      addListener('sendURLH', 'hashchange', sendURL, win);
      addListener('sendURLP', 'popstate', sendURL, win);
      try { // bekanntes Bug: Das abhoeren von replaceState und PushState funktioniert nach reload der Seite nicht!
        if (!evListeners['OldPushState']) { evListeners['OldPushState'] = win.history.pushState; win.history.pushState = function (obj, title, url) { evListeners['OldPushState'].call(win.history, obj, title, url); sendURL(); }; }
        if (!evListeners['OldReplaceState']) { evListeners['OldReplaceState'] = win.history.replaceState; win.history.replaceState = function (obj, title, url) { evListeners['OldReplaceState'].call(win.history, obj, title, url); sendURL(); }; }
      } catch (e) {}
    } else {
      rmListener('sendURLL');
      rmListener('sendURLH');
      rmListener('sendURLP');
      try {
        if (evListeners['OldPushState']) { win.history.pushState = evListeners['OldPushState']; delete evListeners['OldPushState']; }
        if (evListeners['OldReplaceState']) { win.history.replaceState = evListeners['OldReplaceState']; delete evListeners['OldReplaceState']; }
      } catch (e) {}
    }
    if (allowed & 256) {
      sendTitle();
      addListener('sendTitleL', 'load', sendTitle, win);
      try {
        if (!evListeners['titleObserver']) {
          evListeners['titleObserver'] = new MutationObserver(sendTitle);
          var config = { childList: true };
          evListeners['titleObserver'].observe(win.document.getElementsByTagName('title')[0], config);
        }
      } catch (e) { addListener('sendTitleI', 'DOMNodeInserted', sendTitle, win.document.getElementsByTagName('title')[0]); }
    } else {
      rmListener('sendTitleL');
      try {
        if (evListeners['titleObserver']) {
          evListeners['titleObserver'].disconnect();
          delete evListeners['titleObserver'];
        }
      } catch (e) { rmListener('sendTitleI'); }
    }
    if (allowed & 512) {
      addListener('sendMousewheel1', 'mousewheel', mouseevent, win.document);
      addListener('sendMousewheel2', 'DOMMouseScroll', mouseevent, win.document);
    } else {
      rmListener('sendMousewheel1');
      rmListener('sendMousewheel2');
    }
  }

  // init
  if (__allowed) this.setAllowed(__allowed);
}

function fapiIndexLocation (loc) {
  if (navigator.userAgent.indexOf(' Atom\/') >= 0) return; // keep simple site without frame in Atom-preview
  if (/bot|googlebot|bingbot|crawler|spider|robot|crawling/i.test(navigator.userAgent)) return; // don't forward crawlers!
  if (loc != '' && parent == self)
    parent.location.href = absolutePath(loc, self.location.href) + '?' + self.location.href;
}
(function () {
  let fapiAllowed = fapiStandard;
  try {
    if (parent.fapi) fapiAllowed = 65535;
  } catch (e) { }
  if (!window.fapi) new FrameAPI(fapiAllowed);
})();
