/** 
* Global variable: viskort
* (<VisKort.Application>) Holds the application 
*/
var viskort;

VisKort = {};

//UTM32 EUREF89 def:
Proj4js.defs['EPSG:25832'] = "+proj=utm +zone=32 +ellps=GRS80 +units=m +no_defs";
//Google Mercator def:
Proj4js.defs['EPSG:102113'] = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs";
//Google Mercator def:
Proj4js.defs['EPSG:4326'] = "+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs";

/** 
* Class: VisKort.Application
*
* The application holds the map, all the layers and all the components.
* viskort is the main entry to all javascript in the solution.
*
* Only one instance of this class should be created. All access to underlaying 
* objects should be through this instance.
*
* The constructor of the class initializes the map.
*/
VisKort.Application = OpenLayers.Class({

    /** 
    * Property: map
    * {<VisStedet.Map>} The OpenLayers/VisStedet map.
    */
    map: null,

    /** 
    * Property: infoBox
    * {<OpenLayers.Control.InfoBox>} The reference to the infobox to show popup balloon information
    */
    infoBox: null,

    /** 
    * Property: active_theme
    * {<String>} Name of the active layout theme (ASP.NET)
    */
    active_theme: null,

    /**
    * Property: zoomlevels
    * {<Number>} Number of zoom levels available on the map
    */
    zoomlevels: 20,

    /**
    * Property: components
    * {Object} Hashtable of available components <VisKort.Component>. 
    */
    components: {},

    /**
    * Property: activeComponentName
    * {String} The key of the active component in the components-hashtable. 
    */
    activeComponentName: null,

    /**
    * Property: pagemode
    * {String} Can either be "iframe", "popup" or "print". 
    */
    pagemode: "popup",

    /**
    * Property: baseQuerystring
    * (String) constant parameters
    */
    baseQuerystring: null,

    overviewMap: null,

    /**
    * Constructor: VisKort.Application
    * Initializes the map, the map controls, and the layers
    *
    * Parameters:
    * mapDivId - <String> The id of the HTML DIV tag to hold the map
    * activeTheme - <String>
    * mapConfig - <Object> The relevant subset of the MapConfig.xml file content (all layers needed in the current request). JSON formatted.
    * pagemode - <String>
    * baseQuerystring - <String> contains the querystring
    */
    initialize: function(mapDivId, activeTheme, mapConfig, pagemode, baseQuerystring,
                         parentMapDivHeight, parentMapDivWidth, googlerouteEnabled,
                         rejseplanEnabled, msveEnabled, loadingPanel) {
        if (pagemode)
            this.pagemode = pagemode;

        // Setup proxy location and active theme
        OpenLayers.ProxyHost = "Proxy.ashx?url=";
        OpenLayers.ImgPath = '/VisKort/App_Themes/' + activeTheme + '/Images/OpenLayers/';
        this.active_theme = activeTheme;

        this.baseQuerystring = baseQuerystring;

        // Set InfoBox area (covers Denmark)
        var infoBoxRing = new OpenLayers.Geometry.LinearRing();
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(440000, 6040000))); // SW
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(440000, 6330000))); // W
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(610000, 6430000))); // N
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(730000, 6220000))); // NE 1
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(750000, 6140000))); // NE 2
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(900000, 6160000))); // E
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(900000, 6100000))); // SE
        infoBoxRing.addComponent(this.convertPoint(new OpenLayers.Geometry.Point(710000, 6040000))); // S
        var infoBoxArea = new OpenLayers.Geometry.Polygon(infoBoxRing);

        // Initialize controls
        var layerSwitcher = new OpenLayers.Control.StyledLayerSwitcher();
        var scaleLineOptions = { bottomOutUnits: "", bottomInUnits: "" };
        var scaleLine = new OpenLayers.Control.ScaleLineBugFixed(scaleLineOptions);
        var navigation = new OpenLayers.Control.Navigation();
        var panZoomBar = new OpenLayers.Control.PanZoomBar();
        var overviewMapOptions = {
            //controls: mapcontrols,
            projection: new OpenLayers.Projection("EPSG:102113"),
            //projection: "EPSG:102113",
            //displayProjection: "EPSG:4326",
            numZoomLevels: this.zoomlevels,
            units: "m",
            maxResolution: 156543.03390625,
            maxExtent: new OpenLayers.Bounds(-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892)
        };
        //var overviewMapOptions = { projection: new OpenLayers.Projection("EPSG:102113"), units: 'm', maxExtent: maxExt };
        overviewMap = new OpenLayers.Control.StyledOverviewMap({ minRatio: 32, maxRatio: 64, mapOptions: overviewMapOptions });
        var infoBoxLinkGenerators = null;

        var printButtonOptions = { displayClass: "olControlPrintButton", trigger: "" };
        var printButton = new OpenLayers.Control.StyledPrintButton(printButtonOptions);
        printButton.baseQuerystring = this.baseQuerystring;
        printButton.parent_theme = this.active_theme;

        var virtualEarthButton = null;
        if (msveEnabled) {
            var virtualEarthButtonOptions = { displayClass: "olControlVirtualEarthButton", trigger: "" };
            virtualEarthButton = new OpenLayers.Control.StyledVirtualEarthButton(virtualEarthButtonOptions);
            virtualEarthButton.baseQuerystring = this.baseQuerystring;
            virtualEarthButton.parent_theme = this.active_theme;
        }

        var counter = 0;
        infoBoxLinkGenerators = new Array();
        if (pagemode == "popup") {
            infoBoxLinkGenerators[counter] = new VisKort.Utils.LinkGenerator.Route(false);
            if (googlerouteEnabled)
                infoBoxLinkGenerators[++counter] = new VisKort.Utils.LinkGenerator.GoogleRoutePlan("TwoLinks", false);
            if (rejseplanEnabled)
                infoBoxLinkGenerators[++counter] = new VisKort.Utils.LinkGenerator.Rejseplanen("TwoLinks", false);
        } else {
            if (googlerouteEnabled)
                infoBoxLinkGenerators[counter] = new VisKort.Utils.LinkGenerator.GoogleRoutePlan("TwoLinks", false);
            if (rejseplanEnabled)
                infoBoxLinkGenerators[++counter] = new VisKort.Utils.LinkGenerator.Rejseplanen("TwoLinks", false);
        }
        infoBox = new OpenLayers.Control.InfoBox({}, infoBoxArea, infoBoxLinkGenerators);

        // Initialize mapcontrols
        if (activeTheme.toLowerCase() == "hilleroed") {
            mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, overviewMap, infoBox];
        } else {
            //mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, new OpenLayers.Control.MousePosition(), infoBox, printButton];
            mapcontrols = [layerSwitcher, scaleLine, panZoomBar, navigation, overviewMap, infoBox, printButton];
            if (virtualEarthButton != null)
                mapcontrols[mapcontrols.length] = virtualEarthButton;
            if (loadingPanel == true)
                mapcontrols[mapcontrols.length] = new OpenLayers.Control.LoadingPanel();
        }

        if (this.pagemode == "print") {
            mapcontrols = [scaleLine, infoBox, panZoomBar, navigation];
        }
        /*
        var maxresolution = null;

        if (this.pagemode == "print") {
        var printmapDiv = document.getElementById(mapDivId);           
        try
        {
        if (parentMapDivHeight >= printmapDiv.clientHeight || parentMapDivWidth >= printmapDiv.clientWidth) {
        maxresolution = (maxExt.top - maxExt.bottom) / 164;
        } else {
        maxresolution = (maxExt.top - maxExt.bottom) / 256;
        }
        }catch(err)
        {
        maxresolution = (maxExt.top - maxExt.bottom) / 164;
        }
        } else {
        maxresolution = (maxExt.top - maxExt.bottom) / 256;
        }
        */

        var options = {
            controls: mapcontrols,
            projection: new OpenLayers.Projection("EPSG:102113"),
            //projection: "EPSG:102113",
            //displayProjection: "EPSG:4326",
            //displayProjection: "EPSG:102113",
            numZoomLevels: this.zoomlevels,
            units: "m",
            maxResolution: 156543.0339,
            maxExtent: new OpenLayers.Bounds(-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892)
        };

        this.map = new VisStedet.Map(mapDivId, options);

        if (this.pagemode != "print" && activeTheme.toLowerCase() != "hilleroed") {
            printButton.activate();
            printButton.active_print();
            if (msveEnabled) {
                virtualEarthButton.activate();
                virtualEarthButton.active_virtualEarth();
            }
        }

        // Add layers
        var layers = mapConfig.mapconfig.layerlist.layer;
        if (layers == null)
            alert("Kortlag mangler at blive inkluderet");
        else if (layers[0] == null)
            this.addLayer(layers);
        else {
            for (var i = 0; i < layers.length; i++) {
                this.addLayer(layers[i]);
            }
        }

        infoBox.activate();
        this.infoBox = infoBox;
        if (this.pagemode == "print") {
            this.map.baseLayer.redraw();
        }

        /*

            // style the sketch fancy
        var sketchSymbolizers = {
        "Point": {
        pointRadius: 4,
        graphicName: "square",
        fillColor: "white",
        fillOpacity: 1,
        strokeWidth: 1,
        strokeOpacity: 1,
        strokeColor: "#333333"
        },
        "Line": {
        strokeWidth: 3,
        strokeOpacity: 1,
        strokeColor: "#666666",
        strokeDashstyle: "dash"
        },
        "Polygon": {
        strokeWidth: 2,
        strokeOpacity: 1,
        strokeColor: "#666666",
        fillColor: "white",
        fillOpacity: 0.3
        }
        };
        var style = new OpenLayers.Style();
        style.addRules([
        new OpenLayers.Rule({symbolizer: sketchSymbolizers})
        ]);
        var styleMap = new OpenLayers.StyleMap({"default": style});

            measureControls = {
        line: new OpenLayers.Control.Measure(
        OpenLayers.Handler.Path, {
        persist: true,
        handlerOptions: {
        layerOptions: {styleMap: styleMap}
        }
        }
        ),
        polygon: new OpenLayers.Control.Measure(
        OpenLayers.Handler.Polygon, {
        persist: true,
        handlerOptions: {
        layerOptions: {styleMap: styleMap}
        }
        }
        )
        };

            var control;
        for(var key in measureControls) {
        control = measureControls[key];
        control.events.on({
        "measure": this.handleMeasurements,
        "measurepartial": this.handleMeasurements
        });
        this.map.addControl(control);
        }


            document.getElementById('noneToggle').checked = true;
        */

    },

    setInitialZoom: function() {
        // Setup boundings definition        
        var maxExt = this.convertBounds(new OpenLayers.Bounds(420000, 6025000, 905000, 6450000));
        // set default extent
        this.map.zoomToExtent(maxExt);

    },

    handleMeasurements: function(event) {
        var geometry = event.geometry;
        alert(event.geometry.getGeodesicArea(new OpenLayers.Projection("EPSG:102113")));
        var vertArray = geometry.getVertices();
        for (var i = 0; i < vertArray.length; i++) {
            var convertedPoint = viskort.convertBounds(vertArray[i]);
            vertArray[i] = convertedPoint;
        }
        event.geometry

        var units = event.units;
        var order = event.order;
        var measure = event.measure;
        var element = document.getElementById('output');
        var out = "";
        if (order == 1) {
            out += "measure: " + measure.toFixed(3) + " " + units;
        } else {
            out += "measure: " + measure.toFixed(3) + " " + units + "<sup>2</" + "sup>";
        }
        element.innerHTML = out;
    },

    toggleControl: function(element) {
        for (key in measureControls) {
            var control = measureControls[key];
            if (element.value == key && element.checked) {
                control.activate();
            } else {
                control.deactivate();
            }
        }
    },

    toggleGeodesic: function(element) {
        for (key in measureControls) {
            var control = measureControls[key];
            control.geodesic = element.checked;

        }
    },

    /**
    * Method: addLayer
    * Method to add a layer. Invoked by the constructor during the initialization
    *
    * Parameters:
    * layerConfig - <Object> Subset of the MapConfig.xml file content corresponding to the layer. JSON formatted
    */
    addLayer: function(layerConfig) {
        var layer, selectControl;
        if (layerConfig["@type"] == "WMS") {
            layerConfig.options['getFullRequestString'] = this.wmsCoordinateTransform;
            layerConfig.options['buffer'] = 0;
            layerConfig.options['ratio'] = 1;
            layer = new OpenLayers.Layer.WMS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }
        if (layerConfig["@type"] == "WMS.Untiled") {
            layerConfig.options['getFullRequestString'] = this.wmsCoordinateTransformUntiled;
            layer = new OpenLayers.Layer.WMS.Untiled(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }
        else if (layerConfig["@type"] == "WFS") {
            layer = new OpenLayers.Layer.WFS(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }
        else if (layerConfig["@type"] == "KML") {
            layer = new OpenLayers.Layer.KML(layerConfig.name, layerConfig.url, layerConfig.options)
        }
        else if (layerConfig["@type"] == "GDK_TMS") {
            layerConfig.options['getURL'] = this.get_gdk_url;
            layer = new OpenLayers.Layer.TMS(layerConfig.name, layerConfig.url, layerConfig.options)
        }
        else if (layerConfig["@type"] == "AGS_TMS") {
            layerConfig.options['getURL'] = this.get_ags_url;
            layer = new OpenLayers.Layer.TMS(layerConfig.name, layerConfig.url, layerConfig.options)
        }
        else if (layerConfig["@type"] == "AGS_REST") {
            var agsRestUrl = layerConfig.url + "/" + layerConfig.params['servicename'] + "/MapServer/export";
            layer = new OpenLayers.Layer.ArcGIS93Rest(layerConfig.name, agsRestUrl, layerConfig.params, layerConfig.options);
        }
        else if (layerConfig["@type"] == "OPEN_STREET_MAPS") {
            layerConfig.options['getURL'] = this.get_osm_url;
            layer = new OpenLayers.Layer.TMS(layerConfig.name, layerConfig.url, layerConfig.options)
        }
        else if (layerConfig["@type"] == "PANORAMIO" ||
                layerConfig["@type"] == "GOOGLEWEBCAMS" ||
                layerConfig["@type"] == "WIKIPEDIA" ||
                layerConfig["@type"] == "STREETVIEW") {
            layerConfig.options['getURL'] = this.get_GM_util_url;
            layer = new OpenLayers.Layer.TMS(layerConfig.name, layerConfig.url, layerConfig.options)
        }
        else if (layerConfig["@type"] == "G_NORMAL_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name,
		{ type: G_NORMAL_MAP, numZoomLevels: 20, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "G_PHYSICAL_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name,
		{ type: G_PHYSICAL_MAP, numZoomLevels: 20, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "G_HYBRID_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name,
		{ type: G_HYBRID_MAP, numZoomLevels: 20, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "G_SATELLITE_MAP") {
            layer = new OpenLayers.Layer.Google(layerConfig.name,
		{ type: G_SATELLITE_MAP, numZoomLevels: 20, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "BING_SHADED") {
            layer = new OpenLayers.Layer.VirtualEarth(layerConfig.name,
		{ type: VEMapStyle.Shaded, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "BING_HYBRID") {
            layer = new OpenLayers.Layer.VirtualEarth(layerConfig.name,
		{ type: VEMapStyle.Hybrid, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "BING_AERIAL") {
            layer = new OpenLayers.Layer.VirtualEarth(layerConfig.name,
		{ type: VEMapStyle.Aerial, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "YAHOO_NORMAL") {
            layer = new OpenLayers.Layer.Yahoo(layerConfig.name,
		{ numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "YAHOO_SAT") {
            layer = new OpenLayers.Layer.Yahoo(layerConfig.name,
		{ 'type': YAHOO_MAP_SAT, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "YAHOO_HYBRID") {
            layer = new OpenLayers.Layer.Yahoo(layerConfig.name,
		{ 'type': YAHOO_MAP_HYB, numZoomLevels: this.zoomlevels, 'sphericalMercator': true, projection: new OpenLayers.Projection("EPSG:900913") });
        }
        else if (layerConfig["@type"] == "KA_MAP") {
            layer = new OpenLayers.Layer.KaMap(layerConfig.name, layerConfig.url, layerConfig.params, layerConfig.options);
        }


        layer.addOptions({ mapConfigId: layerConfig["@id"] });

        this.map.addLayer(layer);

        if (layer.isBaseLayer && layerConfig.options && layerConfig.options.visibility) {
            this.map.setBaseLayer(layer);
        }
    },

    /**
    * Method: get_ags_url
    * Method to determine tile from ArcGIS Server tile cached output
    *
    * Parameters:
    * bounds - bounds of tile
    */
    get_ags_url: function(bounds) {
        var res = this.map.getResolution();
        var x = (Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w)));
        var y = (Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h)));
        var z = this.map.getZoom();

        return this.url + "/" + z + "/" + y + "/" + x + "." + this.type;
    },


    /**
    * Method: get_osm_url
    * Method to determine tile from Open Street Maps
    *
    * Parameters:
    * bounds - bounds of tile
    */

    get_osm_url: function(bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.map.getZoom();
        var limit = Math.pow(2, z);

        if (y < 0 || y >= limit) {
            return OpenLayers.Util.getImagesLocation() + "404.png";
        } else {
            x = ((x % limit) + limit) % limit;
            return this.url + z + "/" + x + "/" + y + "." + this.type;
        }
    },

    /**
    * Method: get_GM_util_url
    * Method to determine tile from panoramio images
    *
    * Parameters:
    * bounds - bounds of tile
    */
    get_GM_util_url: function(bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.map.getZoom();
        var limit = Math.pow(2, z);

        if (y < 0 || y >= limit) {
            return OpenLayers.Util.getImagesLocation() + "404.png";
        } else {
            x = ((x % limit) + limit) % limit;
            var resultUrl = this.url + "?";

            if (this.lyr != null)
                resultUrl += "lyrs=" + this.lyr;

            resultUrl += "&output=overlay" +
                    "&x=" + x +
                    "&y=" + y +
                    "&z=" + z +
                    "&zoom=" + z +
                    "&w=" + this.tileSize.w +
                    "&h=" + this.tileSize.h +
                    "&gl=dk&hl=da";

            return resultUrl;
        }
    },

    /**
    * Method: get_my_url
    * Method to recalculate tile from TMS layers from GDK generated tile output
    *
    * Parameters:
    * bounds - bounds of tile
    */
    get_gdk_url: function(bounds) {
        var res = this.map.getResolution();
        var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
        var y = Math.round((this.maxExtent.top - bounds.top) / (res * this.tileSize.h));
        var z = this.map.getZoom();

        var path = "google_" + x + "_" + y + "_" + z + "." + this.type;

        var url = this.url;
        if (url instanceof Array) {
            url = this.selectUrl(path, url);
        }

        return url + "/" + path;
    },


    /**
    * Method: wmsCoordinateTransform
    * Method to transform WMS layers into EPSG:4326 projection. 
    *
    * Parameters:
    * newParams - <String> newParams for WMS layer
    * altUrl - altUrl option
    */
    wmsCoordinateTransform: function(newParams, altUrl) {

        var url = altUrl || this.url;
        var allParams = OpenLayers.Util.extend({}, this.params);
        allParams = OpenLayers.Util.extend(allParams, newParams);

        var srsFound = false;
        for (var key in allParams) {
            var paramvalue = allParams[key];
            var paramKey = encodeURIComponent(key).toUpperCase();
            if (paramKey == 'BBOX') {
                var paramSplitter = paramvalue.toString().split(',');
                var xMin = paramSplitter[0];
                var yMin = paramSplitter[1];
                var xMax = paramSplitter[2];
                var yMax = paramSplitter[3];

                // transforming point coordinates
                var pMinConverted = new Proj4js.Point(xMin, yMin);
                Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:4326'), pMinConverted);

                var pMaxConverted = new Proj4js.Point(xMax, yMax);
                Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:4326'), pMaxConverted);

                // store reprojected coordinates
                allParams[key] = pMinConverted.x + "," + pMinConverted.y + "," + pMaxConverted.x + "," + pMaxConverted.y;
            }
            if (paramKey == 'SRS') {
                srsFound = true;
                allParams[key] = 'EPSG:4326';
            }
        }
        if (!srsFound) {
            allParams['SRS'] = 'EPSG:4326';
        }
        var paramsString = OpenLayers.Util.getParameterString(allParams);
        if (url instanceof Array) { url = this.selectUrl(paramsString, url) };
        return url + "?" + paramsString;
    },


    /**
    * Method: wmsCoordinateTransform
    * Method to transform WMS layers into EPSG:4326 projection. 
    *
    * Parameters:
    * newParams - <String> newParams for WMS layer
    * altUrl - altUrl option
    */
    wmsCoordinateTransformUntiled: function(newParams, altUrl) {

        var url = altUrl || this.url;
        var allParams = OpenLayers.Util.extend({}, this.params);
        allParams = OpenLayers.Util.extend(allParams, newParams);

        var srsFound = false;
        for (var key in allParams) {
            var paramvalue = allParams[key];
            var paramKey = encodeURIComponent(key).toUpperCase();
            if (paramKey == 'BBOX') {
                var paramSplitter = paramvalue.toString().split(',');
                var xMin = paramSplitter[0];
                var yMin = paramSplitter[1];
                var xMax = paramSplitter[2];
                var yMax = paramSplitter[3];

                // transforming point coordinates
                var pMinConverted = new Proj4js.Point(xMin, yMin);
                Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:4326'), pMinConverted);

                var pMaxConverted = new Proj4js.Point(xMax, yMax);
                Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:4326'), pMaxConverted);

                // store reprojected coordinates
                allParams[key] = pMinConverted.x + "," + pMinConverted.y + "," + pMaxConverted.x + "," + pMaxConverted.y;
            }
            if (paramKey == 'SRS') {
                srsFound = true;
                allParams[key] = 'EPSG:4326';
            }
        }
        if (!srsFound) {
            allParams['SRS'] = 'EPSG:4326';
        }
        var paramsString = OpenLayers.Util.getParameterString(allParams);
        if (url instanceof Array) { url = this.selectUrl(paramsString, url) };
        return url + "?" + paramsString;
    },

    /**
    * Method: onInfoCallBack
    * Invoked, when callbacks are received from xml getfeature info responses
    *
    * Parameters:
    * url - <String>  url to request
    * i - <int>  unique key defined by InfoBox.js
    */
    onInfoCallBack: function(url, i) {
        OpenLayers.loadURL(url, { InfoIdx: i }, this, function(response) { viskort.infoBox.onInfoResponse(i, response); });
    },


    /**
    * Method: convertBounds
    * Method to convert bounds in source coordinate system into destination coordinate system
    *
    * Parameters:
    * bounds - <OpenLayers.Bounds>  bounds to convert
    */
    convertBounds: function(bounds) {
        // transforming point coordinates
        var pLeftBottomConverted = new Proj4js.Point(bounds.left, bounds.bottom);
        Proj4js.transform(new Proj4js.Proj('EPSG:25832'), new Proj4js.Proj('EPSG:102113'), pLeftBottomConverted);

        var pRightTopConverted = new Proj4js.Point(bounds.right, bounds.top);
        Proj4js.transform(new Proj4js.Proj('EPSG:25832'), new Proj4js.Proj('EPSG:102113'), pRightTopConverted);
        return new OpenLayers.Bounds(pLeftBottomConverted.x, pLeftBottomConverted.y, pRightTopConverted.x, pRightTopConverted.y);
    },

    /**
    * Method: convertPoint
    * Method to convert point in source coordinate system into destination coordinate system
    *
    * Parameters:
    * bounds - <OpenLayers.Geometry.Point>  bounds to convert
    */
    convertPoint: function(point) {
        // transforming point coordinates
        var pConverted = new Proj4js.Point(point.x, point.y);
        Proj4js.transform(new Proj4js.Proj('EPSG:25832'), new Proj4js.Proj('EPSG:102113'), pConverted);

        return new OpenLayers.Geometry.Point(pConverted.x, pConverted.y);
    },

    /**
    * Method: reverseConvertBounds
    * Method to convert bounds in source coordinate system into destination coordinate system
    *
    * Parameters:
    * bounds - <OpenLayers.Bounds>  bounds to convert
    */
    reverseConvertBounds: function(bounds) {
        // transforming point coordinates
        var pLeftBottomConverted = new Proj4js.Point(bounds.left, bounds.bottom);
        Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:4326'), pLeftBottomConverted);

        var pRightTopConverted = new Proj4js.Point(bounds.right, bounds.top);
        Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:4326'), pRightTopConverted);
        return new OpenLayers.Bounds(pLeftBottomConverted.x, pLeftBottomConverted.y, pRightTopConverted.x, pRightTopConverted.y);
    },

    /**
    * Method: reverseConvertPoint
    * Method to convert point in destination coordinate system into source coordinate system
    *
    * Parameters:
    * bounds - <OpenLayers.Geometry.Point>  bounds to convert
    */
    reverseConvertPoint: function(point) {
        // transforming point coordinates
        var pConverted = new Proj4js.Point(point.x, point.y);
        Proj4js.transform(new Proj4js.Proj('EPSG:102113'), new Proj4js.Proj('EPSG:25832'), pConverted);

        return new OpenLayers.Geometry.Point(pConverted.x, pConverted.y);
    },



    /**
    * Method: reverseConvertBounds
    * Method to convert bounds in source coordinate system into destination coordinate system
    *
    * Parameters:
    * bounds - <OpenLayers.Bounds>  bounds to convert
    */
    kmlConvertBounds: function(bounds) {
        // transforming point coordinates
        var pLeftBottomConverted = new Proj4js.Point(bounds.left, bounds.bottom);
        Proj4js.transform(new Proj4js.Proj('EPSG:4326'), new Proj4js.Proj('EPSG:102113'), pLeftBottomConverted);

        var pRightTopConverted = new Proj4js.Point(bounds.right, bounds.top);
        Proj4js.transform(new Proj4js.Proj('EPSG:4326'), new Proj4js.Proj('EPSG:102113'), pRightTopConverted);
        return new OpenLayers.Bounds(pLeftBottomConverted.x, pLeftBottomConverted.y, pRightTopConverted.x, pRightTopConverted.y);
    },

    /**
    * Method: kmlConvertPoint
    * Method to convert kml point in destination coordinate system into source coordinate system
    *
    * Parameters:
    * bounds - <OpenLayers.Geometry.Point>  bounds to convert
    */
    kmlConvertPoint: function(point) {
        // transforming point coordinates
        var pConverted = new Proj4js.Point(point.x, point.y);
        Proj4js.transform(new Proj4js.Proj('EPSG:4326'), new Proj4js.Proj('EPSG:102113'), pConverted);

        return new OpenLayers.Geometry.Point(pConverted.x, pConverted.y);
    },


    /**
    * Method: reverseConvertPoint
    * Method to convert point in destination coordinate system into source coordinate system
    *
    * Parameters:
    * point - <OpenLayers.Geometry.Point>  bounds to convert
    */
    kmlTransformGeometry: function(geometry) {
        // transforming point coordinates
        geometry.transform(new OpenLayers.Projection('EPSG:4326'), new OpenLayers.Projection('EPSG:102113'));
    },


    /**
    * Method: reverseConvertPoint
    * Method to convert point in destination coordinate system into source coordinate system
    *
    * Parameters:
    * point - <OpenLayers.Geometry.Point>  bounds to convert
    */
    kmlReverseTransformGeometry: function(geometry) {
        // transforming point coordinates
        geometry.transform(new OpenLayers.Projection('EPSG:102113'), new OpenLayers.Projection('EPSG:4326'));
    },



    /**
    * Method: JSONparseAGSRestInfo
    * JSon parser and replacement from VisKort layer information settings,
    * Specified in MapConfig.xml
    *
    * Parameters:
    * replacement - <String> information html in which parameters are to be replaced
    * JSONstring - <String> the JSON string to parse
    */
    JSONparseAGSRestInfo: function(originalReplacement, JSONstring, linkLayer) {
        var bLinkLayer = false;
        if (linkLayer == null || linkLayer == "undefined") bLinkLayer = false;
        else if (linkLayer.toString().toLowerCase() == "true") bLinkLayer = true;

        var replacement = originalReplacement;
        var result = "";
        var firstTimer = true;
        var replacementComplete = false;
        try{
            myData = JSON.parse(JSONstring, function(key, value) {
                var sKey = key;
                var sValue = "'" + value + "'";

                var bFound = false;
                if (bLinkLayer) {
                    if (sValue.indexOf("http://") > -1) {
                        bFound = true;
                    }
                    else if (sValue.indexOf("https://") > -1) {
                        bFound = true;
                    }
                    else if (sValue.indexOf("www.") > -1) {
                        bFound = true;
                    }
                    if (bFound) {
                        if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent) ||
                        	/Chrome[\/\s](\d+\.\d+)/.test(navigator.userAgent)){ //test for Chrome or Firefox/x.x or Firefox x.x (ignoring remaining digits);
	                        window.open(value, key, 'channelmode=no,directories=no,fullscreen=no,location=no,menubar=no,resizable=yes,scrollbars=yes,status=yes,titlebar=yes,toolbar=no,top=0,left=0,height=600,width=1100', true);
	                } else {
	                        var elem = document.getElementById('fakeLink');
	                        elem.href = value;
                        	elem.click();
                        }
                    }
                }
                var r = new RegExp("<!--#" + key + "#-->", 'gi');
                replacement = replacement.replace(r, value);
                if (replacement.indexOf("<!--#") < 0) {
                /*
                    if (replacementComplete) {
                        // more than one record has been found
                        // add next record and format response
                        // in multirecord friendly GUI
                        if (firstTimer) {
                            // trigger implemented in order to format 
                            // previous object, since this objec talso need to be formated
                            // in multirecord friendly GUI
                            result = 
                            
                            // assure, that we never will enter this statement again
                            firstTimer = false;
                        }
                    }
                    */
                    replacementComplete = true;
                    result += replacement;
                    replacement = originalReplacement;
                }
                return value;
            });
        } catch(e) {
            // an exception may occur due to use of Internet
            // Explorer version 8+, since Microsoft
            // has added new implementation og JSON.parse
            // this exception handling will not affect the result
            // since the error occur when all parsing has been done
        }
        return result;
    },

    /**
    * Method: addComponent
    * Method to add components (<VisKort.Component> sub classes). Must be called after component initialization.
    *
    * Parameters:
    * name - <String> name (key) of the component
    * component - <VisKort.Component>
    */
    addComponent: function(name, component) {
        component.setContext(this, this.map, this.pagemode);
        this.components[name] = component;

        component.startup();
    },

    /**
    * Method: activateComponent
    * Method to activate a component. Must be invoked when a component gets focus.
    * Only one component can be active at a time.
    *
    * Parameters:
    * name - <String> name (key) of the component
    */
    activateComponent: function(name) {
        if (this.activeComponentName != name) {
            if (this.activeComponentName && this.components[this.activeComponentName])
                this.components[this.activeComponentName].deActivate();

            if (this.components[name])
                this.components[name].activate();

            this.activeComponentName = name;
        }
    },

    /**
    * Method: getActiveComponent
    * Method to return the active compoent <VisKort.Component>.
    */
    getActiveComponent: function() {
        if (this.activeComponentName)
            return this.components[this.activeComponentName]
        else
            return null;
    },

    /**
    * Method: getLayerById
    * Method to get the layer <OpenLayers.Layer> by a layer id from the MapConfig.xml file
    *
    * Parameters:
    * id - <String> 
    */
    getLayerById: function(id) {
        var layerList = this.map.layers;
        for (var i = 0; i < layerList.length; i++) {
            if (layerList[i].mapConfigId == id)
                return layerList[i];
        }
        return null;
    },

    /**
    * Method: openPopup
    * Method to open a "popup" map from the current "iframe" map - transfer the current state of the map
    *
    * Parameters:
    * baseQuerystring - <string> part of the original querystring that is readonly (not affected by user interaction)
    */
    openPopup: function() {
        var pConverted = viskort.reverseConvertPoint(new OpenLayers.Geometry.Point(this.map.getCenter().lon, this.map.getCenter().lat));
        var currentstate = "PanToEasting=" + pConverted.x + "&PanToNorthing=" + pConverted.y + "&ZoomLevel=" + Math.round(this.map.getZoom());

        var activeLayers = "";
        var includeComma = false;
        for (var q = 0; q < this.map.layers.length; q++) {
            if (this.map.layers[q].visibility && this.map.layers[q].options.mapConfigId != null) {
                if (includeComma)
                    activeLayers += ",";
                else
                    includeComma = true;

                activeLayers += this.map.layers[q].options.mapConfigId;
            }
        }

        var qs = currentstate;
        if (qs.length > 0 && activeLayers.length > 0)
            qs += "&";
        qs += "DefaultOn=" + activeLayers;
        if (qs.length > 0 && this.baseQuerystring.length > 0)
            qs += "&";
        qs += this.baseQuerystring;

        window.open('PopupMap.aspx?' + qs, '', 'location=no,menubar=no,toolbar=no,status=no,width=850,height=550,resize=yes,resizable=yes', false);
    },

    /**
    * Method: openAboutPage
    * Method to open the "about" page
    */
    openAboutPage: function() {
        window.open('About.aspx?CallingApp=' + this.active_theme, '', 'status=no,width=800,height=800,resize=yes', false);
    },

    /**
    * Method: Setmap
    * Method to set zoomlevel and center coordinate
    *
    * Parameters:
    * map - <VisStedet.Map>
    * zoomlevel - <Number>
    * panToEasting - <Number>
    * panToNorthing - <Number>
    */
    SetmapAndTransform: function(map, zoomlevel, panToEasting, panToNorthing) {
        // convert point into destination coordinate system
        var pConverted = viskort.convertPoint(new OpenLayers.Geometry.Point(panToEasting, panToNorthing));
        this.map.setCenter(new OpenLayers.LonLat(pConverted.x, pConverted.y));

        // hack in order to support missing zoomlevel
        // whcih should be standard in OpenLayers setCenter
        // command, but not working properly when using
        // Google/Bing Maps
        var initialZoomScale = 295829355.45;
        var endZoomScale = initialZoomScale;
        for (var i = 1; i <= zoomlevel; i++) {
            endZoomScale = endZoomScale / 2;
        }
        this.map.zoomToScale(Math.round(endZoomScale));
    },



    CLASS_NAME: "VisKort.Application"
});
