function WindowControl(config) {
  var self=this, winCtrl=this;



  // *** check prerequisites, prepare data ***
  var evalXPath=HelperFunctions.getEvalXPathFunction({tmb:'http://reiseland-brandenburg.de/XML', xhtml:'http://www.w3.org/1999/xhtml'});
  if(
        !document.getElementById
    ||  !HelperFunctions.getXMLHttpRequest()
    ||  !evalXPath
  ) {
    window.location.href='notsupported.php';
    return null;
  }



  // *** process configurations ***
  self.config=config; (function() {
    if(config.vcardBaseURI) self.vcardBaseURI=config.vcardBaseURI;
    config.layout=config.layout.split(' ');
    for(var i=0; i<config.layout.length; i++) config.layout[ config.layout[i].replace(/^\s*(.*)\s*$/, '$1') ]=true;
  })();
  var getExtraQuery=self.getExtraQuery=function() { return '&lang='+config.langKey+'&idVer='+config.idVer; };



  // *** notifications ***
  // must be executed before map is initialized, because map sends events via these functions
  var parentWinCtrl=null;
  self.onSetIntendedGeoPosition=function(pos) {
    if(parentWinCtrl) parentWinCtrl.eventFromIframe({window:window, type:'setIntendedGeoPos', pos:pos?[pos[0], pos[1]]:null});
  }
  self.onMapSetPosition=function(pos) {
    if(parentWinCtrl) parentWinCtrl.eventFromIframe({window:window, type:'setPosition', pos:[pos[0], pos[1]]});
  }
  self.onMapMoveToPosition=function(pos) {
    if(parentWinCtrl) parentWinCtrl.eventFromIframe({window:window, type:'moveToPosition', pos:[pos[0], pos[1]]});
  }
  self.onVCardClick=function(uri) {
    if(parentWinCtrl) parentWinCtrl.openVCard(uri);
    else window.open(uri, 'vcard').focus();
  }


  
  // *** loader ***
  self.loader=new (function() {
    var self=this;
    self.show=function() { if(parentWinCtrl) parentWinCtrl.loader.show(); };
    self.hide=function() { if(parentWinCtrl) parentWinCtrl.loader.hide(); };
  })();


  
  // *** initialize map ***
  var map=self.map=new Map({minimap:undefined!=window.__isMinimap}, self);
  if(window.__isMinimap) {
    map.state.set('minimap');
    document.getElementsByTagName('body')[0].className+=' minimap';
  } else {
    switch(config.user) {
    case 'ic':
      map.switchScale(0);
      break;
    default:
      map.switchScale(2);
    }
  }



  // *** adapt to window size ***
  self.onResize=null;
  (function() {
    var windowOnresize=window.onresize;
    function onResize(e) {
      var winSize=HelperFunctions.getWindowSize();
      if(!winSize) return;
      var w=Math.max(200, winSize[0])
          , h=Math.max(200, winSize[1])
      ;
      map.setSize(w, h);
      if(windowOnresize) return windowOnresize.call(window, e);
    };
    window.onresize=self.onResize=onResize;
    var resizeInterval=window.setInterval(
      function() {
        if( !HelperFunctions.getWindowSize() ) return;
        window.clearInterval( resizeInterval );
        onResize();
      }
      , 100
    );
  })();



  // *** externally controlled actions ***
  self.activateIconByObjectXML=function(objXML) {
    var coords=evalXPath(objXML, 'tmb:coords')[0]
        , pos=[parseInt(coords.getAttribute('x')), parseInt(coords.getAttribute('y'))]
        , poi=map.pois.getByObjXML(objXML)
    ;
    map.switchScale(null, Number.MAX_VALUE);
    map.moveToGeoPosition(pos);
    map.setIntendedGeoPosition(pos);
    map.pois.clearActive();
    map.pois.setActive( map.pois.calc_id(objXML) );
    map.icons.redraw();
    map.infoBox.add(poi.pos, [poi]); 
  }
  self.activateIconById=function(id) {
    var poi=map.pois.getById(id);
    if(!poi || !poi.objXML) return;
    return self.activateIconByObjectXML(poi.objXML);
  }



  // *** navigation ***
  var navigation=new Navigation();
  function Navigation() {
    var elem=document.getElementById('jsMap-navi')
        , documentOnmouseup=document.onmouseup
        , move=null
        , elemMousedown=null
        , regexp={
            naviClass:{
              move:/(^|\s+)(top|right|bottom|left)(\s+|$)/
              , scale:/(^|\s+)(scaleup|scaledown)(\s+|$)/
              , middle:/(^|\s+)(middle)(\s+|$)/
            }
            , mousedown:/(^|\s+)mousedown(\s+|$)/
          }
        , mapNaviClassTo={
            move:{top:[0, 1], right:[-1, 0], bottom:[0, -1], left:[1, 0]}
            , scale:{scaleup:1, scaledown:-1}
          }
    ;
    if(window.__isMinimap) elem.className+=' minimap';
    function onmousedown(e) {
      e||(e=window.event);
      var m;
      if( m=regexp.naviClass.move.exec(this.className) )  map.addMove( move=mapNaviClassTo.move[ m[2] ] );
      else if( m=regexp.naviClass.scale.exec(this.className) ) map.switchScale( null, mapNaviClassTo.scale[ m[2] ] );
      else if( m=regexp.naviClass.middle.exec(this.className) ) {
//        map.setGeoPosition( map.scales.posRefToGeo([0, 0]) );
//        map.switchScale(0);
        map.moveToBBox();
      }
      elemMousedown=this;
      this.className+=' mousedown';
      return HelperFunctions.stopPropagation(e);
    }
    function onmouseup(e) {
      if(move) {
        map.addMove([-move[0], -move[1]]);
        move=null;
      }
      if(elemMousedown) {
        elemMousedown.className=elemMousedown.className.replace(regexp.mousedown, '$2');
        elemMousedown=null;
      }
      if(documentOnmouseup) return documentOnmouseup.call(document, e);
    }
    for(var i=0, divs=elem.getElementsByTagName('div'); i<divs.length; i++) divs[i].onmousedown=onmousedown;
    document.onmouseup=onmouseup;
  }



  // *** mouse handling ***
  document.ondragstart=document.onselectstart=function(e) { return false; };



  // *** key handling ***
  (function() {
    var keysDown=[];
    function getKeyCode(e) {
      if(!e) return null;
      if(e.which) return e.which;
      if(e.keyCode) return e.keyCode;
    }
    function onKeyDown(e) {
      if(!e) e=window.event;
      var k=getKeyCode(e);
      switch(k) {
        case 37: if(!keysDown[k]) map.addMove([1, 0]); keysDown[k]=1; break;
        case 38: if(!keysDown[k]) map.addMove([0, 1]); keysDown[k]=1; break;
        case 39: if(!keysDown[k]) map.addMove([-1, 0]); keysDown[k]=1; break;
        case 40: if(!keysDown[k]) map.addMove([0, -1]); keysDown[k]=1; break;
      }
    }
    document.onkeydown=onKeyDown;
    function onKeyUp(e) {
      if(!e) e=window.event;
      var k=getKeyCode(e);
      switch(k) {
        case 33: map.switchScale(null, 1); break;
        case 34: map.switchScale(null, -1); break;
        case 37: if(keysDown[k]) map.addMove([-1, 0]); delete keysDown[k]; break;
        case 38: if(keysDown[k]) map.addMove([0, -1]); delete keysDown[k]; break;
        case 39: if(keysDown[k]) map.addMove([1, 0]); delete keysDown[k]; break;
        case 40: if(keysDown[k]) map.addMove([0, 1]); delete keysDown[k]; break;
      }
    }
    document.onkeyup=onKeyUp;
  })();



  // all done, notify parent
  (function() {
    var waitForParentWinCtrl=window.setInterval(
      function() {
        if(!parentWinCtrl && window!=window.parent && window.parent.winCtrl) {
          parentWinCtrl=window.parent.winCtrl;
          window.clearTimeout(waitForParentWinCtrl);
          parentWinCtrl.eventFromIframe({window:window, type:'ready'});
        }
      }
      , 500
    );
  })();



}

