cms.modules.gmap = function(moduleId, mapData)
{
    this.moduleId = moduleId;
    this.useResults = false;
    this.useFilters = false;
    this.filteredResult = false;
    this.locationMarker = null;
    
    this.mapData = mapData;
    if (this.mapData.ZoomLevel == undefined)
    {
        this.mapData.ZoomLevel = 2;    
    }
    if (this.mapData.Center != undefined)
    {
        // Zet de string om naar een LatLng object
        var segments = this.mapData.Center.split(/, /);
        this.mapData.Center = new google.maps.LatLng(segments[0], segments[1]);
    }
    else
    {
        this.mapData.Center = new google.maps.LatLng(52.1554, 5.393257);
    }
    
    this.PANNING_ALLOW = 0;
    this.PANNING_REPOSITION = 1;
    this.PANNING_DISABLE = 2;
    this.OFFSET_CONTROL = 10;

    // Bevat een instantie van de filter code
    this._filtersId = this._filters = null;
    this._resultsId = this._results = null;

    this.kmlLoaded = function()
    {
    	this.show();
    	
        if (this.useResults)
        {
            this._results.fetchResults();
            this._results.show();
        }

        if (this.useFilters)
        {
            this._filters.switchInputElements(true);
            if (this.filteredResult)
            {
                // Haal de bounding box op van de kaart en bereken daarmee de pixeloffset van de marker
                if (this._filterOptions.markerOnLocation)
                {
                    this.setLocationMarker(this.mapData.Center);
                    
                    // @fixme Kan nu nog niet, omdat we geen pixel offset hebben
                    // this.displayWmsInfoWindow(this.mapData.Center, pixel);
                }
                if (this._filterOptions.centerOnLocation)
                {
                    this.mapData.GoogleMap.panTo(this.mapData.Center);
                    this.mapData.GoogleMap.setZoom(this.mapData.ZoomLevel);
                }
                
                // Als we een WMS window willen tonen, moeten we wachten tot de kaart
                // klaar is met zoomen en pannen
                if (this.mapData.Wms
                    && this.mapData.WmsLayers)
                {
                    // Als we gaan pannen wachten we tot de pan actie klaar is
                    if (this._filterOptions.centerOnLocation)
                    {
                        this.mapIdleCallback = this.showWmsInfoWindowForMarker;
                    }
                    else
                    {
                        this.showWmsInfoWindowForMarker();
                    }
                }
            }
        }
    };
    
    this.showWmsInfoWindowForMarker = function()
    {
        // Bereken de huidige pixel offset op basis van de bounding box
        var boundingBox = this.mapData.GoogleMap.getBounds();
        
        var latOffset = this.locationMarker.getPosition().lat() - boundingBox.getNorthEast().lat();
        var lngOffset = this.locationMarker.getPosition().lng() - boundingBox.getNorthEast().lng();
        
        var latDiff = boundingBox.getSouthWest().lat() - boundingBox.getNorthEast().lat();
        var lngDiff = boundingBox.getSouthWest().lng() - boundingBox.getNorthEast().lng();
        var xPixelDiff = Math.abs(latDiff) / this.mapData.Width;
        var yPixelDiff = Math.abs(lngDiff) / this.mapData.Height;
        
        var xPixel = Math.round(Math.abs(latOffset) / xPixelDiff);
        var yPixel = Math.round(Math.abs(lngOffset) / yPixelDiff);
        
        // Toon het venster
        var pixel = new google.maps.Point(xPixel, yPixel);
        this.displayWmsInfoWindow(this.locationMarker.getPosition(), pixel);        
    };
    
    this.setLocationMarker = function(location)
    {
        // Verwijder een bestaand info window
        if (this.locationMarkerInfoWindow != null)
        {
            this.locationMarkerInfoWindow.setMap(null);
        }
        // Verwijder een bestaande marker
        if (this.locationMarker != null)
        {
            this.locationMarker.setMap(null);
        }
        this.locationMarker = new google.maps.Marker({
            position: location, 
            map: this.mapData.GoogleMap 
        });                  
    };

    this.enableResults = function(moduleId)
    {
        this.useResults = true;
        this._resultsId = moduleId;
        this._results = new cms.modules.gmapResults(this);
    };

    this.enableFilters = function(moduleId, options)
    {
        this.useFilters = true;
        this._filtersId = moduleId;
        this._filterOptions = options;
        this._filters = new cms.modules.gmapFilters(this, options);
    };

    this.disableFilters = function()
    {
        this.useFilters = false;
    };

    this.loadMarkers = function(page, kmlUri, filteredResult)
    {
        if (filteredResult == null)
        {
            filteredResult = false;
        }
        
        if (this.useFilters)
        {
            if (kmlUri == null)
            {
                // Als we de methode zonder kmlUri aanroepen gaan we eerst een adres geocoden
                return this._filters.prepareKmlUri();
            }
            else
            {
                this.filteredResult = filteredResult;
            }
        }
        else
        {
            kmlUri = this.mapData.Kml;
        }
        
        // Als we geen kml URL ontvangen hebben verbergen we de kaart en de resultaten
        if (kmlUri == null)
        {
        	if (this.useResults)
        	{
        		this._results.hide();
        	}
        	this.hide();
        	
        	return;
        }
        
        if (this.useResults)
        {
            if (page)
            {
                kmlUri += '&cms[cm' + this.moduleId + '][page]=' + page;
            }
            if (this._results.perpage)
            {
                kmlUri += '&cms[cm' + this.moduleId + '][perpage]=' + this._results.perpage;
            }
            if (this._results.sort)
            {
                kmlUri += '&cms[cm' + this.moduleId + '][sort]=' + this._results.sort;
            }
        }

        this.RenderKml(kmlUri, jQuery.proxy(this.kmlLoaded, this));
    };
    
    this.hide = function()
    {
    	$(this.mapData.Canvas).hide();
    };

    this.show = function()
    {
    	$(this.mapData.Canvas).show();
    };
    
    this.Initialize = function()
    {
    };

    this._getMapType = function(type)
    {
        switch (type)
        {
            case 1:
            case 'satellite':
                return google.maps.MapTypeId.SATELLITE;

            case 2:
            case 'hybrid':
                return google.maps.MapTypeId.HYBRID;

            case 3:
            case 'physical':
                return google.maps.MapTypeId.TERRAIN
        }
        return google.maps.MapTypeId.ROADMAP;
    };

    this._DrawMap = function(Center, Initialize)
    {
        // Iets uitzoomen, zodat alles er goed op staat.
        var ZoomLevel = this.mapData.ZoomLevel;

        if (Center != null)
        {
            this.mapData.Center = Center;
        }

        if (Initialize)
        {
            var mapOptions = {
                center : this.mapData.Center,
                zoom : ZoomLevel,
                mapTypeId : this._getMapType(this.mapData.MapType),
                crollwheel : (this.mapData.ScrollWheelZoom == 1),
                disableDoubleClickZoom : (this.mapData.DoubleClickZoom == 0) 
            };
            this.mapData.GoogleMap.setOptions(mapOptions);
            
            // Voeg eventuele WMS lagen toe
            if (this.mapData.Wms
                && this.mapData.WmsLayers)
            {
                for (layerId in this.mapData.WmsLayers)
                {
                    var layerData = this.mapData.WmsLayers[layerId];
                    
                    // Voeg de laag toe aan de kaart
                    this.addWmsLayer(layerData.url + '?', layerData.layer, layerData.style);
                }
            }
        }
        else
        {
            this.mapData.GoogleMap.setZoom(ZoomLevel);
            this.mapData.GoogleMap.panTo(this.mapData.Center);
        }
    };

    this.DefineIcon = function(IconData)
    {
        if (this.GetIconByID_(IconData.ID) == null)
        {
            IconData.Object = this.CreateIcon_(IconData);
            this.Icons.push(IconData);
        }
    };

    this.focusMarker = function(n)
    {
        google.maps.event.trigger(this.mapData.geoXml.gmarkers[n-1], 'click');
    };

    this.RenderKml = function(kml, callback)
    {
        // Stel de KML als nieuwe KML in
        if (kml == null)
        {
            kml = this.mapData.Kml;
        }

        // Voeg de KML layer toe
        if (this.mapData.kmlParser == undefined)
        {
            this.mapData.kmlParser = new geoXML3.parser({
                map: this.mapData.GoogleMap,
                afterParse : callback,
				zoom : false
            });
        }
        this.mapData.kmlParser.parse(kml);
    };

    /*
     * @todo Op dit moment wordt er geen mouse-over en mouse-out op de markers afgevuurt 
     */
    /*
    this.addMarker = function(marker, name, desc, imagefile, n)
    {
        // Stel de onmouseover en onmouseout in
        if (this.useResults)
        {
            var self = this;
            google.maps.event.addListener(marker, "mouseover", function() { this._results.highlightResult(n+1); }.bind(this));
            google.maps.event.addListener(marker, "mouseout", function() { this._results.clearHighlightResult(n+1); }.bind(this));
        }

        // Voeg de marker toe
        this.mapData.GoogleMap.addOverlay(marker);

        return true;
    };
    */

    this.Render = function()
    {
        this.mapData.Canvas = document.getElementById(this.mapData.ElementID);

        if (this.mapData.LoadingImage)
        {
            this.mapData.Canvas.style.backgroundImage = "";
        }

        this.mapData.GoogleMap = new google.maps.Map(this.mapData.Canvas);
        this._DrawMap(null, true);

        // @todo Manager wordt momenteel niet gebruikt
        // this.mapData.Manager = new MarkerManager(this.mapData.GoogleMap);

        if (this.mapData.PanningAction != this.PANNING_DISABLE)
        {
            google.maps.event.bind(this.mapData.GoogleMap, "drag", this, this.OnDrag);
        }
        
        // Luister naar het idle event van de kaart
        google.maps.event.addListener(this.mapData.GoogleMap, 'idle', jQuery.proxy(this.onMapIdle, this));

        // Controls

        // Stel de weergave van de panning/zoom control in
        // @todo Mogelijkheid om toegestane kaarttypes te kiezen ontbreekt nog
        var mapOptions = {
            panControl : (this.mapData.PanningAction == this.PANNING_ALLOW
                && this.mapData.PanningControl >= 0),
            panControlOptions : {
                position : this.GetControlPosition(this.mapData.PanningControl)
            },
            scaleControl : this.mapData.ScaleControl >= 0,
            scaleControlOptions : {
                position : this.GetControlPosition(this.mapData.ScaleControl)
            },
            mapTypeControl : this.mapData.MapTypeControl >= 0,
            mapTypeControlOptions : {
                position : this.GetControlPosition(this.mapData.MapTypeControl)
            },
            streetViewControl : this.mapData.StreetViewControl >= 0,
            streetViewControlOptions : {
                position : this.GetControlPosition(this.mapData.StreetViewControl)
            },
            backgroundColor : '#ffffff'
        };

        // Stel de nieuwe opties in
        this.mapData.GoogleMap.setOptions(mapOptions);

        // Render de KML die bij de map hoort
        this.loadMarkers();
    };

    this.GetControlPosition = function(Numeric)
    {
        var Anchor;
        switch (Numeric)
        {
            case 0: Anchor = google.maps.ControlPosition.TOP_RIGHT; break;
            case 1: Anchor = google.maps.ControlPosition.TOP_LEFT; break;
            case 2: Anchor = google.maps.ControlPosition.BOTTOM_RIGHT; break;
            case 3: Anchor = google.maps.ControlPosition.BOTTOM_LEFT; break;
        }
        return Anchor;
    };
    
    this.onMapIdle = function()
    {
        if (this.mapIdleCallback)
        {
            this.mapIdleCallback();
            this.mapIdleCallback = null;
        }
    };

    this.OnDrag = function()
    {
        if (!this.mapData.GoogleMap.getBounds().contains(this.mapData.Center) && this.mapData.PanningAction == this.PANNING_REPOSITION)
            this.mapData.GoogleMap.panTo(this.mapData.Center);
    };
    
    this.addWmsLayer = function(baseURL, layers, styles)
    {
        this.wmsBaseUrl = baseURL;
        this.wmsLayers = layers;
        
        var tileHeight = 256;
        var tileWidth = 256;
        var opacityLevel = 0.75;
        var isPng = true;
        var minZoomLevel = 2;
        var maxZoomLevel = 28;

        var wmsParams = [
            "REQUEST=GetMap",
            "SERVICE=WMS",
            "VERSION=1.1.1",
            "BGCOLOR=0xFFFFFF",
            "TRANSPARENT=TRUE",
            "SRS=EPSG:900913", // 3395? 
            "WIDTH="+ tileWidth,
            "HEIGHT="+ tileHeight
            ];

        this.overlayOptions = 
        {
            getTileUrl: function(coord, zoom) 
            {
                var lULP = new google.maps.Point(coord.x*256,(coord.y+1)*256);
                var lLRP = new google.maps.Point((coord.x+1)*256,coord.y*256);
            
                var projectionMap = new MercatorProjection();
                
                var lULg = projectionMap.fromDivPixelToSphericalMercator(lULP, zoom);
                var lLRg  = projectionMap.fromDivPixelToSphericalMercator(lLRP, zoom);
                    
                var lUL_Latitude = lULg.y;
                var lUL_Longitude = lULg.x;
                var lLR_Latitude = lLRg.y;
                var lLR_Longitude = lLRg.x;     
                
                cms.modules.gmap.boundingBox = lUL_Longitude + "," + lUL_Latitude + "," + lLR_Longitude + "," + lLR_Latitude;
                var urlResult = baseURL + this.parcelParams.join("&") + "&bbox=" + cms.modules.gmap.boundingBox;
                return urlResult;
            },
            tileSize: new google.maps.Size(tileHeight, tileWidth),
            minZoom: minZoomLevel,
            maxZoom: maxZoomLevel,
            opacity: opacityLevel,
            isPng: isPng
        };      
        
        this.overlayOptions.parcelParams = wmsParams.concat([
             "FORMAT=image/png8",
             "LAYERS=" + layers,
             "STYLES=" + styles  
         ]);
        
        // Voeg de overlay toe aan de kaart
        overlayWMS = new google.maps.ImageMapType(this.overlayOptions);
        this.mapData.GoogleMap.overlayMapTypes.insertAt(0, overlayWMS);
        
        this.mapData.GoogleMap.setOptions({
            mapTypeControlOptions: {
            mapTypeIds: [
              'wms',
              google.maps.MapTypeId.ROADMAP,
              google.maps.MapTypeId.TERRAIN,
              google.maps.MapTypeId.SATELLITE,
              google.maps.MapTypeId.HYBRID
            ],
            style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
          }
        });
        
        // Voeg een onclick toe aan de nieuwe laag, om informatie van de WMS layer op te vragen
        // @fixme Wat gebeurd er als we meerdere lagen toevoegen?
        google.maps.event.addListener(this.mapData.GoogleMap, 'click', jQuery.proxy(this.handleWmsClick, this));
    };
    
    this.handleWmsClick = function(mouseEvent)
    {
        this.displayWmsInfoWindow(mouseEvent.latLng, mouseEvent.pixel);
    };
    
    this.displayWmsInfoWindow = function(latLng, pixel)
    {
        var uri = this.mapData.WmsProxy + 
            '&cms[cm' + this.moduleId + '][bbox]=' + this.mapData.GoogleMap.getBounds().toUrlValue() +
            '&cms[cm' + this.moduleId + '][lat]=' + latLng.lat() +
            '&cms[cm' + this.moduleId + '][lng]=' + latLng.lng() +
            '&cms[cm' + this.moduleId + '][x]=' + pixel.x +
            '&cms[cm' + this.moduleId + '][y]=' + pixel.y;
        
        // Plaats de marker op de aangeklikte positie
        this.setLocationMarker(latLng);
        
        // Centreer op de marker
        /*
        this.mapData.GoogleMap.panTo(latLng);
        
        // Zoom tot een mimimum
        if (this.mapData.GoogleMap.getZoom() < 10)
        {
            this.mapData.GoogleMap.setZoom(10);
        }
        */

        // Roep de informatie voor het informatie window op via AJAX
        $.ajax({
            url : uri,
            dataType : 'html',
            context : this,
            success: this.handleWmsResult
          });        
    };
    
    this.handleWmsResult = function(htmlResult)
    {
        this.locationMarkerWindow = new google.maps.InfoWindow({
            content : htmlResult
        });
        this.locationMarkerWindow.open(this.mapData.GoogleMap, this.locationMarker);
        
        // Registreer de onclick handler op de marker
        google.maps.event.addListener(this.locationMarker, 'click', jQuery.proxy(function() {
            this.toggleLocationMarkerInfoWindow();
        }, this));
    };
    
    this.toggleLocationMarkerInfoWindow = function()
    {
        if (this.locationMarkerWindow.getMap())
        {
            this.locationMarkerWindow.setMap(null);
        }
        else
        {
            this.locationMarkerWindow.setMap(this.mapData.GoogleMap);
        }
        
    };
};

cms.modules.gmapFilters = function(gmap, options)
{
    this.gmap = gmap;
    this.options = options;
    this.form = document.getElementById('gmapFiltersForm' + gmap.moduleId);
    
    // Formulier bestaat niet, schakel filters uit
    if (this.form == null)
    {
        gmap.disableFilters();
    }

    this.prepareKmlUri = function()
    {
        // Schakel de filter velden uit
        this.switchInputElements(false);
        
        // Zoek alle velden op die onderdeel zijn van de Geocoder aanroep
        var geocodeInputs = $('input[rel *= "geocode"]', this.form);
        var geocodeData = new Array();
        var geocodeRegexp = new RegExp(/geocode\[([0-9]+)\]/);
        for (var iInput = 0; iInput < geocodeInputs.length; iInput++)
        {
            var regMatch = geocodeRegexp.exec($(geocodeInputs[iInput]).attr('rel'));
            if (regMatch != null)
            {
                if ($(geocodeInputs[iInput]).attr('value').length)
                {
                    geocodeData[regMatch[1]] = $(geocodeInputs[iInput]).attr('value');
                }
            }
        }
        
        // Controleer of onderdelen zijn waarop we willen zoeken
        if (geocodeData.length > 0)
        {
            var address = geocodeData.join(/, /);
            var geocoder = new cms.modules.gmapGeocoder();
            geocoder.geoCode(address, jQuery.proxy(this.geocodeCallback, this));
            
            // Stop de verwerking. Het proces wordt via de callback verder doorgezet.
            return true;
        }
        
        // Als er geen onderdelen zijn voor de geocoder, gaan we normaal filteren
        this.pushKmlUri();
    };
    
    this.pushKmlUri = function()
    {
        var kmlUri = '';

        var selects = this.form.getElementsByTagName('select');
        var inputs = this.form.getElementsByTagName('input');
        
        for (var iSelect = 0; iSelect < selects.length; iSelect++)
        {
            if (selects[iSelect].selectedIndex != 0)
            {
                kmlUri += '&' + selects[iSelect].name + '=' + escape(selects[iSelect].value);
            }
        }
        
        for (var iInput = 0; iInput < inputs.length; iInput++)
        {
            if (inputs[iInput].type == 'checkbox')
            {
                if (inputs[iInput].checked)
                {
                    kmlUri += '&' + inputs[iInput].name + '=' + escape(inputs[iInput].value);
                }
            }
            else if (inputs[iInput].type == 'text' && inputs[iInput].value.length)
            {
                kmlUri += '&' + inputs[iInput].name + '=' + escape(inputs[iInput].value);
            }
            else if (inputs[iInput].type == 'hidden' && inputs[iInput].value.length)
            {
                kmlUri += '&' + inputs[iInput].name + '=' + escape(inputs[iInput].value);
            }
        }
        
        if (this.form.action.indexOf("?") == -1)
        {
            kmlUri = this.form.action + "?" + kmlUri;
        }
        else
        {
            kmlUri = this.form.action + kmlUri;
        }
        
        this.switchInputElements(false);

        this.gmap.loadMarkers(null, kmlUri);
    };
    
    this.geocodeCallback = function(result, status)
    {
        switch (status)
        {
            case google.maps.GeocoderStatus.INVALID_REQUEST:
                break;
                
            case google.maps.GeocoderStatus.ERROR:
            case google.maps.GeocoderStatus.UNKNOWN_ERROR:
                break;
                
            case google.maps.GeocoderStatus.OVER_QUERY_LIMIT:
                break;
                
            case google.maps.GeocoderStatus.REQUEST_DENIED:
                break;
                
            case google.maps.GeocoderStatus.ZERO_RESULTS:
                break;
                
            case google.maps.GeocoderStatus.OK:
                var kmlUri = this.gmap.mapData.Kml;
                kmlUri += '&lat=' + result[0].geometry.location.lat();
                kmlUri += '&lng=' + result[0].geometry.location.lng();
                
                // Controleer of het gevonden land binnen de toegestane lijst valt
                var validResult = true;
                if (this.options.allowedCountries)
                {
                    validResult = false;
                    
                    for (var iComponent = 0; iComponent < result[0].address_components.length && !validResult; iComponent++)
                    {
                        for (var iType = 0; iType < result[0].address_components[iComponent].types.length && !validResult; iType++)
                        {
                            // Controleer of het deel van het resultaat een land is
                            if (result[0].address_components[iComponent].types[iType] == 'country')
                            {
                                if (jQuery.inArray(result[0].address_components[iComponent].short_name.toLowerCase(),
                                    this.options.allowedCountries) != -1)
                                {
                                    validResult = true;
                                }
                            }
                        }
                    }
                }
                
                if (validResult)
                {
                    // Stel het nieuwe centrum punt in voor de kaart
                    if (this.options.centerOnLocation)
                    {
                        this.gmap.mapData.Center = new google.maps.LatLng(
                            result[0].geometry.location.lat(), result[0].geometry.location.lng());
                        this.gmap.mapData.ZoomLevel = 12;
                    }
                    
                    this.gmap.loadMarkers(null, kmlUri, true);
                }
                break;
        }  
        
        // Schakel de filter velden weer aan
        this.switchInputElements(true);
    };
    
    this.switchInputElements = function(state)
    {
        if (!this.form)
        {
            return false;
        }

        var selects = this.form.getElementsByTagName('select');

        for (var iSelect = 0; iSelect < selects.length; iSelect++)
        {
            selects[iSelect].disabled = !state
        }

        var inputs = this.form.getElementsByTagName('input');

        for (var iInput = 0; iInput < inputs.length; iInput++)
        {
            inputs[iInput].disabled = !state
        }
    };
};

cms.modules.gmapResults = function(gmap)
{
    this.gmap = gmap;
    this.page = 1;
    this.perpage = 0;
    this.sort = null;

    this.fetchResults = function()
    {
        var updateUrl = window.location.pathname + '?cms[exclusive][]=' + this.gmap._resultsId
            + '&cms[cm' + this.gmap._resultsId + '][format]=xml';

        if (this.page)
        {
            updateUrl += '&cms[cm' + this.gmap._resultsId + '][page]=' + this.page;
        }
        if (this.perpage)
        {
            updateUrl += '&cms[cm' + this.gmap._resultsId + '][perpage]=' + this.perpage;
        }
        if (this.sort != null)
        {
            updateUrl += '&cms[cm' + this.gmap._resultsId + '][sort]=' + this.sort;
        }
        
        $('#gmapResultsContainer').load(updateUrl);
    };

    this.changePage = function(page)
    {
        this.page = page;

        this.gmap.loadMarkers(page);
    };

    this.changeMaxResults = function(perpage)
    {
        this.perpage = perpage;

        this.gmap.loadMarkers();
    };

    this.changeSort = function(sort)
    {
        this.sort = sort;

        this.gmap.loadMarkers();
    };

    this.highlightResult = function(index)
    {
        // Vervang de marker van het gevraagde resultaat
        this.gmap.mapData.geoXml.gmarkers[index - 1].setImage('/client/difrax/upload/module/gmap/marker_highlight.png');

        if (obj = document.getElementById('gmapResult' + index))
        {
            obj.className += ' highlighted';
        }
    };

    this.clearHighlightResult = function(index)
    {
        //var character = String.fromCharCode(65+index-1);
        var character = '';

        // Vervang de marker van het gevraagde resultaat
        this.gmap.mapData.geoXml.gmarkers[index - 1].setImage('http://maps.google.com/mapfiles/marker' + character +'.png');

        if (obj = document.getElementById('gmapResult' + index))
        {
            obj.className = obj.className.replace(/\s?highlighted/, '');
        }
    };
    
    this.hide = function()
    {
    	$('#gmapResultsContainer').hide();
    };
    
    this.show = function()
    {
    	$('#gmapResultsContainer').show();
    };
};

cms.modules.gmapGeocoder = function()
{
    this.geoCode = function(address, callback)
    {
        if (address.length == 0)
        {
            return null;
        }
        
        var geocoder = new google.maps.Geocoder();
        geocoder.geocode({
            address : address
        }, callback);
    };
};

Function.prototype.bind = function(scope){
	var _this = this;
	return function() {
		return _this.apply(scope, arguments);
	}
}
