if( typeof gCLOOKCHAIN == 'undefined' ){
  var gCLOOKCHAIN = 'defined';

  /* CLookChain.js */
  var ALook = Class.create();
  ALook.prototype = {
    initialize: function() {
    }
  }
  
  /**
   * @brief Abstract basis class for Chain visualization
   *
   * - needs an Traindrawer
   * - needs an Url-Producer
   **/
  var gLookChainReferenceCount = gLookChainReferenceCount?gLookChainReferenceCount:0;
  var ALookChain = Class.create();
  ALookChain.prototype = Object.extend(new ALook(), {
    
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.init( aGlobalInstancName, aMainElemName );
    },
  
    init: function( aGlobalInstancName, aMainElemName) {
       this.refcount = gLookChainReferenceCount;
       gLookChainReferenceCount++;
       this.globalInstanceName = aGlobalInstancName;
       this.mainelemname       = aMainElemName;
  
       this.debugmode          = false;
       this.requeststatus      = 'initiallocatingrequest';
    },
  
    getElemName: function( shortname ){
      return 'CLookChain_' + this.refcount + '_' + shortname;
    },
    
    updatetime: 3,
    constantwidth:480,
    constantheight:1500,
  
    // 'all','some','few','no'
    showstopnames: 'few',
  
    // 'all','some','few','startstop','no'
    showstoptimes: 'all',

    showline: 'yes',
    showvehicles: true,
    showvehicledetails: true,
  
    titletext: 'Chain map',
    extraparameter: '',
  
    mapmaxgeox: 0,
    mapmaxgeoy: 0,
    mapmingeox: 0,
    mapmingeoy: 0,
    
    train:'',
    stationpool:'',
    station:'',
    
    // supported timetypes: 'looktime', 'currenttime'
    timetype: '',
    
    show: false,
  
    currentstopcanvas: 'undefined',
    currentlinecanvas: 'undefined',
    currenttextdiv: 'undefined',
    currentdetailsdiv: 'undefined',
    currentstoptextdiv: 'undefined',
    currentmousediv: 'undefined',
    mainelem: 'undefined',
    maxtextwidth: 160,
    offsetx: 200,
    offsety: 60,
    factorx: 40,
    factory: 27,
    aktiv:'',
    imagePath:'',
    
    /* functions */
    /* --------- */
    stop: function( mtrain ){
      if ( this.aktiv != '' ) this.aktiv.stop();
      // reinitialization
      this.aktiv = '';
      this.show = false;
      this.timetype = '';
      this.showvehicles = true;
      this.showvehicledetails = true;
      this.extraparameter = '';
      this.train = '';
      this.station = '';
      this.stationpool = '';
      if ( this.mainelem != 'undefined' ) this.mainelem.style.display = 'none';
    },
  
    restart: function( mupdatetime ){
      this.updatetime = mupdatetime;
      if ( this.aktiv != '' ) this.aktiv.stop();
      this.startanimation();
    },
  
    start: function( mtimetype, mupdatetime ){
      if( this.show == true ){
        this.stop();
      }
      if( mtimetype == 'looktime' || mtimetype == 'currenttime' ){
        this.timetype = mtimetype;
      } else {
        alert( 'CLookChain::start(): Unsupported timetype: ' + mtimetype );
        return;
      }
      this.initMainElem();
      this.updatetime = mupdatetime;
      this.show = true;
      this.requeststatus = 'start';
      this.handlenextstatus();
    },
  
    callback: function(){
      this.makeLocatingRequest();
    },
  
    makeLocatingRequest: function(){
      var myAjax = new Ajax.Request(
        this.getUrl(),
        {
          method: 'post',
          onComplete: this.handleLocatingResult.bind(this)
        }
      );
    },
  
    handleLocatingResult: function(ajaxResponse){
      // Interpret and deserialise response
      var response = eval('(' + ajaxResponse.responseText + ')');
      var l = response.look;
      
      // Behaviour after initial locating request
      if( this.requeststatus == 'initiallocatingrequest' ){
        // store stops and edges after initial request
        // and store it in the correct id-order
        this.handleInitialLocatingResult( l );
      } else if( this.requeststatus == 'updatinglocatingrequest' ){
        this.handleUpdatingLocatingResult( l );
      } else {
//        alert( 'CLookChain::handleLocatingResult(): Unexpected requeststatus: ' + this.requeststatus ); 
      }
    },
  
    handleUpdatingLocatingResult: function(l){
      // Show trains, Don't remember them
      if( this.showvehicles == true ){
        this.traindrawer.setTransformer( this.transformer );
        this.traindrawer.drawalltrains( l.trains, this.stops, this.edges, this.showvehicledetails );
      }
      this.traindrawer.show();
    },
  
    // Behaviour after initial locating request
    handleInitialLocatingResult: function(l){
      // store stops and edges after initial request
      // and store it in the correct id-order
      this.storestops(l);
      this.edges = new Array();
      for( var i=0; i< l.edges.length; i++){
        this.edges[l.edges[i].id] = l.edges[i];
      }
  
      // count appearences of stops on edges
      for( var i=0; i< this.edges.length; i++){
        if( this.edges[i] ){
          if( this.stops[this.edges[i].id1] )
            this.stops[this.edges[i].id1].count++;
          else
            alert( 'CLookChain::handleLocatingResult: Stop id ' + this.edges[i].id1 + ' not found' );
          
          if( this.stops[this.edges[i].id2] )
            this.stops[this.edges[i].id2].count++;
          else
            alert( 'CLookChain::handleLocatingResult: Stop id ' + this.edges[i].id2 + ' not found' );
        }
      }
      this.setGeometry();
      this.drawtitle();
      this.drawallstops();
      this.drawalledges();
      this.handlenextstatus();
    },
    
  
    getUrl: function (){
      if( typeof this.UrlProducer != 'undefined' )
        return this.UrlProducer.getUrl();
      alert( 'virtual function getUrl() or this.UrlProducer not implemented' );
      return '';
    },
  
  
    startanimation: function(){
      if( this.updatetime == 0 ){
        this.callback();
      } else {
        this.callback();
        this.aktiv = new PeriodicalExecuterForObjects(this, this.updatetime);
      }
    },
  
    handlenextstatus: function(){
      switch (this.requeststatus){
      case 'start':
        this.makeLocatingRequest();
        this.requeststatus = 'initiallocatingrequest';
        break;
      case 'initiallocatingrequest':
        this.startanimation();
        this.requeststatus = 'updatinglocatingrequest';
        break;
      case 'updatinglocatingrequest':
        break;
      default:
//        alert('Unknown Request Status: ' + this.requeststatus);
      }
    },
  
    appendMainChilds: function(){
       var elem = '';
  
       elem = this.getDivElemStandard( this.getElemName('underalldiv') );
       elem.style.zIndex = "+3";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('stopdiv') );
       elem.style.zIndex = "+4";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('traindiv') );
       elem.style.zIndex = "+5";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('defaultdiv') );
       elem.style.zIndex = "+6";
       if (this.debugmode==false)
         elem.style.display = "none";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('stopdetailsdiv') );
       elem.style.zIndex = "+7";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('traindetailsdiv') );
       elem.style.zIndex = "+8";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('detailsdiv') );
       elem.style.zIndex = "+9";
       this.mainelem.appendChild(elem);
  
       elem = this.getDivElemStandard( this.getElemName('abovealldiv') );
       elem.style.zIndex = "+10";
       this.mainelem.appendChild(elem);
    },
  
    initMainElem: function(){
      this.mainelem = $(this.mainelemname);
      CNodes.removeAllChilds( this.mainelem );
  
      this.appendMainChilds();
  
      this.mainelem.style.display = 'block';
      if( this.traindrawer )
        this.traindrawer.init( $(this.getElemName('traindiv')),
                             $(this.getElemName('traindetailsdiv')),
                             this.constantwidth, this.constantheight );

      this.stopdrawer.init( $(this.getElemName('stopdiv')),
                            $(this.getElemName('stopdetailsdiv')),
                            $(this.getElemName('detailsdiv')),
                            $(this.getElemName('abovealldiv')),
                            this.constantwidth, this.constantheight );
      this.clearcanvasfixed();
    },
  
  	morestopnames: function(){
  	  if( this.showstopnames == 'all' )
        return false;
      if( this.showstopnames == 'some' )
        this.showstopnames = 'all';
      if( this.showstopnames == 'few' )
        this.showstopnames = 'some';
      else
        this.showstopnames = 'few';
      this.drawallstops();
        return true;
  	},
  	lessstopnames: function(){
      if( this.showstopnames == 'no' )
        return false;
      if( this.showstopnames == 'few' )
        this.showstopnames = 'no';
      if( this.showstopnames == 'some' )
        this.showstopnames = 'few';
      else
        this.showstopnames = 'some';
      this.drawallstops();
      return true;
    },
    getCanvasElemStandard: function( canvasname ){
  /*    var elem = document.createElement('canvas');
      elem.id = canvasname;
      elem.style.position = 'absolute';
      elem.style.display = 'block';
      elem.left = 0;
      elem.top = 0;
      elem.width = constantwidth;
      elem.width = constantwidth;*/
      var elem = CNodes.getElem( 'canvas', canvasname, 'absolute', 'block',
                               0, 0, this.constantwidth, this.constantheight );
      elem.appendChild(document.createTextNode("CANVAS NOT AVAILABLE"));
      return elem;
    },
  
    addCanvasElem: function( canvasname, parentelem ){
      var elem = this.getCanvasElemStandard( canvasname );
      parentelem.appendChild(elem);
      if( typeof G_vmlCanvasManager != 'undefined' ) G_vmlCanvasManager.initElement(elem);
      return $( canvasname );
    },
  
    getDivElemStandard: function( divname ){
      var elem = document.createElement('div');
      elem.id = divname;
      elem.style.position = 'absolute';
      elem.style.display = 'block';
      elem.left = 0;
      elem.top = 0;
      return elem;
  //    return CNodes.getElem( 'div', divname, 'absolute', 'block',
  //                          0, 0, this.constantwidth, this.constantheight );
    },
  
    addDivElem: function( divname, parentelem ){
      var elem = this.getDivElemStandard( divname );
      parentelem.appendChild(elem);
      return $( divname );
    },
  
    clearcanvasfixed: function (){
      var canvaspageunderall = $(this.getElemName('underalldiv'));
      CNodes.removeAllChilds( canvaspageunderall );
      this.currentstoptextdiv = this.addDivElem( this.getElemName('stoptextdiv'), canvaspageunderall );
      this.currentlinecanvas = this.addCanvasElem( this.getElemName('linecanvas'), canvaspageunderall );
      this.currentstopcanvas = this.addCanvasElem( this.getElemName('stopcanvas'), canvaspageunderall );
      this.currentmousediv = this.addDivElem( this.getElemName('mousediv'), $(this.getElemName('abovealldiv')) );
      this.currentdetailsdiv = $(this.getElemName('detailsdiv'));
    },
     
    drawtitle:function(){
      this.currentstoptextdiv.innerHTML = this.currentstoptextdiv.innerHTML +
        '<div class="look_maptitle" style="position:absolute;' + 
        'width:' + this.constantwidth + 'px;' +
        'top:0px;' +
        'left:0px;' +
        '">' + this.titletext + '</div>';
    },
  
    drawtime:function(timetext){
      this.currenttextdiv.innerHTML = this.currenttextdiv.innerHTML +
        '<div class="look_maptitletime" style="position:absolute;' + 
        '">' + timetext + '</div>';
    }
  
  });
  
  
  //##############################################################################################
  //### Abstract Look-Object with own map ########################################################
  //##############################################################################################
  
  
  /**
   * @brief Abstract basis class for chains with own map
   *
   * Derived from abstract basis class ALookChain.
   **/
  var ALookChainWithOwnMap = Class.create();
  ALookChainWithOwnMap.prototype= Object.extend(new ALookChain(), {
    
    alphashadow:this.alphashadow?this.alphashadow:99,
    
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.init( aGlobalInstancName, aMainElemName);
    },
    
    handlenextstatus: function(){
      switch (this.requeststatus){
      case 'start':
        this.makeLocatingRequest();
        this.requeststatus = 'initiallocatingrequest';
        break;
      case 'initiallocatingrequest':
        var myAjax = new Ajax.Request(
          this.getMapUrl() + "look_json=yes&",
          {
            method: 'post',
            onComplete: this.handleMapResult.bind(this)
          }
        );
        this.requeststatus = 'maprequest';
        break;
      case 'maprequest':
        this.startanimation();
        this.requeststatus = 'updatinglocatingrequest';
        break;
      case 'updatinglocatingrequest':
        break;
      default:
//        alert('Unknown Request Status: ' + this.requeststatus);
      }
    },
  
    // Behaviour after initial locating request
    handleInitialLocatingResult: function(l){
      // store stops and edges after initial request
      // and store it in the correct id-order
      this.stops = new Array();
      for( var i=0; i< l.stops.length; i++){
        this.stops[l.stops[i].id] = l.stops[i];
      }
      this.edges = new Array();
      for( var i=0; i< l.edges.length; i++){
        this.edges[l.edges[i].id] = l.edges[i];
      }
  
      // count appearences of stops on edges
      for( var i=0; i< this.edges.length; i++){
        if( this.edges[i] ){
          if( this.stops[this.edges[i].id1] )
            this.stops[this.edges[i].id1].count++;
          else
            alert( 'CLookChain::handleLocatingResult: Stop id ' + this.edges[i].id1 + ' not found' );
          
          if( this.stops[this.edges[i].id2] )
            this.stops[this.edges[i].id2].count++;
          else
            alert( 'CLookChain::handleLocatingResult: Stop id ' + this.edges[i].id2 + ' not found' );
        }
      }
      
      // calculate bounding box for stops
      this.mapmingeox =  180000000;
      this.mapmaxgeox = -180000000;
      this.mapmingeoy =   90000000;
      this.mapmaxgeoy =  -90000000; 
      for( var i=0; i< this.stops.length; i++){
        if( this.stops[i] ){
          this.mapmingeox = Math.min( this.stops[i].x, this.mapmingeox ); 
          this.mapmaxgeox = Math.max( this.stops[i].x, this.mapmaxgeox );
          this.mapmingeoy = Math.min( this.stops[i].y, this.mapmingeoy );
          this.mapmaxgeoy = Math.max( this.stops[i].y, this.mapmaxgeoy );
        }
      }
  //    alert( 'Bounding box: ' + this.mapmingeox+','+this.mapmaxgeox+','+this.mapmingeoy+','+this.mapmaxgeoy);
      var distx = this.mapmaxgeox - this.mapmingeox;
      var disty = this.mapmaxgeoy - this.mapmingeoy;
      // some padding
      this.mapmingeox -= distx / 20;
      this.mapmaxgeox += distx / 20;
      this.mapmingeoy -= disty / 20;
      this.mapmaxgeoy += disty / 10;
  
      this.handlenextstatus();
    },
    
  
    getMapUrl: function (){
        return look_gettpl('urlLocating') +
          'performGis=yes&' +
          'REQMapCornerLowerLeftX=' + this.mapmingeox + '&' +
          'REQMapCornerLowerLeftY=' + this.mapmingeoy + '&' +
          'REQMapCornerUpperRightX=' + this.mapmaxgeox + '&' +
          'REQMapCornerUpperRightY=' + this.mapmaxgeoy + '&' +
          'REQMapWidth=' + this.constantwidth + '&' +
          'REQMapHeight=' + this.constantheight + '&' +
          'lookchain=yes&';
    },
  
    handleMapResult: function(ajaxResponse){
      // Behaviour after map request
      if( this.requeststatus == 'maprequest' ){
        // Interpret and deserialise response
        var response = eval('(' + ajaxResponse.responseText + ')');
        // Store real map geometry from Map Request
        this.mapmingeox = response.map.CornerLowerLeftX;
        this.mapmaxgeox = response.map.CornerUpperRightX;
        this.mapmingeoy = response.map.CornerLowerLeftY;
        this.mapmaxgeoy = response.map.CornerUpperRightY;
        this.setGeometry();
        this.drawmap(response.map.catenateRequiredParameters);
        this.drawtitle();
        this.drawallstops();
        this.drawalledges();
        this.handlenextstatus();
      } else {
//        alert( 'CLookChain::handleMapResult(): Unexpected requeststatus: ' + this.requeststatus ); 
      }
    },
  
    drawmap: function(urlparams){
      var mappage = $(this.getElemName('mapdiv'));
      if( !mappage ) alert( 'drawmap: mappage not found');
      var imgurl = look_gettpl( "urlStandard" ) + urlparams + "performGis=1&REQMapOutput=IMAGEFILE&REQMapTask=RECALL&";
      mappage.innerHTML = '<img ' +
        'id=\"locref\" ' +
        'class=\"mainmap\" ' +
        'name=\"map\" ' +
        'src=\"' + imgurl + '\" ' +
        'width=\"' + this.constantwidth + '\" ' +
        'height=\"' + this.constantheight + '\" ' +
        'border=\"0\" ' +
        'hspace=\"0\" ' +
        'vspace=\"0\" ' +
        '/>';
    },
  
    setGeometry: function (){
      this.mainelem.style.width = Math.round(this.constantwidth) + 'px';
      this.mainelem.style.height = Math.round(this.constantheight) + 'px';
      $(this.getElemName('mapshadowdiv')).style.width = Math.round(this.constantwidth) + 'px';
      $(this.getElemName('mapshadowdiv')).style.height = Math.round(this.constantheight) + 'px';
      if(ie6==true && ie7==false) $(this.getElemName('mapshadowdiv')).style.display = 'none';
      this.setTransformer();
      this.stopdrawer.setTransformer( this.transformer );
    },
  
    setTransformer: function(){
      this.transformer = new CMapTransformer( this.constantwidth, this.constantheight,
                                              this.mapmingeox, this.mapmaxgeox,
                                              this.mapmingeoy, this.mapmaxgeoy );
    },
  
    appendMainChilds: function(){
      var elem = this.getDivElemStandard( this.getElemName('mapdiv') );
      elem.style.zIndex = "+1";
      this.mainelem.appendChild(elem);
      
      elem = this.getDivElemStandard( this.getElemName('mapshadowdiv') );
      elem.style.zIndex = "+2";
      elem.style.backgroundColor = "#FFFFFF";
      elem.style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=' +this.alphashadow+ ')';
      elem.style.opacity = this.alphashadow / 100;
      this.mainelem.appendChild(elem);
      
      ALookChain.prototype.appendMainChilds.call(this);
    },
  
    changeAlphaBy: function ( value ){
      this.setMapAlpha( value + this.alphashadow );
    },
  
    setMapAlpha: function ( newAlpha ){
      this.alphashadow = Math.min(100,Math.max( 0, Math.round(newAlpha) ));
      if( $(this.getElemName('mapshadowdiv')) ){
        $(this.getElemName('mapshadowdiv')).style.filter = 'progid:DXImageTransform.Microsoft.Alpha(opacity=' +this.alphashadow+ ')';
        $(this.getElemName('mapshadowdiv')).style.opacity = this.alphashadow / 100;
      }
    },
  
    storestops: function(l){
       this.stops = new Array();
       for( var i=0; i< l.stops.length; i++){
         this.stops[l.stops[i].id] = l.stops[i];
       }
    },
    
    drawallstops: function(){
      this.stopdrawer.drawallstops( this.transformer, this.stops, this.showstopnames, this.showstoptimes );
    },
  
    drawalledges: function(){
      if( typeof this.edgedrawer != 'undefined' ){
        this.edgedrawer.drawalledges(this.currentlinecanvas,this.transformer, this.stops,this.edges);
      } else {
        alert( 'drawalledges: No Edgedrawer defined' );
      }
    }
  
  });
  
  //##############################################################################################
  //### Abstract Look-Object with foreign map ####################################################
  //##############################################################################################
  
  
  /**
   * @brief Abstract basis class for chains with foreign map
   *
   * Derived from abstract basis class ALookChain.
   **/
  var CLookChainWithForeignMap = Class.create();
  CLookChainWithForeignMap.prototype= Object.extend(new ALookChain(), {
    initialize: function( aMainElemName, aWidth, aHeight, aMinX, aMaxX, aMinY, aMaxY) {
      this.init2( aMainElemName, aWidth, aHeight, aMinX, aMaxX, aMinY, aMaxY);
    },
  
    init2: function( aMainElemName, aWidth, aHeight, aMinX, aMaxX, aMinY, aMaxY ){
      this.constantheight = aHeight;
      this.constantwidth = aWidth;
      this.edgedrawer = new CLookEdgeDrawerChain();
      this.stopdrawer = new CLookStopDrawerChain();
      this.init( 'Dummy', aMainElemName);
      this.transformer = new CMapTransformer( aWidth, aHeight, aMinX, aMaxX, aMinY, aMaxY );
      this.stopdrawer.setTransformer( this.transformer );
      this.UrlProducer = new CLookUrlProducerTrip( this );
    },
    startbytrain: function( mtrain ){
      if( this.show == true ){
        this.stop();
      }
      if( typeof mtrain == 'undefined' ) alert( 'CLookChainWithForeignMap::startbytrain: No Train given' );
      this.train = mtrain;
      this.start( 'currenttime', 0 );
    },
    setGeometry: function(){
    },
  
    drawallstops: function(){
      this.stopdrawer.drawallstops( this.transformer, this.stops, false, false );
    },
    drawalledges: function(){
      this.edgedrawer.drawalledges(this.currentlinecanvas,this.transformer, this.stops,this.edges);
    }
  });
  
  //##############################################################################################
  //### Abstract Look-Object for perl lines ######################################################
  //##############################################################################################
  
  
  /**
   * @brief Abstract basis class for schematical perl lines
   *
   * Derived from abstract basis class ALookChain.
   **/
  var ALookPerl = Class.create();
  ALookPerl.prototype = Object.extend(new ALookChain(), {
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.initperl( aGlobalInstancName, aMainElemName);
    },
  
    initperl: function( aGlobalInstancName, aMainElemName ){
      this.edgedrawer = new CLookEdgeDrawerPerl();
      this.init( aGlobalInstancName, aMainElemName);
      this.maxheight = 1500;
      this.offsetx = 200;
      this.factorx = 40;
      this.offsety = 60;
      this.factory = 27;
      this.maxcol = 0;
      this.maxrow = 0;
    },
    setTransformer: function(){
      // calculate maximal row and column
      for( var i = 0; i < this.stops.length; i++ ){
        if( this.stops[i] ){
          this.maxcol = Math.max(this.maxcol,this.stops[i].col);
          this.maxrow = Math.max(this.maxrow,this.stops[i].row);
        }
      }
  
      if( this.maxrow * this.factory + this.offsety + 20 > this.maxheight ){
        this.factory = ( this.maxheight - this.offsety - 20 ) / this.maxrow;
      }
      this.offsetx = Math.round(( this.constantwidth - this.factorx * this.maxcol ) / 2);
  
      this.transformer = new CPerlTransformer( this.offsetx, this.factorx,
                                               this.offsety, this.factory );
    },
    startbytrain: function( mtrain, mtimetype, mupdatetime ){
      if( this.show == true ){
        this.stop();
      }
      if( typeof mtrain == 'undefined' ) alert( 'CLookPerlLine::startbytrain: No Train given' );
      this.train = mtrain;
      this.start( mtimetype, mupdatetime );
    },
    setGeometry: function(){
      this.setTransformer();
      this.constantheight = Math.round(this.transformer.getY(this.maxcol,this.maxrow) + this.offsety);
      this.mainelem.style.height = this.constantheight + 'px';
      this.mainelem.height = this.constantheight;
      this.mainelem.style.width = Math.round(this.constantwidth) + 'px';
      this.stopdrawer.setTransformer( this.transformer );
    },
  
    storestops: function(l){
      this.stops = new Array();
      var maxrow = 0;
      for( var i=0; i< l.stops.length; i++){
        this.stops[l.stops[i].id] = l.stops[i];
        this.stops[l.stops[i].id].x = l.stops[i].col;
        this.stops[l.stops[i].id].y = l.stops[i].row;
        maxrow = Math.max( maxrow, l.stops[i].row );
      }
      this.maxstopcol = new Array();
      for( var i=0; i<= maxrow; i++){
        this.maxstopcol[i] = 0;
      }
      for( var i=0; i< l.stops.length; i++){
        this.maxstopcol[l.stops[i].row] = Math.max(this.maxstopcol[l.stops[i].row], l.stops[i].col);
      }
    },
    drawallstops: function(){
      this.stopdrawer.drawallstops( this.transformer, this.stops, this.maxstopcol, this.showstopnames, this.showstoptimes );
    },
  
    /**
     * @brief calls Edgedrawer to draw all edges   
     *
     * ALookPerl-canvas for edges must be set to variable height of main element before drawing
     **/
    drawalledges: function(){
    if( typeof this.edgedrawer != 'undefined' ){
      $(this.getElemName('underalldiv')).style.height = this.constantheight;
      this.currentlinecanvas.style.height= this.constantheight;
      this.edgedrawer.drawalledges(this.currentlinecanvas,this.transformer, this.stops,this.edges);
    } else {
      alert( 'drawalledges: No Edgedrawer defined' );
    }
  }
  });
  
  
  //##############################################################################################
  //### Classes for different URL-Calls ##########################################################
  //##############################################################################################
  
  
  
  var CLookUrlProducerTrip = Class.create();
  CLookUrlProducerTrip.prototype = {
    initialize: function( lookchainobject ){
      this.lco = lookchainobject;
    },
    getUrl: function (){
      var looktime='';
      var trainid='';
      if( this.lco.timetype == 'looktime' ){
        looktime = 'look_requesttime=' + look_time.getString() + '&';
      }
      if( this.lco.train != '' ) {
        trainid = 'look_trainid=' + this.lco.train + '&';
      } else {
        return look_gettpl('urlLook')+'performLocating=0&';
      }
      return look_gettpl('urlLookChainUpdate') +
        'performLocating=16&' + 
        looktime + 
        trainid +
        'look_json=yes&' +
        this.lco.extraparameter;
    }
  }        
  
  var CLookUrlProducerLine = Class.create();
  CLookUrlProducerLine.prototype = {
    initialize: function( lookchainobject ){
      this.lco = lookchainobject;
    },
    getUrl: function (){
      var looktime='';
      var trainid='';
      if( this.lco.timetype == 'looktime' ){
        looktime = 'look_requesttime=' + look_time.getString() + '&';
      }
      if( this.lco.train != '' ) {
        trainid = 'look_trainid=' + this.lco.train + '&';
      } else {
        return look_gettpl('urlLook')+'performLocating=0&';
      }
      return look_gettpl('urlLookChainUpdate') +
        'performLocating=32&' + 
        looktime + 
        trainid +
        'look_json=yes&' +
        this.lco.extraparameter;
    }
  }
  
  var CLookUrlProducerSpider = Class.create();
  CLookUrlProducerSpider.prototype = {
    initialize: function( lookchainobject ){
      this.lco = lookchainobject;
    },
    getUrl: function (){
      var looktime='';
      var stationid='';
      if( this.lco.timetype == 'looktime' ){
        looktime = 'look_requesttime=' + look_time.getString() + '&';
      }
      if( this.lco.station != '' && this.lco.stationpool != '' ){
        stationid = 'look_station='+this.lco.station+'&look_stationpool='+this.lco.stationpool+'&';
      } else {
        return look_gettpl('urlLook')+'performLocating=0&';
      }
      return look_gettpl('urlLookChainUpdate') +
        'performLocating=64&' + 
        looktime + 
        stationid +
        'look_json=yes&' +
        this.lco.extraparameter;
    }
  }
  
  
  //##############################################################################################
  //### Classes for different transformation of coordinates ######################################
  //##############################################################################################
  
  
  var ATransformer = Class.create();
  ATransformer.prototype = {
    initialize: function() {
    },
  
    getX: function (x, y){
      alert( 'abstract method ATransformer::transformX( x, y ) not implemented!!' );
    },
  
    getY: function (x, y){
       alert( 'abstract method ATransformer::transformY( x, y ) not implemented!!' );
    }
  }
  
  var CMapTransformer = Class.create();
  CMapTransformer.prototype = Object.extend(new ATransformer(), {
    
    initialize: function( aWidth, aHeight, aMinX, aMaxX, aMinY, aMaxY ){
      this.width  = aWidth;
      this.height = aHeight;
		if( typeof gGoogleBoundingBox != 'undefined' ){
			this.minx   = gGoogleminx;
			this.maxx   = gGooglemaxx;
			this.miny   = gGoogleminy;
			this.maxy   = gGooglemaxy;
		}else{
			this.minx   = aMinX;
			this.maxx   = aMaxX;
			this.miny   = aMinY;
			this.maxy   = aMaxY;
		}
    },
  
    getX: function (x, y){
      return this.width*((x-this.minx)/(this.maxx-this.minx));
    },
  
    getY: function (x, y){
      return this.height*((this.maxy-y)/(this.maxy-this.miny));
    }
  });
  
  var CPerlTransformer = Class.create();
  CPerlTransformer.prototype = Object.extend(new ATransformer(), {
    
    initialize: function(  aoffsetx, afactorx, aoffsety, afactory ){
      this.offsetx  = aoffsetx;
      this.factorx = afactorx;
      this.offsety = aoffsety;
      this.factory = afactory;
  //    alert( 'CPerlTransformer initialized: ' + this.offsetx+ ' ' + this.factorx+ ' ' + this.offsety+ ' ' +this.factory );
    },
  
    getX: function (x, y){
//      if( y > 40 ) alert ('y out of bounds: ' + y );
      return x*this.factorx+this.offsetx;
    },
  
    getY: function (x, y){
      return y*this.factory+this.offsety;
    }
  });
  
  
  //##############################################################################################
  //### Concrete classes for look-objects ########################################################
  //##############################################################################################
  
  
  /**
   * @brief Class for Spider map visualization
   *
   * Derived from abstract basis class ALookChainWithOwnMap.
   *
   * Uses Locating Request Type 64 which meens "netspdier".
   **/
  var CLookNetspider = Class.create();
  CLookNetspider.prototype = Object.extend(new ALookChainWithOwnMap(), {
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.UrlProducer = new CLookUrlProducerSpider( this );
      this.traindrawer = new CLookTrainDrawerLeftAndRightBesideMainDiv();
      this.stopdrawer = new CLookStopDrawerNetspider();
      this.edgedrawer = new CLookEdgeDrawerChain();
      this.init( aGlobalInstancName, aMainElemName);
    },
    startbystation: function( mstationpool, mstation, mtimetype, mupdatetime ){
      if( this.show == true ){
        this.stop();
      }
      this.stationpool = mstationpool;
      this.station = mstation;
      this.start( mtimetype, mupdatetime );
    }
  });
  
  
  /**
   * @brief Class for Line map visualization
   *
   * Derived from abstract basis class ALookChainWithOwnMap.
   *
   * Uses Locating Request Type 32 which meens "chain line".
   **/
  var CLookLineMap = Class.create();
  CLookLineMap.prototype = Object.extend(new ALookChainWithOwnMap(), {
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.UrlProducer = new CLookUrlProducerLine( this );
      this.traindrawer = new CLookTrainDrawerLeftAndRightBesideMainDiv();
      this.stopdrawer = new CLookStopDrawerChain();
      this.edgedrawer = new CLookEdgeDrawerChain();
      this.init( aGlobalInstancName, aMainElemName);
    },
    startbytrain: function( mtrain, mtimetype, mupdatetime ){
      if( this.show == true ){
        this.stop();
      }
      if( typeof mtrain == 'undefined' ) alert( 'CLookLineMap::startbytrain: No Train given' );
      this.train = mtrain;
      this.start( mtimetype, mupdatetime );
    }
  
  });
  
  /**
   * @brief Class for Trip map visualization
   *
   * Derived from abstract basis class ALookChainWithOwnMap.
   *
   * Uses Locating Request Type 16 which meens "single line".
   **/
  var CLookTripMap = Class.create();
  CLookTripMap.prototype = Object.extend(new ALookChainWithOwnMap(), {
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.UrlProducer = new CLookUrlProducerTrip( this );
      this.traindrawer = new CLookTrainDrawer();
      this.stopdrawer = new CLookStopDrawerChain();
      this.edgedrawer = new CLookEdgeDrawerChain();
      this.init( aGlobalInstancName, aMainElemName);
    },
    startbytrain: function( mtrain, mtimetype, mupdatetime ){
      if( this.show == true ){
        this.stop();
      }
      if( typeof mtrain == 'undefined' ) alert( 'CLookTripMap::startbytrain: No Train given' );
      this.train = mtrain;
      this.start( mtimetype, mupdatetime );
    }
  });
  
  
  /**
   * @brief Class for Line visualization as schematical perl line
   *
   * Derived from abstract basis class ALookPerl.
   *
   * Uses Locating Request Type 32 which meens "chain line".
   **/
  var CLookPerlLine = Class.create();
  CLookPerlLine.prototype = Object.extend(new ALookPerl(), {
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.UrlProducer = new CLookUrlProducerLine( this );
      this.stopdrawer = new CLookStopDrawerPerl();
      this.traindrawer = new CLookTrainDrawerLeftAndRightCloseBy();
      this.initperl( aGlobalInstancName, aMainElemName);
    },
    setGeometry: function(){
      ALookPerl.prototype.setGeometry.call( this );
    }
  });
  
  /**
   * @brief Class for trip visualization as schematical perl line
   *
   * Derived from abstract basis class ALookPerl.
   *
   * Uses Locating Request Type 16 which meens "single line".
   **/
  var CLookPerlTrip = Class.create();
  CLookPerlTrip.prototype = Object.extend(new ALookPerl(), {
    initialize: function( aGlobalInstancName, aMainElemName) {
      this.UrlProducer = new CLookUrlProducerTrip( this );
      this.stopdrawer = new CLookStopDrawerPerl();
      this.traindrawer = new CLookTrainDrawerLeftAndRightCloseBy();
      this.initperl( aGlobalInstancName, aMainElemName);
    },
    setGeometry: function(){
      ALookPerl.prototype.setGeometry.call( this );
    }
  });
  
  
  
  
}
  


