/* SVN FILE:   $Id: GoogleMap.js 24 2009-04-15 12:45:16Z a.rouel $
 * SVN HEADER: 1.0
 ***
 * $title           Core/GoogleMap
 *
 * $description
 *    Abstraktion einer Standortkarte für AHG.
 *
 *
 * $classname       GoogleMap
 * $author          a.rouel
 * $copyright       $Copyright$
 * $version         $Revision: 24 $
 * $lastrevision    $Date: 2009-04-15 14:45:16 +0200 (Mi, 15 Apr 2009) $
 * $modifiedby      $LastChangedBy: a.rouel $
 * $lastmodified    $LastChangedDate: 2009-04-15 14:45:16 +0200 (Mi, 15 Apr 2009) $
 * $filesource      $URL: http://svn.babiel.com/Babiel/trunk/Projekte/Internet/Projekte/GoogleMap/NPS-Content/htdocs/includes/script/GoogleMap.js $
 * $keywords        Google, Map, Karte, marker, Markierung, faculty, facility, Einrichtung, Fachbereich
 * $require         Google Maps API
 *
 */

function GoogleMap(configuration) {

  /**
   * Attribut über die zur Laufzeit definierte Funktionen Zugriff auf 'normale'
   * Attribute und Funktionen bekommen.
   * Hinweis: Muss vor Benutzung aktualisiert werden (I = this;)!!!
   *
   * @var object
   * @access private
   */   
   
  /**
   * (Virtueller) Konstruktor der Klasse
   */
 this.__construct = function(configuration) {
    // Google Maps API laden
    this.setConfiguration(configuration);   
  };
 
  
  /**
   * aktueller EventListener der Karte
   *
   * @var object
   * @access private
   */
  var currentEventListener = false;

  
  /**
   * Standard Zoomstufe beim zentrieren auf einen Marker
   *
   * @var object
   * @access protected
   */
  this.defaultZoom = 18;  
  /**
   * Debug-Modus
   *
   *
   * @var object
   * @access protected
   */
  this.debug = false; 
  /**
   * Ausgabe für die Routenberechnung
   *
   * @var object
   * @access protected
   */
  this.direction = false;

 
  /**
   * IDs der HTML-Elemente für die Karte, Routenplaner etc.
   *
   * @var Array
   * @access protected
   */
  this.elements = {
    master : 'gmapFachbereicheContent',
    map : 'map',
    selectbox : 'GM_SelectBox',
    directions : 'GM_Directions',
    options : 'GM_Options'
  };

  
  /**
   * Geocoder Europa: Umwandeln der Adressdaten in die entsprechenden Geodaten
   * (Längengrad,Breitengrad)
   *
   * @var GClientGeocoder
   * @access private
   */
  var geocoder = new GClientGeocoder();

  
  /**
   * Symbole für die Markierungen
   *
   * @var Array
   * @access protected
   */
  this.icons = [];
  
    
  /**
   * Gebietsschemaparameter (z.B. de, en, fr)
   *
   * @var string
   * @access protected
   */
  this.locale = 'de';

  
  /**
   * Google Karte
   *
   * @var object
   * @access protected
   */
  this.map = false;

  
  /**
   * Definition der zu verarbeitenden Markierungsattribute (Achtung: Einige
   * Attribute sind für die Verarbeitung notwendig!)
   *
   * @var Array
   * @access protected
   */
  this.markerAttributesTmpl = [];
  // required attributes
  this.markerAttributesTmpl[0] = 'id'; // required
  this.markerAttributesTmpl[1] = 'class'; // required
  this.markerAttributesTmpl[2] = 'title'; // required  
  this.markerAttributesTmpl[3] = 'lat'; // required
  this.markerAttributesTmpl[4] = 'lng'; // required  
  this.markerAttributesTmpl[5] = 'zoomstart'; // required
  this.markerAttributesTmpl[6] = 'zoomend'; // required
  this.markerAttributesTmpl[7] = 'markertype'; // required
    
  /**
   * Verwalter für die Sichbarkeit aller Markierungen auf einer Karte
   *
   * @var Array
   * @access private
   */
  var markerManager = false;

  
  /**
   * Optionen einer Markierung
   *
   * @var Array
   * @access private
   */
  var markerOptions = [];

  
  /**
   * Markierungspunkte auf der Karte
   *
   * @var Array
   * @access protected
   */
  this.markers = [];

  
  /**
   * Konfiguration der Markierungen für die Karte
   *
   * @var Array
   * @access protected
   */
  this.markersDoc = [];

  
  /**
   * Konfigurationsdatei der Markierungen im XML-Format
   *
   * @var string
   * @access protected
   */  
  this.markersDocPath = System.appUri()+'includes/googleMaps/marker/index.xml';
  //Live: this.markersDocPath = location.hostname+'/includes/googleMaps/marker/index.xml';  
  //this.configDocPath = System.appUri()+'includes/googleMaps/gMapConfiguration.xml';
  
  /**
   * Auswählbare Einrichtungen (nach Name)
   *
   * @var string
   * @access protected
   */
  this.names = [];

  
  /**
   * Breitengrad zur Zentrierung der Karte nach Initialisierung
   *
   * @var float
   * @access protected
   */
  this.defaultLongitude = 10.451526;

  
  /**
   * Längengrad zur Zentrierung der Karte nach Initialisierung
   *
   * @var float
   * @access protected
   */
  this.defaultLatitude = 51.165691;

  this.zoomOnMarker = true;
  /**
   * Zoomstufe am Anfang
   *
   * @var integer
   * @access protected
   */
  this.defaultZoom = 6;
  this.defaultMarkerZoom = 15;
  this.validMarkerUrl = "";
  
  /**
   * Tooltip-Anzeige für Markierungen
   *
   * @var integer
   * @access protected
   */
  this.toolTip = false;
  
  this.configuration = [];
  /**
  * Setzen der GoogleMap Konfiguration
  */
   this.setConfiguration = function(configuration) {
    this.configuration = configuration;  
    try
      {
        this.debug = this.configuration["debug"];
        this.defaultLatitude = this.configuration["defaultLatitude"];
        this.defaultLongitude = this.configuration["defaultLongitude"];
        this.defaultZoom = this.configuration["defaultZoom"];   
        this.defaultMarkerZoom = this.configuration["defaultMarkerZoom"];   
        this.zoomOnMarker = this.configuration["zoomOnMarker"];
        this.validMarkerUrl = this.configuration["validUrlPath"];                      
      }
    catch(err)
      {
      //Handle errors here
      }
   }
  
  /**
   * Markierungen setzten
   *
   * @access protected
   */
  this.createMarkers = function() {
    // Auslesen des XML Files
    var request = GXmlHttp.create();
    request.open('GET', this.markersDocPath, true);

    // Callback
    request.onreadystatechange = function() {

      // Falls Server geantwortet hat
      if (request.readyState == 4) {

        // Parse XML Struktur
        var xmlDoc = GXml.parse(request.responseText);
        
        // Feststellen, wieviele <marker> es gibt; Durchlaufen der einzelnen
        // marker
   
        I.markersDoc = xmlDoc.documentElement.getElementsByTagName('marker');
       
        // Abruch wenn keine marker-Elemente gesetzt sind
        if ( !I.markersDoc ) {
           return false;
        }
        
        var floatRegExp = /[0-9]+[.]{1}[0-9]+/;
		      
        for ( var i = 0; i <  I.markersDoc.length ; i++) {
        
          // Attribute der Marker auslesen
          var attributes = [];
      
          for ( var x = 0; x < I.markerAttributesTmpl.length; x++) {                                    
              attributes[I.markerAttributesTmpl[x]] = I.markersDoc[i].getAttribute(I.markerAttributesTmpl[x]);              
          }
				
          // Inhalt des 'InfoWindow' holen         
          var currentMarkerInfoWindow = GXml.value(I.markersDoc[i].getElementsByTagName('infowindow')[0]);              
          var currentMarkerShadow =  System.appUri()+GXml.value(I.markersDoc[i].getElementsByTagName('markershadow')[0]);         
          var currentMarkerSymbol =  System.appUri()+GXml.value(I.markersDoc[i].getElementsByTagName('markersymbol')[0]);          
          
          // wenn die Extension "trim" geladen wurde, entferne führende
          // und anschließende Leerzeichen (Whitespaces)
          if (typeof currentMarkerInfoWindow.trim != 'undefined')
            currentMarkerInfoWindow = currentMarkerInfoWindow.trim();

          // wenn Längen- und Breitengrad gesetzt sind, dann Punkt setzten
          // sonst über Geocoder Punkt ermitteln

          if (floatRegExp.test(attributes['lat'])
              && floatRegExp.test(attributes['lng'])) {
            var point = new GLatLng(attributes['lat'], attributes['lng']);
			
            I.setMarker(point, attributes, currentMarkerInfoWindow,currentMarkerSymbol,currentMarkerShadow);
		
          } else if (tempStreet !== "" && tempZipcode !== "" && tempCity !== "") {
            alert('kein lat und lng gesetzt');
            I.setMarkerByAddress(attributes, currentMarkerInfoWindow);
          }		  		  
        }

        
        // ToolTip für Markierungen erstellen
        I.toolTip = document.createElement('div');
        I.toolTip.style.visibility = 'hidden';
        I.map.getPane(G_MAP_FLOAT_PANE).appendChild(I.toolTip);
        						
		//URL-Analyse um Mapnamen zu erhalten
    
        if(I.zoomOnMarker && location.pathname.match(/\/Unternehmen\/Niederlassungen\//i)){
        
          var urlparts = location.pathname.split('/');	          
          I.showMarkerByName( urlparts[urlparts.length-2], I.defaultMarkerZoom);
        }
        else{    
        // URL-Analyse Get-Parameter name auslesen um Mapname zu erhalten z.Z nicht verwendet
            var queryMarkerName = I.getQueryParameter('name');
          var queryZoom = I.getQueryParameter('zoom');
          if( queryMarkerName != null && queryMarkerName != '' && 
            queryZoom  != null && queryZoom != '') {
           
            I.showMarkerByName(queryMarkerName, I.defaultMarkerZoom);
          }
        }		      
      }
    };

    // Gib Ergebnis zurück
    request.send(null);
    I = this;

  };

  
  /**
   * Prüft den QueryString (GET-Parameter) auf einen bestimmten Schlüssel und liefert den gesetzten Wert zurück.
   */
  this.getQueryParameter = function(parameterName) {
    var pattern = new RegExp('([?]|[&])' + parameterName + '=([0-9A-Za-z_-]*)');
    var parameterValue = null;
    if( typeof window.location.search == "string") {
      var parts = pattern.exec(window.location.search);
	  if(parts!=null&&parts.length>1)
      parameterValue = parts[2];
    }
    return parameterValue;
  }
  
  
  /**
   * Gibt den Verwalter der Markierungen zurück (gegebenfalls wird er erzeugt)
   *
   * @access protected
   * @return markerManager
   */
  this.getMarkerManager = function() {
    if (markerManager === false) {
      // es wird der einsatz von 'MarkerManager' empfohlen, wenn
      // er vorhanden ist... verwende ihn, sonst 'GMarkerManager'
      // weitere Informationen unter:
      // http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager
	
      if (markerManager !== null) {
	
        var mgrOptions = {
          borderPadding :50,
          maxZoom :15,
          trackMarkers :true
        };
        markerManager = new GMarkerManager(this.map);
		
      } else {
        // GMarkerManager is deprecated
	
		markerManager = new GMarkerManager(this.map);
		
        //markerManager = new GMarkerManager(this.map);
      }
    }
    return markerManager;
  };

  /**
   * Gibt einen Marker über eine ID aus der Verwaltungsliste (this.markers)
   * zurück.
   *
   * @param id
   * @access protected
   * @return marker
   */
  this.getMarkerById = function(id) {
    return this.markers[id];
  };

  /**
   * Setzt einen Marker in die Verwaltungsliste (this.markers) (über eine ID).
   *
   * @param id
   * @access protected
   * @return marker
   */
  this.setMarkerById = function(id, marker) {
    this.markers[id] = marker;
  };
  

  /**
   * Markierung auf der Karte erstellen
   *
   * @param point,
   *          attributes, infoWindow
   * @access protected
   * @return marker
   */
  this.setMarker = function(point, attributes, infoWindow,markerSymbol,markerShadow) {

    // Icontype der Markierung setzen
    if (markerSymbol =="") {
	    alert("standard");
      attributes['icontype'] = 'standard';
      // Erweiterung des IconType um spezielle Attribute
      // Achtung: Das ist nur möglich weil die Grafik der Icons stets
      // die selben Maße und Form (Zeiger/Punkt) hat. Bei neuen Icons
      // in anderem Format muss dies im oberen Bereich
      // (Deklaration this.icons usw.) gesetzt werden.
      this.icons['standard'] = new GIcon();
      this.icons['standard'].image = "marker_paedia.png"; 
      this.icons[attributes['icontype']].shadow = System.appUri() + "includes/images/googleMaps/shadow.png";
      this.icons[attributes['icontype']].iconSize = new GSize(20.0, 34.0);
      this.icons[attributes['icontype']].shadowSize = new GSize(38.0, 34.0);
      this.icons[attributes['icontype']].iconAnchor = new GPoint(10.0, 34.0);
      this.icons[attributes['icontype']].infoWindowAnchor = new GPoint(10.0, 2.0);

      
      // Setze spezielles Icon in die Optionsliste einer Markierung
      markerOptions['icon'] = this.icons[(attributes['icontype'])];
    }
	else
	{	  	  
	  this.icons['marker'] = new GIcon();     
	  this.icons['marker'].image = markerSymbol;
	  this.icons['marker'].shadow = markerShadow;    
    //this.icons['marker'].iconSize = new GSize(20.0, 34.0);
    //alert(attributes['markertype']);
    if(attributes['markertype'] != "information"){
      this.icons['marker'].shadowSize = new GSize(38.0, 34.0);    
      this.icons['marker'].iconAnchor = new GPoint(10.0, 34.0);
    }
    else{
      this.icons['marker'].iconAnchor = new GPoint(8.0, 8.0);
    }    
    this.icons['marker'].infoWindowAnchor = new GPoint(10.0, 2.0);	  
	  markerOptions['icon'] = this.icons['marker'];
	}

    // Gebe Markierung aus...
    var marker = new GMarker(point, markerOptions);
    this.setMarkerById(attributes['id'], marker);
    // prüfe ob Informationen für das Fenster gesetzt wurden
    if (infoWindow != '') {
       // Mache beim Klick auf die Markierung die InfoBox auf.	
      GEvent.addListener(marker, 'click', function() {  marker.openInfoWindowHtml(infoWindow);	    });			 
 
      // Nach dem Öffnen des Fensters den Event-Handler 'onsubmit' (neu) setzen
      GEvent.addListener(this.map, 'infowindowopen', function() {
      // Routenberechnung bei Anfrage (onsubmit)
      var address = (attributes['street']+', '+attributes['zipcode']+' '+attributes['city']);

      var form = document.getElementById('directions_' + attributes['id']);
      if( form != null ) {
        form.onsubmit = function() {
            if( this.from.value != '' ) I.setDirections( this.from.value, address, I.locale );
            return false;
        };
      }
      });
    }

  // Tooltips sind zZ deaktiviert  
	if(attributes['name']!=null){
    // ToolTip für die Markierungen einfügen und EventListener kreieren
    marker.toolTip = '<div class="gmapTooltip"><nobr>' + attributes['name'] + '</nobr></div>';
    GEvent.addListener(marker, 'mouseover',
        function() {
          I.toolTip.innerHTML = marker.toolTip;
          var point = I.map.getCurrentMapType().getProjection().fromLatLngToPixel(
                  I.map.fromDivPixelToLatLng(new GPoint(0, 0), true),
                  I.map.getZoom());
          var offset = I.map.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getPoint(), I.map.getZoom());
          var anchor = marker.getIcon().iconAnchor;
          var width = marker.getIcon().iconSize.width;
          var height = I.toolTip.clientHeight;
          var pos = new GControlPosition(G_ANCHOR_TOP_LEFT, new GSize(offset.x
              - point.x - anchor.x + width, offset.y - point.y - anchor.y
              - height));
          pos.apply(I.toolTip);
          I.toolTip.style.visibility = 'visible';
        });
	}
	
	
    GEvent.addListener(marker, 'mouseout', function() {
      I.toolTip.style.visibility = 'hidden';
    });
	   
    this.getMarkerManager().addMarker(marker,attributes['zoomstart'], attributes['zoomend']);
   		
    return marker;
  };

  /**
   * Aufschlüsseln einer Adresse zu Längen- und Breitengrad und Markierung auf
   * der Karte erstellen
   *
   * @param attributes,
   *          infoWindow
   * @access protected
   */
  this.setMarkerByAddress = function(attributes, infoWindow) {
    if (attributes['street'] && attributes['zipcode'] && attributes['city']) {
      // Adresse zusammenbauen
      var address = attributes['street'] + ", " + attributes['zipcode'] + " "
          + attributes['city'];
      // Punkt anhand der Adresse ermitteln
      geocoder.getLatLng(address, function(responsePoint) {
        if (!responsePoint) {
          alert("Die gesuchte Adresse \"" + address
              + "\" konnte nicht gefunden werden!");
        } else {
          I.setMarker(responsePoint, attributes, infoWindow);
        }
      });
    } else {
      alert("error: street, zipcode and city required, only \"" + address + "\" is set");
      return false;
    }
  };
 
  /**
   * Karte generieren
   *
   * @access protected
   */
  this.loadMap = function() {
    if (GBrowserIsCompatible()) {
      if (this.map === false) {

        // Auslesen oder erstellen eines Allgemeinen Block-Elements für die
        // Karte
        var div = document.getElementById(this.elements['map']);
        
        if (div == null) {
          var div = document.createElement('div');
          div.id = this.elements['map'];
          div.className = 'map';
          if (document.getElementById(this.elements['master']) != null) {
            document.getElementById(this.elements['master']).appendChild(div);
          } else {
            document.getElementsByTagName("body")[0].appendChild(div);
          }
        }

        // Karte und Steuerung generieren
        this.map = new GMap2(div);
        this.map.addControl(new GLargeMapControl());
        this.map.addControl(new GMapTypeControl());

        // Zentriere Karte zu Beginn auf einen spez. Punkt
        var startPoint = new GLatLng(parseFloat(this.defaultLatitude),
            parseFloat(this.defaultLongitude));
        this.map.setCenter(startPoint, this.defaultZoom);

        // Zoomen mit Mausrad aktivieren
        // this.map.enableScrollWheelZoom();
        // GEvent.addDomListener( div,
        // (div.addEventListener?"DOMMouseScroll":"mousewheel"), function (e) {
        // if(window.event) { event.returnValue = false; } // IE
        // if(e.cancelable) { e.preventDefault(); } // DOM-Standard
        // } );

        // Erzeuge alle Standorte in der Karte
        this.createMarkers();     
        // Req. für Routenberechnung
        if (this.direction === false) {
          this.direction = new GDirections(this.map, document.getElementById(this.elements['directions']));
        }
      }
    }        
  };

  /**
   * Setze Daten fuer Route
   *
   * @param fromAddress,
   *          toAddress, locale
   * @access protected
   * @return true, false
   */
  this.setDirections = function(fromAddress, toAddress, locale) {
    // offenes Informationfenster schließen, sonst wird
    // die automatische Positionierung beeinflusst
    this.map.closeInfoWindow();

    if (this.direction != false) {
      this.direction.load("from: " + fromAddress + " to: " + toAddress, {
        "locale" :locale
      });

      // Füge Druck-Button ein
      var div = document.getElementById(I.elements['options']);
      if (div) {
        var printButton = document
            .getElementById(I.elements['options'] + '_element');

        // prüfen ob schon eine SelectBox da ist
        if (printButton == null) {
          // druckknopf bauen
          var printButton = document.createElement('input');
          printButton.id = I.elements['options'] + '_element';
          printButton.value = 'Drucken';
          printButton.type = 'button';
          printButton.className = 'button';
          printButton.onclick = function() {
            I.replaceClassName('gmprint');
            window.print();
          };
          div.appendChild(printButton);
        }
      }
      return true;
    } else
      return false;
  };

  /**
   * Setze Daten fuer Route
   *
   * @param fromAddress,
   *          toAddress, locale
   * @access protected
   * @return true, false
   */
  this.clearDirections = function() {
    // zuletzt angezeigte Route löschen wenn vorhanden
    this.direction.clear();

    if (this.direction != false) {
      // Lösche Druck-Button
      var div = document.getElementById(I.elements['options']);
      if (div) {
        var printButton = document.getElementById(I.elements['options'] + '_element');
        // prüfen ob Button da ist
        if (printButton) {
          div.removeChild(printButton);
        }
      }
    }
  };

  /**
   * Bei Auswahl eines Namens, auf Markierung zentrieren und Informationsfenster
   * öffnen
   *
   * @param name,
   *          (zoom)
   * @access protected
   */
  this.showMarkerByName = function(name) {

    // zuletzt angezeigte Route löschen wenn vorhanden
    //this.clearDirections();
	
    // Löschen aller Markierungen
    //this.getMarkerManager().clearMarkers();
	
    // überprüfe auf optionalen zoom-parameter
    if (this.showMarkerByName.arguments.length == 2) {
      var zoom = this.showMarkerByName.arguments[1];
    
    } else {
      var zoom = this.defaultZoom;
    }
    zoom = 15;
    // regulärer Ausdruck eines Float-Datentypes
    var floatRegExp = /[0-9]+[.]{1}[0-9]+/;
    // vorhergehenden Listener auflösen, sonst Probleme bei der Zuordnung
    /*if (currentEventListener != false) {
      GEvent.removeListener(currentEventListener);
    }*/   	
    for ( var i = 0; i < this.markersDoc.length; i++) {

      // Attribute der Marker auslesen
      var attributes = [];
      for ( var x = 0; x < I.markerAttributesTmpl.length; x++) {
        attributes[I.markerAttributesTmpl[x]] = this.markersDoc[i]
            .getAttribute(this.markerAttributesTmpl[x]);
      }
	  
      if (attributes['title'] == name) {

        // Inhalt des 'InfoWindow' holen
        var currentMarkerInfoWindow = GXml.value(I.markersDoc[i]
            .getElementsByTagName('infowindow')[0]);

        // wenn die Extension "trim" geladen wurde, entferne führende
        // und anschließende Leerzeichen (Whitespaces)
        if (typeof currentMarkerInfoWindow.trim != 'undefined')
          currentMarkerInfoWindow = currentMarkerInfoWindow.trim();

        // wenn Längen- und Breitengrad gesetzt sind, dann Punkt setzten
        // sonst über Geocoder Punkt ermitteln
        if (floatRegExp.test(attributes['lat'])
            && floatRegExp.test(attributes['lng'])) {
          var point = new GLatLng(attributes['lat'], attributes['lng']);
          this.map.setCenter(point, zoom);
          var marker = this.setMarker(point, attributes,
              currentMarkerInfoWindow);			 
          //marker.openInfoWindow(currentMarkerInfoWindow);
          // this.getMarkerById( attributes['id'] ).openInfoWindow(
          // currentMarkerInfoWindow );
        } else if (tempStreet !== "" && tempZipcode !== "" && tempCity !== "") {
        
          this.map.setCenter(point, zoom);
          this.setMarkerByAddress(attributes, currentMarkerInfoWindow);
          // this.getMarkerById( attributes['id'] ).openInfoWindow(
          // currentMarkerInfoWindow );
        }
      } else {
        //this.getMarkerManager().removeMarker(
           // this.getMarkerById(attributes['id']));
      }
    }
  }; 
  
  /**
   * Sucht in einer Zeichenkette (str) die in der Suchliste (searchStr)
   * angegebenen Zeichen und ersetzt sie durch eine Zeichenkette der
   * Ersetzungliste (replaceString). Die Position einer Zeichenkette in der
   * Suchliste verweist auf die zu ersetzende Zeichenkette in der Ersetzungliste
   * hin, d.h. die Länge der beiden Listen muss stets gleich sein.
   *
   * @param str,
   *          searchStr, replaceStr
   * @access protected
   */
  this.replacePlaceHolder = function(str, searchStr, replaceStr) {
    if (searchStr.length == replaceStr.length) {
      for ( var i = 0; i < replaceStr.length; i++) {
        str = str.replace(searchStr[i], replaceStr[i]);
      }
      return (str);
    } else
      alert("this.replacePlaceHolder() : abort, because searchStr.length and replaceStr.length not equal");
  };

  /**
   * Ermöglicht das Ändern von Klassennamen
   *
   * @author m.rolfes
   * @modified-by a.rouel
   * @access protected
   */
  this.replaceClassName = function(replaceClassName) {
    var allPageTags = [];
    allPageTags = document.getElementsByTagName('svg');
    for (i = 0; i < allPageTags.length; i++) {
      allPageTags[i].parentNode.className = replaceClassName;
    }
  };
  this.__construct(configuration); // Aufruf des virtuellen Konstruktors
}
